1 /*
2 * Copyright (c) 2006-2019 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <sys/uio.h>
30 #include <sys/conf.h>
31
32 #include <IOKit/IOLib.h>
33 #include <IOKit/IOBSD.h>
34 #include <IOKit/IOService.h>
35 #include <IOKit/IOPlatformExpert.h>
36 #include <IOKit/IOPolledInterface.h>
37 #include <IOKit/IOHibernatePrivate.h>
38 #include <IOKit/IOBufferMemoryDescriptor.h>
39 #include <IOKit/AppleKeyStoreInterface.h>
40 #include <libkern/c++/OSSharedPtr.h>
41 #include "IOKitKernelInternal.h"
42
43 #if defined(__arm64__)
44 #include <pexpert/arm64/board_config.h>
45 #endif /* defined(__arm64__) */
46
47 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
48
49 OSDefineMetaClassAndAbstractStructors(IOPolledInterface, OSObject);
50
51 OSMetaClassDefineReservedUsedX86(IOPolledInterface, 0);
52 OSMetaClassDefineReservedUnused(IOPolledInterface, 1);
53 OSMetaClassDefineReservedUnused(IOPolledInterface, 2);
54 OSMetaClassDefineReservedUnused(IOPolledInterface, 3);
55 OSMetaClassDefineReservedUnused(IOPolledInterface, 4);
56 OSMetaClassDefineReservedUnused(IOPolledInterface, 5);
57 OSMetaClassDefineReservedUnused(IOPolledInterface, 6);
58 OSMetaClassDefineReservedUnused(IOPolledInterface, 7);
59 OSMetaClassDefineReservedUnused(IOPolledInterface, 8);
60 OSMetaClassDefineReservedUnused(IOPolledInterface, 9);
61 OSMetaClassDefineReservedUnused(IOPolledInterface, 10);
62 OSMetaClassDefineReservedUnused(IOPolledInterface, 11);
63 OSMetaClassDefineReservedUnused(IOPolledInterface, 12);
64 OSMetaClassDefineReservedUnused(IOPolledInterface, 13);
65 OSMetaClassDefineReservedUnused(IOPolledInterface, 14);
66 OSMetaClassDefineReservedUnused(IOPolledInterface, 15);
67
68 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
69
70 #ifndef kIOMediaPreferredBlockSizeKey
71 #define kIOMediaPreferredBlockSizeKey "Preferred Block Size"
72 #endif
73
74 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
75
76 class IOPolledFilePollers : public OSObject
77 {
78 OSDeclareDefaultStructors(IOPolledFilePollers);
79
80 public:
81 IOService * media;
82 OSArray * pollers;
83 IOBufferMemoryDescriptor * ioBuffer;
84 bool abortable;
85 bool io;
86 IOReturn ioStatus;
87 uint32_t openCount;
88
89 static IOPolledFilePollers * copyPollers(IOService * media);
90 };
91
OSDefineMetaClassAndStructors(IOPolledFilePollers,OSObject)92 OSDefineMetaClassAndStructors(IOPolledFilePollers, OSObject)
93
94 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
95
96 IOPolledFilePollers *
97 IOPolledFilePollers::copyPollers(IOService * media)
98 {
99 IOPolledFilePollers * vars;
100 IOReturn err;
101 IOService * service;
102 OSObject * obj;
103 IORegistryEntry * next;
104 IORegistryEntry * child;
105
106 if ((obj = media->copyProperty(kIOPolledInterfaceStackKey))) {
107 IOPolledFilePollers * ioPFPObj = OSDynamicCast(IOPolledFilePollers, obj);
108 if (!ioPFPObj) {
109 OSSafeReleaseNULL(obj);
110 }
111 return ioPFPObj;
112 }
113
114 do{
115 vars = OSTypeAlloc(IOPolledFilePollers);
116 vars->init();
117
118 vars->pollers = OSArray::withCapacity(4);
119 if (!vars->pollers) {
120 err = kIOReturnNoMemory;
121 break;
122 }
123
124 next = vars->media = media;
125 do{
126 IOPolledInterface * poller;
127 OSObject * obj;
128
129 obj = next->getProperty(kIOPolledInterfaceSupportKey);
130 if (kOSBooleanFalse == obj) {
131 vars->pollers->flushCollection();
132 break;
133 } else if ((poller = OSDynamicCast(IOPolledInterface, obj))) {
134 vars->pollers->setObject(poller);
135 }
136
137 if ((service = OSDynamicCast(IOService, next))
138 && service->getDeviceMemory()
139 && !vars->pollers->getCount()) {
140 break;
141 }
142
143 child = next;
144 }while ((next = child->getParentEntry(gIOServicePlane))
145 && child->isParent(next, gIOServicePlane, true));
146
147 if (!vars->pollers->getCount()) {
148 err = kIOReturnUnsupported;
149 break;
150 }
151 }while (false);
152
153 media->setProperty(kIOPolledInterfaceStackKey, vars);
154
155 return vars;
156 }
157
158 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
159
160 static IOReturn
161 IOPolledFilePollersIODone(IOPolledFilePollers * vars, bool abortable);
162
163 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
164
165 static IOReturn
IOPolledFilePollersProbe(IOPolledFilePollers * vars)166 IOPolledFilePollersProbe(IOPolledFilePollers * vars)
167 {
168 IOReturn err = kIOReturnError;
169 int32_t idx;
170 IOPolledInterface * poller;
171
172 for (idx = vars->pollers->getCount() - 1; idx >= 0; idx--) {
173 poller = (IOPolledInterface *) vars->pollers->getObject(idx);
174 err = poller->probe(vars->media);
175 if (err) {
176 HIBLOG("IOPolledInterface::probe[%d] 0x%x\n", idx, err);
177 break;
178 }
179 }
180
181 return err;
182 }
183
184 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
185
186 IOReturn
IOPolledFilePollersOpen(IOPolledFileIOVars * filevars,uint32_t state,bool abortable)187 IOPolledFilePollersOpen(IOPolledFileIOVars * filevars, uint32_t state, bool abortable)
188 {
189 IOPolledFilePollers * vars = filevars->pollers;
190 IOBufferMemoryDescriptor * ioBuffer;
191 IOPolledInterface * poller;
192 IOService * next;
193 IOReturn err = kIOReturnError;
194 int32_t idx;
195
196 vars->abortable = abortable;
197 ioBuffer = NULL;
198
199 if (kIOPolledAfterSleepState == state) {
200 vars->ioStatus = 0;
201 vars->io = false;
202 }
203 (void) IOPolledFilePollersIODone(vars, false);
204
205 if ((kIOPolledPreflightState == state) || (kIOPolledPreflightCoreDumpState == state)) {
206 ioBuffer = vars->ioBuffer;
207 if (!ioBuffer) {
208 vars->ioBuffer = ioBuffer = IOBufferMemoryDescriptor::withOptions(kIODirectionInOut,
209 kDefaultIONumBuffers * kDefaultIOSize, page_size);
210 if (!ioBuffer) {
211 return kIOReturnNoMemory;
212 }
213 }
214 }
215
216 for (idx = vars->pollers->getCount() - 1; idx >= 0; idx--) {
217 poller = (IOPolledInterface *) vars->pollers->getObject(idx);
218 err = poller->open(state, ioBuffer);
219 if (kIOReturnSuccess != err) {
220 HIBLOG("IOPolledInterface::open[%d] 0x%x\n", idx, err);
221 break;
222 }
223 }
224 if ((kIOReturnSuccess == err) && (kIOPolledPreflightState == state)) {
225 next = vars->media;
226 while (next) {
227 next->setProperty(kIOPolledInterfaceActiveKey, kOSBooleanTrue);
228 next = next->getProvider();
229 }
230 }
231
232 return err;
233 }
234
235 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
236
237 IOReturn
IOPolledFilePollersClose(IOPolledFileIOVars * filevars,uint32_t state)238 IOPolledFilePollersClose(IOPolledFileIOVars * filevars, uint32_t state)
239 {
240 IOPolledFilePollers * vars = filevars->pollers;
241 IOPolledInterface * poller;
242 IORegistryEntry * next;
243 IOReturn err;
244 int32_t idx;
245
246 (void) IOPolledFilePollersIODone(vars, false);
247
248 if ((kIOPolledPostflightState == state) || (kIOPolledPostflightCoreDumpState == state)) {
249 vars->openCount--;
250 }
251
252 for (idx = 0, err = kIOReturnSuccess;
253 (poller = (IOPolledInterface *) vars->pollers->getObject(idx));
254 idx++) {
255 err = poller->close(state);
256 if ((kIOReturnSuccess != err) && (kIOPolledBeforeSleepStateAborted == state)) {
257 err = poller->close(kIOPolledBeforeSleepState);
258 }
259 if (err) {
260 HIBLOG("IOPolledInterface::close[%d] 0x%x\n", idx, err);
261 }
262 }
263
264 if (kIOPolledPostflightState == state) {
265 next = vars->media;
266 while (next) {
267 next->removeProperty(kIOPolledInterfaceActiveKey);
268 next = next->getParentEntry(gIOServicePlane);
269 }
270 }
271
272 if ((kIOPolledPostflightState == state) || (kIOPolledPostflightCoreDumpState == state)) {
273 do{
274 if (vars->openCount) {
275 break;
276 }
277 if (vars->ioBuffer) {
278 vars->ioBuffer->release();
279 vars->ioBuffer = NULL;
280 }
281 }while (false);
282 }
283
284 return err;
285 }
286
287 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
288
289 IOReturn
setEncryptionKey(const uint8_t * key,size_t keySize)290 IOPolledInterface::setEncryptionKey(const uint8_t * key, size_t keySize)
291 {
292 return kIOReturnUnsupported;
293 }
294
295 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
296
297 IOReturn
IOPolledFilePollersSetEncryptionKey(IOPolledFileIOVars * filevars,const uint8_t * key,size_t keySize)298 IOPolledFilePollersSetEncryptionKey(IOPolledFileIOVars * filevars,
299 const uint8_t * key, size_t keySize)
300 {
301 IOReturn ret = kIOReturnUnsupported;
302 IOReturn err;
303 int32_t idx;
304 IOPolledFilePollers * vars = filevars->pollers;
305 IOPolledInterface * poller;
306
307 for (idx = 0;
308 (poller = (IOPolledInterface *) vars->pollers->getObject(idx));
309 idx++) {
310 poller = (IOPolledInterface *) vars->pollers->getObject(idx);
311 err = poller->setEncryptionKey(key, keySize);
312 if (kIOReturnSuccess == err) {
313 ret = err;
314 }
315 }
316
317 return ret;
318 }
319
320 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
321
322 IOMemoryDescriptor *
IOPolledFileGetIOBuffer(IOPolledFileIOVars * vars)323 IOPolledFileGetIOBuffer(IOPolledFileIOVars * vars)
324 {
325 return vars->pollers->ioBuffer;
326 }
327
328 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
329
330 static void
IOPolledIOComplete(void * target,void * parameter,IOReturn status,UInt64 actualByteCount)331 IOPolledIOComplete(void * target,
332 void * parameter,
333 IOReturn status,
334 UInt64 actualByteCount)
335 {
336 IOPolledFilePollers * vars = (IOPolledFilePollers *) parameter;
337
338 vars->ioStatus = status;
339 }
340
341 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
342
343 static IOReturn
IOStartPolledIO(IOPolledFilePollers * vars,uint32_t operation,uint32_t bufferOffset,uint64_t deviceOffset,uint64_t length)344 IOStartPolledIO(IOPolledFilePollers * vars,
345 uint32_t operation, uint32_t bufferOffset,
346 uint64_t deviceOffset, uint64_t length)
347 {
348 IOReturn err;
349 IOPolledInterface * poller;
350 IOPolledCompletion completion;
351
352 err = vars->ioStatus;
353 if (kIOReturnSuccess != err) {
354 return err;
355 }
356
357 completion.target = NULL;
358 completion.action = &IOPolledIOComplete;
359 completion.parameter = vars;
360
361 vars->ioStatus = -1;
362
363 poller = (IOPolledInterface *) vars->pollers->getObject(0);
364 err = poller->startIO(operation, bufferOffset, deviceOffset, length, completion);
365 if (err) {
366 if (kernel_debugger_entry_count) {
367 HIBLOG("IOPolledInterface::startIO[%d] 0x%x\n", 0, err);
368 } else {
369 HIBLOGFROMPANIC("IOPolledInterface::IOStartPolledIO(0x%p, %d, 0x%x, 0x%llx, %llu) : poller->startIO(%d, 0x%x, 0x%llx, %llu, completion) returned 0x%x",
370 vars, operation, bufferOffset, deviceOffset, length, operation, bufferOffset, deviceOffset, length, err);
371 }
372 }
373 return err;
374 }
375
376 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
377
378 static IOReturn
IOPolledFilePollersIODone(IOPolledFilePollers * vars,bool abortable)379 IOPolledFilePollersIODone(IOPolledFilePollers * vars, bool abortable)
380 {
381 IOReturn err = kIOReturnSuccess;
382 int32_t idx = 0;
383 IOPolledInterface * poller;
384 AbsoluteTime deadline;
385
386 if (!vars->io) {
387 return kIOReturnSuccess;
388 }
389
390 abortable &= vars->abortable;
391
392 clock_interval_to_deadline(2000, kMillisecondScale, &deadline);
393
394 while (-1 == vars->ioStatus) {
395 for (idx = 0;
396 (poller = (IOPolledInterface *) vars->pollers->getObject(idx));
397 idx++) {
398 IOReturn newErr;
399 newErr = poller->checkForWork();
400 if ((newErr == kIOReturnAborted) && !abortable) {
401 newErr = kIOReturnSuccess;
402 }
403 if (kIOReturnSuccess == err) {
404 err = newErr;
405 }
406 }
407 if ((false) && (kIOReturnSuccess == err) && (mach_absolute_time() > AbsoluteTime_to_scalar(&deadline))) {
408 HIBLOG("IOPolledInterface::forced timeout\n");
409 vars->ioStatus = kIOReturnTimeout;
410 }
411 }
412 vars->io = false;
413
414 #if HIBERNATION
415 if ((kIOReturnSuccess == err) && abortable && hibernate_should_abort()) {
416 err = kIOReturnAborted;
417 HIBLOG("IOPolledInterface::checkForWork sw abort\n");
418 }
419 #endif
420
421 if (err) {
422 HIBLOG("IOPolledInterface::checkForWork[%d] 0x%x\n", idx, err);
423 } else {
424 err = vars->ioStatus;
425 if (kIOReturnSuccess != err) {
426 HIBLOG("IOPolledInterface::ioStatus 0x%x\n", err);
427 }
428 }
429
430 return err;
431 }
432
433 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
434 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
435
436 struct _OpenFileContext {
437 OSData * extents;
438 uint64_t size;
439 };
440
441 static void
file_extent_callback(void * ref,uint64_t start,uint64_t length)442 file_extent_callback(void * ref, uint64_t start, uint64_t length)
443 {
444 _OpenFileContext * ctx = (_OpenFileContext *) ref;
445 IOPolledFileExtent extent;
446
447 extent.start = start;
448 extent.length = length;
449 ctx->extents->appendValue(extent);
450 ctx->size += length;
451 }
452
453 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
454
455 static IOService *
IOCopyMediaForDev(dev_t device)456 IOCopyMediaForDev(dev_t device)
457 {
458 OSDictionary * matching;
459 OSNumber * num;
460 OSIterator * iter;
461 IOService * result = NULL;
462
463 matching = IOService::serviceMatching("IOMedia");
464 if (!matching) {
465 return NULL;
466 }
467 do{
468 num = OSNumber::withNumber(major(device), 32);
469 if (!num) {
470 break;
471 }
472 matching->setObject(kIOBSDMajorKey, num);
473 num->release();
474 num = OSNumber::withNumber(minor(device), 32);
475 if (!num) {
476 break;
477 }
478 matching->setObject(kIOBSDMinorKey, num);
479 num->release();
480 if (!num) {
481 break;
482 }
483 iter = IOService::getMatchingServices(matching);
484 if (iter) {
485 result = (IOService *) iter->getNextObject();
486 result->retain();
487 iter->release();
488 }
489 }while (false);
490 matching->release();
491
492 return result;
493 }
494
495 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
496
497 #if defined(__i386__) || defined(__x86_64__)
498 #define APFSMEDIA_GETHIBERKEY "getHiberKey"
499
500 static IOReturn
IOGetVolumeCryptKey(dev_t block_dev,LIBKERN_RETURNS_RETAINED OSString ** pKeyUUID,uint8_t * volumeCryptKey,size_t * keySize)501 IOGetVolumeCryptKey(dev_t block_dev,
502 LIBKERN_RETURNS_RETAINED OSString ** pKeyUUID,
503 uint8_t * volumeCryptKey,
504 size_t * keySize)
505 {
506 IOReturn err;
507 IOService * part;
508 OSString * keyUUID = NULL;
509 OSString * keyStoreUUID = NULL;
510 uuid_t volumeKeyUUID;
511 aks_volume_key_t vek;
512 size_t callerKeySize;
513
514 static IOService * sKeyStore;
515
516 part = IOCopyMediaForDev(block_dev);
517 if (!part) {
518 return kIOReturnNotFound;
519 }
520
521 callerKeySize = *keySize;
522 // Try APFS first
523 {
524 uuid_t volUuid = {0};
525 err = part->callPlatformFunction(APFSMEDIA_GETHIBERKEY, false, &volUuid, volumeCryptKey, keySize, keySize);
526 if (kIOReturnBadArgument == err) {
527 // apfs fails on buffer size >32
528 *keySize = 32;
529 err = part->callPlatformFunction(APFSMEDIA_GETHIBERKEY, false, &volUuid, volumeCryptKey, keySize, keySize);
530 }
531 if (err != kIOReturnSuccess) {
532 *keySize = 0;
533 } else {
534 // No need to create uuid string if it's not requested
535 if (pKeyUUID) {
536 uuid_string_t volUuidStr;
537 uuid_unparse(volUuid, volUuidStr);
538 *pKeyUUID = OSString::withCString(volUuidStr);
539 }
540
541 part->release();
542 return kIOReturnSuccess;
543 }
544 }
545
546 // Then old CS path
547 err = part->callPlatformFunction(PLATFORM_FUNCTION_GET_MEDIA_ENCRYPTION_KEY_UUID, false,
548 (void *) &keyUUID, (void *) &keyStoreUUID, NULL, NULL);
549 if ((kIOReturnSuccess == err) && keyUUID && keyStoreUUID) {
550 // IOLog("got volume key %s\n", keyStoreUUID->getCStringNoCopy());
551
552 if (!sKeyStore) {
553 sKeyStore = (IOService *) IORegistryEntry::fromPath(AKS_SERVICE_PATH, gIOServicePlane);
554 }
555 if (sKeyStore) {
556 err = uuid_parse(keyStoreUUID->getCStringNoCopy(), volumeKeyUUID);
557 } else {
558 err = kIOReturnNoResources;
559 }
560 if (kIOReturnSuccess == err) {
561 err = sKeyStore->callPlatformFunction(gAKSGetKey, true, volumeKeyUUID, &vek, NULL, NULL);
562 }
563 if (kIOReturnSuccess != err) {
564 IOLog("volume key err 0x%x\n", err);
565 } else {
566 if (vek.key.keybytecount <= callerKeySize) {
567 *keySize = vek.key.keybytecount;
568 }
569 bcopy(&vek.key.keybytes[0], volumeCryptKey, *keySize);
570 }
571 bzero(&vek, sizeof(vek));
572
573 if (pKeyUUID) {
574 // Create a copy because the caller would release it
575 *pKeyUUID = OSString::withString(keyUUID);
576 }
577 }
578
579 part->release();
580 return err;
581 }
582 #endif /* defined(__i386__) || defined(__x86_64__) */
583
584 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
585
586 #if defined(__arm64__)
587 static IOReturn
IOGetHibernationCryptKey(uint8_t * hibernationKey,size_t * keySize,uint32_t * swSeed)588 IOGetHibernationCryptKey(uint8_t * hibernationKey,
589 size_t * keySize,
590 uint32_t *swSeed
591 )
592 {
593 return kIOReturnNotFound;
594 }
595 #endif /* defined(__arm64__) */
596
597 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
598
599 IOReturn
IOPolledFileOpen(const char * filename,uint32_t flags,uint64_t setFileSize,uint64_t fsFreeSize,void * write_file_addr,size_t write_file_len,IOPolledFileIOVars ** fileVars,OSData ** imagePath,uint8_t * volumeCryptKey,size_t * keySize)600 IOPolledFileOpen(const char * filename,
601 uint32_t flags,
602 uint64_t setFileSize, uint64_t fsFreeSize,
603 void * write_file_addr, size_t write_file_len,
604 IOPolledFileIOVars ** fileVars,
605 OSData ** imagePath,
606 uint8_t * volumeCryptKey, size_t * keySize)
607 {
608 IOReturn err = kIOReturnSuccess;
609 IOPolledFileIOVars * vars;
610 _OpenFileContext ctx;
611 OSData * extentsData = NULL;
612 OSNumber * num;
613 IOService * part = NULL;
614 dev_t block_dev;
615 dev_t image_dev;
616 AbsoluteTime startTime, endTime;
617 uint64_t nsec;
618
619 vars = IOMallocType(IOPolledFileIOVars);
620 vars->allocated = true;
621
622 do{
623 extentsData = OSData::withCapacity(32);
624 ctx.extents = extentsData;
625 ctx.size = 0;
626 clock_get_uptime(&startTime);
627
628 vars->fileRef = kern_open_file_for_direct_io(filename,
629 flags,
630 &file_extent_callback, &ctx,
631 setFileSize,
632 fsFreeSize,
633 // write file:
634 0, write_file_addr, write_file_len,
635 // results
636 &block_dev,
637 &image_dev,
638 &vars->block0,
639 &vars->maxiobytes,
640 &vars->flags);
641 #if 0
642 uint32_t msDelay = (131071 & random());
643 HIBLOG("sleep %d\n", msDelay);
644 IOSleep(msDelay);
645 #endif
646 clock_get_uptime(&endTime);
647 SUB_ABSOLUTETIME(&endTime, &startTime);
648 absolutetime_to_nanoseconds(endTime, &nsec);
649
650 if (!vars->fileRef) {
651 err = kIOReturnNoSpace;
652 }
653
654 HIBLOG("kern_open_file_for_direct_io took %qd ms\n", nsec / 1000000ULL);
655 if (kIOReturnSuccess != err) {
656 break;
657 }
658
659 HIBLOG("Opened file %s, size %qd, extents %ld, maxio %qx ssd %d\n", filename, ctx.size,
660 (extentsData->getLength() / sizeof(IOPolledFileExtent)) - 1,
661 vars->maxiobytes, kIOPolledFileSSD & vars->flags);
662 assert(!vars->block0);
663 if (extentsData->getLength() < sizeof(IOPolledFileExtent)) {
664 err = kIOReturnNoSpace;
665 break;
666 }
667
668 vars->fileSize = ctx.size;
669 vars->extentMap = (IOPolledFileExtent *) extentsData->getBytesNoCopy();
670
671 part = IOCopyMediaForDev(image_dev);
672 if (!part) {
673 err = kIOReturnNotFound;
674 break;
675 }
676
677 if (!(vars->pollers = IOPolledFilePollers::copyPollers(part))) {
678 break;
679 }
680
681 if ((num = OSDynamicCast(OSNumber, part->getProperty(kIOMediaPreferredBlockSizeKey)))) {
682 vars->blockSize = num->unsigned32BitValue();
683 }
684 if (vars->blockSize < 4096) {
685 vars->blockSize = 4096;
686 }
687
688 HIBLOG("polled file major %d, minor %d, blocksize %ld, pollers %d\n",
689 major(image_dev), minor(image_dev), (long)vars->blockSize,
690 vars->pollers->pollers->getCount());
691
692 OSString * keyUUID = NULL;
693 #if defined(__i386__) || defined(__x86_64__)
694 if (volumeCryptKey) {
695 err = IOGetVolumeCryptKey(block_dev, &keyUUID, volumeCryptKey, keySize);
696 }
697 #elif defined(__arm64__)
698 uint32_t swSeed = 0;
699 if (volumeCryptKey) {
700 if (flags & kIOPolledFileHibernate) {
701 err = IOGetHibernationCryptKey(volumeCryptKey, keySize, &swSeed);
702 if (kIOReturnSuccess != err) {
703 HIBLOG("error 0x%x from IOGetHibernationCryptKey\n", err);
704 break;
705 }
706 } else {
707 *keySize = 0;
708 }
709 }
710 #else
711 if (volumeCryptKey) {
712 HIBLOG("IOPolledFileOpen: unable to get volumeCryptKey\n");
713 err = kIOReturnNotFound;
714 break;
715 }
716 #endif
717
718 *fileVars = vars;
719 vars->fileExtents = extentsData;
720
721 // make imagePath
722 OSData * data = NULL;
723 if (imagePath) {
724 #if defined(__i386__) || defined(__x86_64__)
725 char str2[24 + sizeof(uuid_string_t) + 2];
726
727 if (keyUUID) {
728 snprintf(str2, sizeof(str2), "%qx:%s",
729 vars->extentMap[0].start, keyUUID->getCStringNoCopy());
730 } else {
731 snprintf(str2, sizeof(str2), "%qx", vars->extentMap[0].start);
732 }
733
734 err = IOService::getPlatform()->callPlatformFunction(
735 gIOCreateEFIDevicePathSymbol, false,
736 (void *) part, (void *) str2,
737 (void *) (uintptr_t) true, (void *) &data);
738 #elif defined(__arm64__)
739 char str2[26];
740 snprintf(str2, sizeof(str2), "%qx:%x", vars->extentMap[0].start, swSeed);
741 data = OSData::withBytes(str2, (unsigned int) strlen(str2));
742 err = kIOReturnSuccess;
743 #else
744 err = kIOReturnNotFound;
745 #endif
746 if (kIOReturnSuccess != err) {
747 HIBLOG("error 0x%x getting path\n", err);
748 OSSafeReleaseNULL(keyUUID);
749 break;
750 }
751 *imagePath = data;
752 }
753
754 // Release key UUID if we have one
755 OSSafeReleaseNULL(keyUUID);
756 }while (false);
757
758 if (kIOReturnSuccess != err) {
759 HIBLOG("error 0x%x opening polled file\n", err);
760 IOPolledFileClose(&vars, 0, NULL, 0, 0, 0, false);
761 if (extentsData) {
762 extentsData->release();
763 }
764 }
765
766 if (part) {
767 part->release();
768 }
769
770 return err;
771 }
772
773 IOReturn
IOPolledFileOpen(const char * filename,uint32_t flags,uint64_t setFileSize,uint64_t fsFreeSize,void * write_file_addr,size_t write_file_len,IOPolledFileIOVars ** fileVars,OSSharedPtr<OSData> & imagePath,uint8_t * volumeCryptKey,size_t * keySize)774 IOPolledFileOpen(const char * filename,
775 uint32_t flags,
776 uint64_t setFileSize, uint64_t fsFreeSize,
777 void * write_file_addr, size_t write_file_len,
778 IOPolledFileIOVars ** fileVars,
779 OSSharedPtr<OSData>& imagePath,
780 uint8_t * volumeCryptKey, size_t * keySize)
781 {
782 OSData* imagePathRaw = NULL;
783 IOReturn result = IOPolledFileOpen(filename, flags, setFileSize, fsFreeSize, write_file_addr, write_file_len,
784 fileVars, &imagePathRaw, volumeCryptKey, keySize);
785 imagePath.reset(imagePathRaw, OSNoRetain);
786 return result;
787 }
788
789 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
790
791 IOReturn
IOPolledFileClose(IOPolledFileIOVars ** pVars,off_t write_offset,void * addr,size_t write_length,off_t discard_offset,off_t discard_end,bool unlink)792 IOPolledFileClose(IOPolledFileIOVars ** pVars,
793 off_t write_offset, void * addr, size_t write_length,
794 off_t discard_offset, off_t discard_end, bool unlink)
795 {
796 IOPolledFileIOVars * vars;
797
798 vars = *pVars;
799 if (!vars) {
800 return kIOReturnSuccess;
801 }
802
803 if (vars->fileRef) {
804 kern_close_file_for_direct_io(vars->fileRef, write_offset, addr, write_length,
805 discard_offset, discard_end, unlink);
806 vars->fileRef = NULL;
807 }
808 if (vars->fileExtents) {
809 vars->fileExtents->release();
810 vars->fileExtents = NULL;
811 }
812 if (vars->pollers) {
813 vars->pollers->release();
814 vars->pollers = NULL;
815 }
816
817 if (vars->allocated) {
818 IOFreeType(vars, IOPolledFileIOVars);
819 } else {
820 bzero(vars, sizeof(IOPolledFileIOVars));
821 }
822 *pVars = NULL;
823
824 return kIOReturnSuccess;
825 }
826
827 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
828
829 IOReturn
IOPolledFilePollersSetup(IOPolledFileIOVars * vars,uint32_t openState)830 IOPolledFilePollersSetup(IOPolledFileIOVars * vars,
831 uint32_t openState)
832 {
833 IOReturn err;
834
835 err = kIOReturnSuccess;
836 do{
837 if (!vars->pollers->openCount) {
838 err = IOPolledFilePollersProbe(vars->pollers);
839 if (kIOReturnSuccess != err) {
840 break;
841 }
842 }
843 err = IOPolledFilePollersOpen(vars, openState, false);
844 if (kIOReturnSuccess != err) {
845 break;
846 }
847 if ((kIOPolledPreflightState == openState) || (kIOPolledPreflightCoreDumpState == openState)) {
848 vars->pollers->openCount++;
849 }
850 vars->pollers->io = false;
851 vars->buffer = (uint8_t *) vars->pollers->ioBuffer->getBytesNoCopy();
852 vars->bufferHalf = 0;
853 vars->bufferOffset = 0;
854 assert(vars->pollers->ioBuffer->getLength() <= UINT_MAX);
855 vars->bufferSize = (typeof(vars->bufferSize))(vars->pollers->ioBuffer->getLength() >> 1);
856
857 if (vars->maxiobytes < vars->bufferSize) {
858 vars->bufferSize = (typeof(vars->bufferSize))vars->maxiobytes;
859 }
860 }while (false);
861
862 if (kIOReturnSuccess != err) {
863 HIBLOG("IOPolledFilePollersSetup(%d) error 0x%x\n", openState, err);
864 }
865
866 return err;
867 }
868
869
870 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
871
872 IOReturn
IOPolledFileSeek(IOPolledFileIOVars * vars,uint64_t position)873 IOPolledFileSeek(IOPolledFileIOVars * vars, uint64_t position)
874 {
875 IOPolledFileExtent * extentMap;
876
877 extentMap = vars->extentMap;
878
879 vars->position = position;
880
881 if (position > vars->fileSize) {
882 HIBLOG("IOPolledFileSeek: called to seek to 0x%llx greater than file size of 0x%llx\n", vars->position, vars->fileSize);
883 return kIOReturnNoSpace;
884 }
885
886 while (position >= extentMap->length) {
887 position -= extentMap->length;
888 extentMap++;
889 }
890
891 vars->currentExtent = extentMap;
892 vars->extentRemaining = extentMap->length - position;
893 vars->extentPosition = vars->position - position;
894
895 if (vars->bufferSize <= vars->extentRemaining) {
896 vars->bufferLimit = vars->bufferSize;
897 } else {
898 vars->bufferLimit = ((uint32_t) vars->extentRemaining);
899 }
900
901 return kIOReturnSuccess;
902 }
903
904 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
905
906 IOReturn
IOPolledFileWrite(IOPolledFileIOVars * vars,const uint8_t * bytes,IOByteCount size,IOPolledFileCryptVars * cryptvars)907 IOPolledFileWrite(IOPolledFileIOVars * vars,
908 const uint8_t * bytes, IOByteCount size,
909 IOPolledFileCryptVars * cryptvars)
910 {
911 IOReturn err = kIOReturnSuccess;
912 IOByteCount copy, original_size = size;
913 bool flush = false;
914
915 do{
916 if (!bytes && !size) {
917 // seek to end of block & flush
918 size = vars->position & (vars->blockSize - 1);
919 if (size) {
920 size = vars->blockSize - size;
921 }
922 flush = true;
923 }
924
925 copy = vars->bufferLimit - vars->bufferOffset;
926 if (copy > size) {
927 copy = size;
928 } else {
929 flush = true;
930 }
931
932 if (bytes) {
933 #if KASAN
934 /* Since this may copy mach-o segments in bulk, use the nosan variants of bcopy to
935 * avoid triggering global redzone sanitizer violations when accessing
936 * interstices between 'C' structures
937 */
938 __nosan_bcopy(bytes, vars->buffer + vars->bufferHalf + vars->bufferOffset, copy);
939 #else
940 bcopy(bytes, vars->buffer + vars->bufferHalf + vars->bufferOffset, copy);
941 #endif
942 bytes += copy;
943 } else {
944 bzero(vars->buffer + vars->bufferHalf + vars->bufferOffset, copy);
945 }
946
947 size -= copy;
948 vars->bufferOffset += copy;
949 vars->position += copy;
950
951 if (flush && vars->bufferOffset) {
952 uint64_t offset = (vars->position - vars->bufferOffset
953 - vars->extentPosition + vars->currentExtent->start);
954 uint32_t length = (vars->bufferOffset);
955
956 #if CRYPTO
957 if (cryptvars && vars->encryptStart
958 && (vars->position > vars->encryptStart)
959 && ((vars->position - length) < vars->encryptEnd)) {
960 AbsoluteTime startTime, endTime;
961
962 uint64_t encryptLen, encryptStart;
963 encryptLen = vars->position - vars->encryptStart;
964 if (encryptLen > length) {
965 encryptLen = length;
966 }
967 encryptStart = length - encryptLen;
968 if (vars->position > vars->encryptEnd) {
969 encryptLen -= (vars->position - vars->encryptEnd);
970 }
971
972 clock_get_uptime(&startTime);
973
974 assert(encryptLen <= UINT_MAX);
975 // encrypt the buffer
976 aes_encrypt_cbc(vars->buffer + vars->bufferHalf + encryptStart,
977 &cryptvars->aes_iv[0],
978 (unsigned int) (encryptLen / AES_BLOCK_SIZE),
979 vars->buffer + vars->bufferHalf + encryptStart,
980 &cryptvars->ctx.encrypt);
981
982 clock_get_uptime(&endTime);
983 ADD_ABSOLUTETIME(&vars->cryptTime, &endTime);
984 SUB_ABSOLUTETIME(&vars->cryptTime, &startTime);
985 vars->cryptBytes += encryptLen;
986
987 // save initial vector for following encrypts
988 bcopy(vars->buffer + vars->bufferHalf + encryptStart + encryptLen - AES_BLOCK_SIZE,
989 &cryptvars->aes_iv[0],
990 AES_BLOCK_SIZE);
991 }
992 #endif /* CRYPTO */
993
994 err = IOPolledFilePollersIODone(vars->pollers, true);
995 if (kIOReturnSuccess != err) {
996 break;
997 }
998
999 if (vars->position & (vars->blockSize - 1)) {
1000 HIBLOG("misaligned file pos %qx\n", vars->position);
1001 }
1002 //if (length != vars->bufferSize) HIBLOG("short write of %qx ends@ %qx\n", length, offset + length);
1003
1004 err = IOStartPolledIO(vars->pollers, kIOPolledWrite, vars->bufferHalf, offset, length);
1005 if (kIOReturnSuccess != err) {
1006 HIBLOGFROMPANIC("IOPolledFileWrite(0x%p, 0x%p, %llu, 0x%p) : IOStartPolledIO(0x%p, kIOPolledWrite, %llu, 0x%llx, %d) returned 0x%x\n",
1007 vars, bytes, (uint64_t) original_size, cryptvars, vars->pollers, (uint64_t) vars->bufferHalf, offset, length, err);
1008 break;
1009 }
1010 vars->pollers->io = true;
1011
1012 vars->extentRemaining -= vars->bufferOffset;
1013 if (!vars->extentRemaining) {
1014 vars->currentExtent++;
1015 vars->extentRemaining = vars->currentExtent->length;
1016 vars->extentPosition = vars->position;
1017 }
1018
1019 vars->bufferHalf = vars->bufferHalf ? 0 : vars->bufferSize;
1020 vars->bufferOffset = 0;
1021 if (vars->bufferSize <= vars->extentRemaining) {
1022 vars->bufferLimit = vars->bufferSize;
1023 } else {
1024 vars->bufferLimit = ((uint32_t) vars->extentRemaining);
1025 }
1026
1027 if (!vars->extentRemaining) {
1028 err = kIOReturnOverrun;
1029 break;
1030 }
1031
1032 flush = false;
1033 }
1034 }while (size);
1035
1036 return err;
1037 }
1038
1039 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1040
1041 IOReturn
IOPolledFileFlush(IOPolledFileIOVars * vars)1042 IOPolledFileFlush(IOPolledFileIOVars * vars)
1043 {
1044 // Only supported by the underlying polled mode driver on embedded currently (expect kIOReturnUnsupported on other platforms)
1045 IOReturn err = kIOReturnSuccess;
1046
1047 err = IOPolledFilePollersIODone(vars->pollers, true);
1048 if (kIOReturnSuccess != err) {
1049 return err;
1050 }
1051
1052 err = IOStartPolledIO(vars->pollers, kIOPolledFlush, 0, 0, 0);
1053 if (kIOReturnSuccess != err) {
1054 HIBLOGFROMPANIC("IOPolledFileFlush(0x%p) : IOStartPolledIO(0x%p, kIOPolledFlush, 0, 0, 0) returned 0x%x\n",
1055 vars, vars->pollers, err);
1056 return err;
1057 }
1058 vars->pollers->io = true;
1059
1060 return err;
1061 }
1062
1063 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1064
1065 IOReturn
IOPolledFileRead(IOPolledFileIOVars * vars,uint8_t * bytes,IOByteCount size,IOPolledFileCryptVars * cryptvars)1066 IOPolledFileRead(IOPolledFileIOVars * vars,
1067 uint8_t * bytes, IOByteCount size,
1068 IOPolledFileCryptVars * cryptvars)
1069 {
1070 IOReturn err = kIOReturnSuccess;
1071 IOByteCount copy;
1072
1073 // bytesWritten += size;
1074
1075 do{
1076 copy = vars->bufferLimit - vars->bufferOffset;
1077 if (copy > size) {
1078 copy = size;
1079 }
1080
1081 if (bytes) {
1082 #if KASAN
1083 __nosan_bcopy(vars->buffer + vars->bufferHalf + vars->bufferOffset, bytes, copy);
1084 #else
1085 bcopy(vars->buffer + vars->bufferHalf + vars->bufferOffset, bytes, copy);
1086 #endif
1087 bytes += copy;
1088 }
1089 size -= copy;
1090 vars->bufferOffset += copy;
1091 // vars->position += copy;
1092
1093 if ((vars->bufferOffset == vars->bufferLimit) && (vars->position < vars->readEnd)) {
1094 if (!vars->pollers->io) {
1095 cryptvars = NULL;
1096 }
1097 err = IOPolledFilePollersIODone(vars->pollers, true);
1098 if (kIOReturnSuccess != err) {
1099 break;
1100 }
1101
1102 if (vars->position & (vars->blockSize - 1)) {
1103 HIBLOG("misaligned file pos %qx\n", vars->position);
1104 }
1105
1106 vars->position += vars->lastRead;
1107 vars->extentRemaining -= vars->lastRead;
1108 vars->bufferLimit = vars->lastRead;
1109
1110 if (!vars->extentRemaining) {
1111 vars->currentExtent++;
1112 vars->extentRemaining = vars->currentExtent->length;
1113 vars->extentPosition = vars->position;
1114 if (!vars->extentRemaining) {
1115 err = kIOReturnOverrun;
1116 break;
1117 }
1118 }
1119
1120 uint32_t length;
1121 uint32_t lastReadLength = vars->lastRead;
1122 uint64_t offset = (vars->position
1123 - vars->extentPosition + vars->currentExtent->start);
1124 if (vars->extentRemaining <= vars->bufferSize) {
1125 length = ((uint32_t) vars->extentRemaining);
1126 } else {
1127 length = vars->bufferSize;
1128 }
1129 if ((length + vars->position) > vars->readEnd) {
1130 length = ((uint32_t) (vars->readEnd - vars->position));
1131 }
1132
1133 vars->lastRead = length;
1134 if (length) {
1135 //if (length != vars->bufferSize) HIBLOG("short read of %qx ends@ %qx\n", length, offset + length);
1136 err = IOStartPolledIO(vars->pollers, kIOPolledRead, vars->bufferHalf, offset, length);
1137 if (kIOReturnSuccess != err) {
1138 break;
1139 }
1140 vars->pollers->io = true;
1141 }
1142
1143 vars->bufferHalf = vars->bufferHalf ? 0 : vars->bufferSize;
1144 vars->bufferOffset = 0;
1145
1146 #if CRYPTO
1147 if (cryptvars) {
1148 uint8_t thisVector[AES_BLOCK_SIZE];
1149 AbsoluteTime startTime, endTime;
1150
1151 // save initial vector for following decrypts
1152 bcopy(&cryptvars->aes_iv[0], &thisVector[0], AES_BLOCK_SIZE);
1153 bcopy(vars->buffer + vars->bufferHalf + lastReadLength - AES_BLOCK_SIZE,
1154 &cryptvars->aes_iv[0], AES_BLOCK_SIZE);
1155
1156 // decrypt the buffer
1157 clock_get_uptime(&startTime);
1158
1159 assert(lastReadLength <= UINT_MAX);
1160 aes_decrypt_cbc(vars->buffer + vars->bufferHalf,
1161 &thisVector[0],
1162 (unsigned int) (lastReadLength / AES_BLOCK_SIZE),
1163 vars->buffer + vars->bufferHalf,
1164 &cryptvars->ctx.decrypt);
1165
1166 clock_get_uptime(&endTime);
1167 ADD_ABSOLUTETIME(&vars->cryptTime, &endTime);
1168 SUB_ABSOLUTETIME(&vars->cryptTime, &startTime);
1169 vars->cryptBytes += lastReadLength;
1170 }
1171 #endif /* CRYPTO */
1172 }
1173 }while (size);
1174
1175 return err;
1176 }
1177
1178 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1179