OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
OcApfsIo.c
Go to the documentation of this file.
1
15#include "OcApfsInternal.h"
16#include <Library/BaseLib.h>
17#include <Library/BaseMemoryLib.h>
18#include <Library/BaseOverflowLib.h>
19#include <Library/DebugLib.h>
20#include <Library/MemoryAllocationLib.h>
21#include <Library/OcApfsLib.h>
22
23STATIC
24UINT64
26 VOID *Data,
27 UINTN DataSize
28 )
29{
30 UINT32 *Walker;
31 UINT32 *WalkerEnd;
32 UINT64 Sum1;
33 UINT64 Sum2;
34 UINT32 Rem;
35
36 //
37 // For APFS we have the following guarantees (checked outside).
38 // - DataSize is always divisible by 4 (UINT32), the only potential exceptions
39 // are multiples of block sizes of 1 and 2, which we do not support and filter out.
40 // - DataSize is always between 0x1000-8 and 0x10000-8, i.e. within UINT16.
41 //
42 ASSERT (DataSize >= APFS_NX_MINIMUM_BLOCK_SIZE - sizeof (UINT64));
43 ASSERT (DataSize <= APFS_NX_MAXIMUM_BLOCK_SIZE - sizeof (UINT64));
44 ASSERT (DataSize % sizeof (UINT32) == 0);
45
46 Sum1 = 0;
47 Sum2 = 0;
48
49 Walker = Data;
50 WalkerEnd = Walker + DataSize / sizeof (UINT32);
51
52 //
53 // Do usual Fletcher-64 rounds without modulo due to impossible overflow.
54 //
55 while (Walker < WalkerEnd) {
56 //
57 // Sum1 never overflows, because 0xFFFFFFFF * (0x10000-8) < MAX_UINT64.
58 // This is just a normal sum of data values.
59 //
60 Sum1 += *Walker;
61 //
62 // Sum2 never overflows, because 0xFFFFFFFF * (0x4000-1) * 0x1FFF < MAX_UINT64.
63 // This is just a normal arithmetical progression of sums.
64 //
65 Sum2 += Sum1;
66 ++Walker;
67 }
68
69 //
70 // Split Fletcher-64 halves.
71 // As per Chinese remainder theorem, perform the modulo now.
72 // No overflows also possible as seen from Sum1/Sum2 upper bounds above.
73 //
74
75 Sum2 += Sum1;
76 APFS_MOD_MAX_UINT32 (Sum2, &Rem);
77 Sum2 = ~Rem;
78
79 Sum1 += Sum2;
80 APFS_MOD_MAX_UINT32 (Sum1, &Rem);
81 Sum1 = ~Rem;
82
83 return (Sum1 << 32U) | Sum2;
84}
85
86STATIC
87BOOLEAN
89 APFS_OBJ_PHYS *Block,
90 UINTN DataSize
91 )
92{
93 UINT64 NewChecksum;
94
95 ASSERT (DataSize > sizeof (*Block));
96
97 NewChecksum = ApfsFletcher64 (
98 &Block->ObjectOid,
99 DataSize - sizeof (Block->Checksum)
100 );
101
102 if (NewChecksum == Block->Checksum) {
103 return TRUE;
104 }
105
106 DEBUG ((DEBUG_INFO, "OCJS: Checksum mismatch for %Lx\n", Block->ObjectOid));
107 return FALSE;
108}
109
110STATIC
111EFI_STATUS
113 IN APFS_PRIVATE_DATA *PrivateData,
114 OUT APFS_NX_EFI_JUMPSTART **JumpStartPtr
115 )
116{
117 EFI_STATUS Status;
118 APFS_NX_EFI_JUMPSTART *JumpStart;
119 EFI_BLOCK_IO_PROTOCOL *BlockIo;
120 EFI_LBA Lba;
121 UINT32 MaxExtents;
122
123 //
124 // No jump start driver, ignore.
125 //
126 if (PrivateData->EfiJumpStart == 0) {
127 DEBUG ((DEBUG_INFO, "OCJS: Missing JumpStart for %g\n", &PrivateData->LocationInfo.ContainerUuid));
128 return EFI_UNSUPPORTED;
129 }
130
131 //
132 // Allocate memory for jump start.
133 //
134 JumpStart = AllocateZeroPool (PrivateData->ApfsBlockSize);
135 if (JumpStart == NULL) {
136 return EFI_OUT_OF_RESOURCES;
137 }
138
139 BlockIo = InternalApfsTranslateBlock (PrivateData, PrivateData->EfiJumpStart, &Lba);
140
141 //
142 // Read jump start and abort on failure.
143 //
144 Status = BlockIo->ReadBlocks (
145 BlockIo,
146 BlockIo->Media->MediaId,
147 Lba,
148 PrivateData->ApfsBlockSize,
149 JumpStart
150 );
151
152 DEBUG ((
153 DEBUG_INFO,
154 "OCJS: Block (P:%d|F:%d) read req %Lx -> %Lx of %x (mask %u, mul %u) - %r\n",
155 BlockIo == PrivateData->BlockIo,
156 PrivateData->IsFusion,
157 PrivateData->EfiJumpStart,
158 Lba,
159 PrivateData->ApfsBlockSize,
160 PrivateData->FusionMask,
161 PrivateData->LbaMultiplier,
162 Status
163 ));
164
165 if (EFI_ERROR (Status)) {
166 FreePool (JumpStart);
167 return Status;
168 }
169
170 //
171 // Jump start is expected to have JSDR magic.
172 // Version is not checked by ApfsJumpStart driver.
173 //
174 if (JumpStart->Magic != APFS_NX_EFI_JUMPSTART_MAGIC) {
175 DEBUG ((DEBUG_INFO, "OCJS: Unknown JSDR magic %08x, expected %08x\n", JumpStart->Magic, APFS_NX_EFI_JUMPSTART_MAGIC));
176 FreePool (JumpStart);
177 return EFI_UNSUPPORTED;
178 }
179
180 //
181 // Calculate and verify checksum.
182 //
183 if (!ApfsBlockChecksumVerify (&JumpStart->BlockHeader, PrivateData->ApfsBlockSize)) {
184 FreePool (JumpStart);
185 return EFI_UNSUPPORTED;
186 }
187
188 //
189 // Ensure that extent count does not overflow.
190 //
191 MaxExtents = (PrivateData->ApfsBlockSize - sizeof (*JumpStart)) / sizeof (JumpStart->RecordExtents[0]);
192 if (MaxExtents < JumpStart->NumExtents) {
193 DEBUG ((DEBUG_INFO, "OCJS: Invalid extent count %u / %u\n", JumpStart->NumExtents, MaxExtents));
194 FreePool (JumpStart);
195 return EFI_UNSUPPORTED;
196 }
197
198 *JumpStartPtr = JumpStart;
199 return EFI_SUCCESS;
200}
201
202STATIC
203EFI_STATUS
205 IN APFS_PRIVATE_DATA *PrivateData,
206 IN APFS_NX_EFI_JUMPSTART *JumpStart,
207 OUT UINT32 *DriverSize,
208 OUT VOID **DriverBuffer
209 )
210{
211 EFI_STATUS Status;
212 VOID *EfiFile;
213 UINTN EfiFileSize;
214 UINTN OrgEfiFileSize;
215 UINT8 *ChunkPtr;
216 UINTN ChunkSize;
217 UINTN Index;
218 EFI_BLOCK_IO_PROTOCOL *BlockIo;
219 EFI_LBA Lba;
220
221 EfiFileSize = JumpStart->EfiFileLen / PrivateData->ApfsBlockSize + 1;
222 if (BaseOverflowMulUN (EfiFileSize, PrivateData->ApfsBlockSize, &EfiFileSize)) {
223 return EFI_SECURITY_VIOLATION;
224 }
225
226 OrgEfiFileSize = EfiFileSize;
227
228 EfiFile = AllocateZeroPool (EfiFileSize);
229 if (EfiFile == NULL) {
230 return EFI_OUT_OF_RESOURCES;
231 }
232
233 ChunkPtr = EfiFile;
234
235 for (Index = 0; Index < JumpStart->NumExtents || EfiFileSize != 0; ++Index) {
237 PrivateData,
238 JumpStart->RecordExtents[Index].StartPhysicalAddr,
239 &Lba
240 );
241
242 if ( (JumpStart->RecordExtents[Index].BlockCount > MAX_UINTN)
243 || BaseOverflowMulUN ((UINTN)JumpStart->RecordExtents[Index].BlockCount, PrivateData->ApfsBlockSize, &ChunkSize)
244 || (ChunkSize > EfiFileSize))
245 {
246 FreePool (EfiFile);
247 return EFI_SECURITY_VIOLATION;
248 }
249
250 Status = BlockIo->ReadBlocks (
251 BlockIo,
252 BlockIo->Media->MediaId,
253 Lba,
254 ChunkSize,
255 ChunkPtr
256 );
257
258 if (EFI_ERROR (Status)) {
259 FreePool (EfiFile);
260 return Status;
261 }
262
263 ChunkPtr += ChunkSize;
264 EfiFileSize -= ChunkSize;
265 }
266
267 //
268 // Ensure that we do not have meaningful trailing memory just in case.
269 //
270 if (OrgEfiFileSize != JumpStart->EfiFileLen) {
271 ChunkPtr = EfiFile;
272 ChunkPtr += JumpStart->EfiFileLen;
273 ZeroMem (ChunkPtr, OrgEfiFileSize - JumpStart->EfiFileLen);
274 }
275
276 *DriverSize = JumpStart->EfiFileLen;
277 *DriverBuffer = EfiFile;
278
279 return EFI_SUCCESS;
280}
281
282EFI_STATUS
284 IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
285 OUT APFS_NX_SUPERBLOCK **SuperBlockPtr
286 )
287{
288 EFI_STATUS Status;
289 APFS_NX_SUPERBLOCK *SuperBlock;
290 UINTN ReadSize;
291 UINTN Retry;
292
293 //
294 // According to APFS spec APFS block size is a multiple of disk block size.
295 // Start by reading APFS_NX_MINIMUM_BLOCK_SIZE aligned to block size.
296 //
297 ReadSize = ALIGN_VALUE (APFS_NX_MINIMUM_BLOCK_SIZE, BlockIo->Media->BlockSize);
298
299 SuperBlock = NULL;
300
301 //
302 // Second attempt is given for cases when block size is bigger than our guessed size.
303 //
304 for (Retry = 0; Retry < 2; ++Retry) {
305 //
306 // Allocate memory for super block.
307 //
308 SuperBlock = AllocateZeroPool (ReadSize);
309 if (SuperBlock == NULL) {
310 break;
311 }
312
313 //
314 // Read super block and abort on failure.
315 //
316 Status = BlockIo->ReadBlocks (
317 BlockIo,
318 BlockIo->Media->MediaId,
319 0,
320 ReadSize,
321 SuperBlock
322 );
323 if (EFI_ERROR (Status)) {
324 break;
325 }
326
327 DEBUG ((
328 DEBUG_VERBOSE,
329 "OCJS: Testing disk with %8X magic %u block\n",
330 SuperBlock->Magic,
331 SuperBlock->BlockSize
332 ));
333
334 //
335 // Super block is expected to have NXSB magic.
336 //
337 if (SuperBlock->Magic != APFS_NX_SIGNATURE) {
338 break;
339 }
340
341 //
342 // Ensure APFS block size is:
343 // - A multiple of disk block size.
344 // - Divisible by UINT32 for fletcher checksum to work (e.g. when block size is 1 or 2).
345 // - Within minimum and maximum edges.
346 //
347 if ( (SuperBlock->BlockSize < BlockIo->Media->BlockSize)
348 || ((SuperBlock->BlockSize & (BlockIo->Media->BlockSize - 1)) != 0)
349 || ((SuperBlock->BlockSize & (sizeof (UINT32) - 1)) != 0)
350 || (SuperBlock->BlockSize < APFS_NX_MINIMUM_BLOCK_SIZE)
351 || (SuperBlock->BlockSize > APFS_NX_MAXIMUM_BLOCK_SIZE))
352 {
353 break;
354 }
355
356 //
357 // Check if we can calculate the checksum and try again on failure.
358 //
359 if (SuperBlock->BlockSize > ReadSize) {
360 ReadSize = SuperBlock->BlockSize;
361 FreePool (SuperBlock);
362 SuperBlock = NULL;
363 continue;
364 }
365
366 //
367 // Calculate and verify checksum.
368 //
369 if (!ApfsBlockChecksumVerify (&SuperBlock->BlockHeader, SuperBlock->BlockSize)) {
370 break;
371 }
372
373 //
374 // Verify object type and flags.
375 // SubType being 0 comes from ApfsJumpStart and is not documented.
376 // ObjectOid being 1 comes from ApfsJumpStart and is not documented.
377 //
379 || (SuperBlock->BlockHeader.ObjectSubType != 0)
380 || (SuperBlock->BlockHeader.ObjectOid != 1))
381 {
382 break;
383 }
384
385 //
386 // Super block is assumed to be legit.
387 //
388 *SuperBlockPtr = SuperBlock;
389 return EFI_SUCCESS;
390 }
391
392 //
393 // All retry attempts exceeded.
394 //
395 if (SuperBlock != NULL) {
396 FreePool (SuperBlock);
397 }
398
399 return EFI_UNSUPPORTED;
400}
401
402EFI_STATUS
404 IN APFS_PRIVATE_DATA *PrivateData,
405 OUT UINT32 *DriverSize,
406 OUT VOID **DriverBuffer
407 )
408{
409 EFI_STATUS Status;
410 APFS_NX_EFI_JUMPSTART *JumpStart;
411
412 Status = ApfsReadJumpStart (
413 PrivateData,
414 &JumpStart
415 );
416 if (EFI_ERROR (Status)) {
417 DEBUG ((
418 DEBUG_INFO,
419 "OCJS: Failed to read JumpStart for %g - %r\n",
420 &PrivateData->LocationInfo.ContainerUuid,
421 Status
422 ));
423 return Status;
424 }
425
426 Status = ApfsReadDriver (
427 PrivateData,
428 JumpStart,
429 DriverSize,
430 DriverBuffer
431 );
432
433 FreePool (JumpStart);
434
435 if (EFI_ERROR (Status)) {
436 DEBUG ((
437 DEBUG_INFO,
438 "OCJS: Failed to read driver for %g - %r\n",
439 &PrivateData->LocationInfo.ContainerUuid,
440 Status
441 ));
442 return Status;
443 }
444
445 return EFI_SUCCESS;
446}
#define APFS_NX_EFI_JUMPSTART_MAGIC
Definition Apfs.h:78
#define APFS_NX_MINIMUM_BLOCK_SIZE
Definition Apfs.h:71
#define APFS_NX_SIGNATURE
Definition Apfs.h:63
#define APFS_OBJ_EPHEMERAL
Definition Apfs.h:54
#define APFS_NX_MAXIMUM_BLOCK_SIZE
Definition Apfs.h:73
#define APFS_OBJECT_TYPE_NX_SUPERBLOCK
Definition Apfs.h:21
EFI_BLOCK_IO_PROTOCOL * InternalApfsTranslateBlock(IN APFS_PRIVATE_DATA *PrivateData, IN UINT64 Block, OUT EFI_LBA *Lba)
#define APFS_MOD_MAX_UINT32(Value, Result)
STATIC BOOLEAN ApfsBlockChecksumVerify(APFS_OBJ_PHYS *Block, UINTN DataSize)
Definition OcApfsIo.c:88
STATIC EFI_STATUS ApfsReadJumpStart(IN APFS_PRIVATE_DATA *PrivateData, OUT APFS_NX_EFI_JUMPSTART **JumpStartPtr)
Definition OcApfsIo.c:112
EFI_STATUS InternalApfsReadDriver(IN APFS_PRIVATE_DATA *PrivateData, OUT UINT32 *DriverSize, OUT VOID **DriverBuffer)
Definition OcApfsIo.c:403
STATIC EFI_STATUS ApfsReadDriver(IN APFS_PRIVATE_DATA *PrivateData, IN APFS_NX_EFI_JUMPSTART *JumpStart, OUT UINT32 *DriverSize, OUT VOID **DriverBuffer)
Definition OcApfsIo.c:204
EFI_STATUS InternalApfsReadSuperBlock(IN EFI_BLOCK_IO_PROTOCOL *BlockIo, OUT APFS_NX_SUPERBLOCK **SuperBlockPtr)
Definition OcApfsIo.c:283
STATIC UINT64 ApfsFletcher64(VOID *Data, UINTN DataSize)
Definition OcApfsIo.c:25
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
#define ASSERT(x)
Definition coder.h:55
APFS_PHYSICAL_RANGE RecordExtents[]
Definition Apfs.h:344
APFS_OBJ_PHYS BlockHeader
Definition Apfs.h:317
APFS_OBJ_PHYS BlockHeader
Definition Apfs.h:198
UINT32 BlockSize
Definition Apfs.h:208
UINT64 Checksum
Definition Apfs.h:161
UINT64 ObjectOid
Definition Apfs.h:167
UINT32 ObjectType
Definition Apfs.h:176
UINT32 ObjectSubType
Definition Apfs.h:183