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