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