]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOPolledInterface.cpp
xnu-7195.101.1.tar.gz
[apple/xnu.git] / iokit / Kernel / IOPolledInterface.cpp
CommitLineData
d1ecb069 1/*
cb323159 2 * Copyright (c) 2006-2019 Apple Inc. All rights reserved.
d1ecb069
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
d1ecb069
A
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.
0a7de745 14 *
d1ecb069
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
d1ecb069
A
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.
0a7de745 25 *
d1ecb069
A
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
3e170ce0
A
29#include <sys/uio.h>
30#include <sys/conf.h>
31
32#include <IOKit/IOLib.h>
33#include <IOKit/IOBSD.h>
d1ecb069 34#include <IOKit/IOService.h>
3e170ce0 35#include <IOKit/IOPlatformExpert.h>
d1ecb069 36#include <IOKit/IOPolledInterface.h>
3e170ce0
A
37#include <IOKit/IOHibernatePrivate.h>
38#include <IOKit/IOBufferMemoryDescriptor.h>
39#include <IOKit/AppleKeyStoreInterface.h>
f427ee49 40#include <libkern/c++/OSSharedPtr.h>
3e170ce0
A
41#include "IOKitKernelInternal.h"
42
f427ee49
A
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__) */
d1ecb069
A
49
50/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
51
52OSDefineMetaClassAndAbstractStructors(IOPolledInterface, OSObject);
53
f427ee49 54OSMetaClassDefineReservedUsedX86(IOPolledInterface, 0);
d1ecb069
A
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
3e170ce0
A
71/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
72
73#ifndef kIOMediaPreferredBlockSizeKey
0a7de745 74#define kIOMediaPreferredBlockSizeKey "Preferred Block Size"
3e170ce0
A
75#endif
76
0a7de745 77enum { kDefaultIOSize = 128 * 1024 };
3e170ce0
A
78
79/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
80
81class IOPolledFilePollers : public OSObject
82{
cb323159 83 OSDeclareDefaultStructors(IOPolledFilePollers);
3e170ce0
A
84
85public:
0a7de745
A
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);
3e170ce0
A
95};
96
97OSDefineMetaClassAndStructors(IOPolledFilePollers, OSObject)
98
99/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
100
101IOPolledFilePollers *
102IOPolledFilePollers::copyPollers(IOService * media)
103{
0a7de745
A
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);
3e170ce0 113 }
3e170ce0 114
0a7de745
A
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;
3e170ce0
A
157}
158
159/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
160
0a7de745 161static IOReturn
3e170ce0
A
162IOPolledFilePollersIODone(IOPolledFilePollers * vars, bool abortable);
163
164/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
165
166static IOReturn
167IOPolledFilePollersProbe(IOPolledFilePollers * vars)
168{
0a7de745
A
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;
3e170ce0
A
183}
184
185/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
186
187IOReturn
188IOPolledFilePollersOpen(IOPolledFileIOVars * filevars, uint32_t state, bool abortable)
189{
0a7de745
A
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;
cb323159 198 ioBuffer = NULL;
0a7de745
A
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 }
3e170ce0 216
0a7de745
A
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 }
3e170ce0 231 }
3e170ce0 232
0a7de745 233 return err;
3e170ce0
A
234}
235
236/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
237
238IOReturn
239IOPolledFilePollersClose(IOPolledFileIOVars * filevars, uint32_t state)
240{
0a7de745
A
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--;
3e170ce0
A
251 }
252
0a7de745
A
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 }
3e170ce0 263 }
490019cf 264
0a7de745
A
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();
cb323159 280 vars->ioBuffer = NULL;
0a7de745
A
281 }
282 }while (false);
283 }
284
285 return err;
3e170ce0 286}
a39ff7e2
A
287
288/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
289
0a7de745
A
290IOReturn
291IOPolledInterface::setEncryptionKey(const uint8_t * key, size_t keySize)
a39ff7e2 292{
0a7de745 293 return kIOReturnUnsupported;
a39ff7e2
A
294}
295
296/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
297
298IOReturn
299IOPolledFilePollersSetEncryptionKey(IOPolledFileIOVars * filevars,
0a7de745 300 const uint8_t * key, size_t keySize)
a39ff7e2 301{
0a7de745
A
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;
a39ff7e2
A
319}
320
3e170ce0
A
321/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
322
323IOMemoryDescriptor *
324IOPolledFileGetIOBuffer(IOPolledFileIOVars * vars)
325{
0a7de745 326 return vars->pollers->ioBuffer;
3e170ce0
A
327}
328
329/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
330
331static void
332IOPolledIOComplete(void * target,
0a7de745
A
333 void * parameter,
334 IOReturn status,
335 UInt64 actualByteCount)
3e170ce0 336{
0a7de745 337 IOPolledFilePollers * vars = (IOPolledFilePollers *) parameter;
3e170ce0 338
0a7de745 339 vars->ioStatus = status;
3e170ce0
A
340}
341
342/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
343
344static IOReturn
0a7de745
A
345IOStartPolledIO(IOPolledFilePollers * vars,
346 uint32_t operation, uint32_t bufferOffset,
347 uint64_t deviceOffset, uint64_t length)
3e170ce0 348{
0a7de745
A
349 IOReturn err;
350 IOPolledInterface * poller;
351 IOPolledCompletion completion;
3e170ce0 352
0a7de745
A
353 err = vars->ioStatus;
354 if (kIOReturnSuccess != err) {
355 return err;
356 }
3e170ce0 357
cb323159 358 completion.target = NULL;
0a7de745
A
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 }
5ba3f43e 373 }
0a7de745 374 return err;
3e170ce0
A
375}
376
377/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
378
379static IOReturn
380IOPolledFilePollersIODone(IOPolledFilePollers * vars, bool abortable)
381{
0a7de745
A
382 IOReturn err = kIOReturnSuccess;
383 int32_t idx = 0;
384 IOPolledInterface * poller;
385 AbsoluteTime deadline;
3e170ce0 386
0a7de745
A
387 if (!vars->io) {
388 return kIOReturnSuccess;
389 }
3e170ce0 390
0a7de745
A
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;
3e170ce0
A
414
415#if HIBERNATION
0a7de745
A
416 if ((kIOReturnSuccess == err) && abortable && hibernate_should_abort()) {
417 err = kIOReturnAborted;
418 HIBLOG("IOPolledInterface::checkForWork sw abort\n");
419 }
3e170ce0
A
420#endif
421
0a7de745
A
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 }
3e170ce0 430
0a7de745 431 return err;
3e170ce0
A
432}
433
434/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
435/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
436
0a7de745
A
437struct _OpenFileContext {
438 OSData * extents;
439 uint64_t size;
3e170ce0
A
440};
441
442static void
443file_extent_callback(void * ref, uint64_t start, uint64_t length)
444{
0a7de745
A
445 _OpenFileContext * ctx = (_OpenFileContext *) ref;
446 IOPolledFileExtent extent;
3e170ce0 447
0a7de745
A
448 extent.start = start;
449 extent.length = length;
450 ctx->extents->appendBytes(&extent, sizeof(extent));
451 ctx->size += length;
3e170ce0
A
452}
453
454/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
455
0a7de745 456static IOService *
3e170ce0
A
457IOCopyMediaForDev(dev_t device)
458{
0a7de745
A
459 OSDictionary * matching;
460 OSNumber * num;
461 OSIterator * iter;
cb323159 462 IOService * result = NULL;
0a7de745
A
463
464 matching = IOService::serviceMatching("IOMedia");
465 if (!matching) {
cb323159 466 return NULL;
0a7de745
A
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;
3e170ce0
A
494}
495
f427ee49
A
496/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
497
498#if defined(__i386__) || defined(__x86_64__)
5ba3f43e
A
499#define APFSMEDIA_GETHIBERKEY "getHiberKey"
500
0a7de745 501static IOReturn
cb323159
A
502IOGetVolumeCryptKey(dev_t block_dev,
503 LIBKERN_RETURNS_RETAINED OSString ** pKeyUUID,
504 uint8_t * volumeCryptKey,
505 size_t * keySize)
3e170ce0 506{
0a7de745
A
507 IOReturn err;
508 IOService * part;
cb323159
A
509 OSString * keyUUID = NULL;
510 OSString * keyStoreUUID = NULL;
0a7de745
A
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
a39ff7e2 524 {
0a7de745
A
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 }
a39ff7e2 545 }
0a7de745
A
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) {
5ba3f43e
A
551// IOLog("got volume key %s\n", keyStoreUUID->getCStringNoCopy());
552
0a7de745
A
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;
3e170ce0 582}
f427ee49
A
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{
f427ee49 594 return kIOReturnNotFound;
f427ee49
A
595}
596#endif /* defined(__arm64__) */
3e170ce0
A
597
598/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
599
d1ecb069 600IOReturn
3e170ce0 601IOPolledFileOpen(const char * filename,
0a7de745
A
602 uint32_t flags,
603 uint64_t setFileSize, uint64_t fsFreeSize,
604 void * write_file_addr, size_t write_file_len,
605 IOPolledFileIOVars ** fileVars,
606 OSData ** imagePath,
607 uint8_t * volumeCryptKey, size_t * keySize)
d1ecb069 608{
0a7de745
A
609 IOReturn err = kIOReturnSuccess;
610 IOPolledFileIOVars * vars;
611 _OpenFileContext ctx;
612 OSData * extentsData = NULL;
613 OSNumber * num;
cb323159 614 IOService * part = NULL;
0a7de745
A
615 dev_t block_dev;
616 dev_t image_dev;
617 AbsoluteTime startTime, endTime;
618 uint64_t nsec;
619
620 vars = IONew(IOPolledFileIOVars, 1);
621 if (!vars) {
622 return kIOReturnNoMemory;
623 }
624 bzero(vars, sizeof(*vars));
625 vars->allocated = true;
626
627 do{
628 extentsData = OSData::withCapacity(32);
629 ctx.extents = extentsData;
630 ctx.size = 0;
631 clock_get_uptime(&startTime);
632
633 vars->fileRef = kern_open_file_for_direct_io(filename,
634 flags,
635 &file_extent_callback, &ctx,
636 setFileSize,
637 fsFreeSize,
638 // write file:
639 0, write_file_addr, write_file_len,
640 // results
641 &block_dev,
642 &image_dev,
643 &vars->block0,
644 &vars->maxiobytes,
645 &vars->flags);
3e170ce0 646#if 0
0a7de745
A
647 uint32_t msDelay = (131071 & random());
648 HIBLOG("sleep %d\n", msDelay);
649 IOSleep(msDelay);
3e170ce0 650#endif
0a7de745
A
651 clock_get_uptime(&endTime);
652 SUB_ABSOLUTETIME(&endTime, &startTime);
653 absolutetime_to_nanoseconds(endTime, &nsec);
654
655 if (!vars->fileRef) {
656 err = kIOReturnNoSpace;
657 }
658
659 HIBLOG("kern_open_file_for_direct_io took %qd ms\n", nsec / 1000000ULL);
660 if (kIOReturnSuccess != err) {
661 break;
662 }
663
664 HIBLOG("Opened file %s, size %qd, extents %ld, maxio %qx ssd %d\n", filename, ctx.size,
665 (extentsData->getLength() / sizeof(IOPolledFileExtent)) - 1,
666 vars->maxiobytes, kIOPolledFileSSD & vars->flags);
667 assert(!vars->block0);
668 if (extentsData->getLength() < sizeof(IOPolledFileExtent)) {
669 err = kIOReturnNoSpace;
670 break;
671 }
672
673 vars->fileSize = ctx.size;
674 vars->extentMap = (IOPolledFileExtent *) extentsData->getBytesNoCopy();
675
676 part = IOCopyMediaForDev(image_dev);
677 if (!part) {
678 err = kIOReturnNotFound;
679 break;
680 }
681
682 if (!(vars->pollers = IOPolledFilePollers::copyPollers(part))) {
683 break;
684 }
685
686 if ((num = OSDynamicCast(OSNumber, part->getProperty(kIOMediaPreferredBlockSizeKey)))) {
687 vars->blockSize = num->unsigned32BitValue();
688 }
689 if (vars->blockSize < 4096) {
690 vars->blockSize = 4096;
691 }
692
693 HIBLOG("polled file major %d, minor %d, blocksize %ld, pollers %d\n",
694 major(image_dev), minor(image_dev), (long)vars->blockSize,
695 vars->pollers->pollers->getCount());
696
697 OSString * keyUUID = NULL;
f427ee49 698#if defined(__i386__) || defined(__x86_64__)
0a7de745
A
699 if (volumeCryptKey) {
700 err = IOGetVolumeCryptKey(block_dev, &keyUUID, volumeCryptKey, keySize);
701 }
f427ee49
A
702#elif defined(__arm64__)
703 uint32_t swSeed = 0;
704 if (volumeCryptKey) {
705 if (flags & kIOPolledFileHibernate) {
706 err = IOGetHibernationCryptKey(volumeCryptKey, keySize, &swSeed);
707 if (kIOReturnSuccess != err) {
708 HIBLOG("error 0x%x from IOGetHibernationCryptKey\n", err);
709 break;
710 }
711 } else {
712 *keySize = 0;
713 }
714 }
715#else
716 if (volumeCryptKey) {
717 HIBLOG("IOPolledFileOpen: unable to get volumeCryptKey\n");
718 err = kIOReturnNotFound;
719 break;
720 }
721#endif
0a7de745
A
722
723 *fileVars = vars;
724 vars->fileExtents = extentsData;
725
726 // make imagePath
f427ee49 727 OSData * data = NULL;
0a7de745 728 if (imagePath) {
3e170ce0 729#if defined(__i386__) || defined(__x86_64__)
0a7de745
A
730 char str2[24 + sizeof(uuid_string_t) + 2];
731
732 if (keyUUID) {
733 snprintf(str2, sizeof(str2), "%qx:%s",
734 vars->extentMap[0].start, keyUUID->getCStringNoCopy());
735 } else {
736 snprintf(str2, sizeof(str2), "%qx", vars->extentMap[0].start);
737 }
738
739 err = IOService::getPlatform()->callPlatformFunction(
740 gIOCreateEFIDevicePathSymbol, false,
741 (void *) part, (void *) str2,
742 (void *) (uintptr_t) true, (void *) &data);
f427ee49
A
743#elif defined(__arm64__)
744 char str2[26];
745 snprintf(str2, sizeof(str2), "%qx:%x", vars->extentMap[0].start, swSeed);
746 data = OSData::withBytes(str2, (unsigned int) strlen(str2));
0a7de745 747 err = kIOReturnSuccess;
f427ee49
A
748#else
749 err = kIOReturnNotFound;
3e170ce0 750#endif
0a7de745
A
751 if (kIOReturnSuccess != err) {
752 HIBLOG("error 0x%x getting path\n", err);
753 break;
754 }
755 *imagePath = data;
756 }
757
758 // Release key UUID if we have one
759 if (keyUUID) {
760 keyUUID->release();
761 keyUUID = NULL; // Just in case
762 }
763 }while (false);
764
765 if (kIOReturnSuccess != err) {
766 HIBLOG("error 0x%x opening polled file\n", err);
cb323159 767 IOPolledFileClose(&vars, 0, NULL, 0, 0, 0);
0a7de745
A
768 if (extentsData) {
769 extentsData->release();
770 }
771 }
772
773 if (part) {
774 part->release();
775 }
776
777 return err;
d1ecb069 778}
3e170ce0 779
f427ee49
A
780IOReturn
781IOPolledFileOpen(const char * filename,
782 uint32_t flags,
783 uint64_t setFileSize, uint64_t fsFreeSize,
784 void * write_file_addr, size_t write_file_len,
785 IOPolledFileIOVars ** fileVars,
786 OSSharedPtr<OSData>& imagePath,
787 uint8_t * volumeCryptKey, size_t * keySize)
788{
789 OSData* imagePathRaw = NULL;
790 IOReturn result = IOPolledFileOpen(filename, flags, setFileSize, fsFreeSize, write_file_addr, write_file_len,
791 fileVars, &imagePathRaw, volumeCryptKey, keySize);
792 imagePath.reset(imagePathRaw, OSNoRetain);
793 return result;
794}
795
3e170ce0
A
796/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
797
798IOReturn
799IOPolledFileClose(IOPolledFileIOVars ** pVars,
0a7de745
A
800 off_t write_offset, void * addr, size_t write_length,
801 off_t discard_offset, off_t discard_end)
3e170ce0 802{
0a7de745
A
803 IOPolledFileIOVars * vars;
804
805 vars = *pVars;
806 if (!vars) {
807 return kIOReturnSuccess;
808 }
809
810 if (vars->fileRef) {
811 kern_close_file_for_direct_io(vars->fileRef, write_offset, addr, write_length,
812 discard_offset, discard_end);
813 vars->fileRef = NULL;
814 }
815 if (vars->fileExtents) {
816 vars->fileExtents->release();
cb323159 817 vars->fileExtents = NULL;
0a7de745
A
818 }
819 if (vars->pollers) {
820 vars->pollers->release();
cb323159 821 vars->pollers = NULL;
0a7de745
A
822 }
823
824 if (vars->allocated) {
825 IODelete(vars, IOPolledFileIOVars, 1);
826 } else {
827 bzero(vars, sizeof(IOPolledFileIOVars));
828 }
829 *pVars = NULL;
830
831 return kIOReturnSuccess;
3e170ce0
A
832}
833
834/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
835
836IOReturn
837IOPolledFilePollersSetup(IOPolledFileIOVars * vars,
0a7de745 838 uint32_t openState)
3e170ce0 839{
0a7de745
A
840 IOReturn err;
841
842 err = kIOReturnSuccess;
843 do{
844 if (!vars->pollers->openCount) {
845 err = IOPolledFilePollersProbe(vars->pollers);
846 if (kIOReturnSuccess != err) {
847 break;
848 }
849 }
850 err = IOPolledFilePollersOpen(vars, openState, false);
851 if (kIOReturnSuccess != err) {
852 break;
853 }
854 if ((kIOPolledPreflightState == openState) || (kIOPolledPreflightCoreDumpState == openState)) {
855 vars->pollers->openCount++;
856 }
857 vars->pollers->io = false;
858 vars->buffer = (uint8_t *) vars->pollers->ioBuffer->getBytesNoCopy();
859 vars->bufferHalf = 0;
860 vars->bufferOffset = 0;
f427ee49
A
861 assert(vars->pollers->ioBuffer->getLength() <= UINT_MAX);
862 vars->bufferSize = (typeof(vars->bufferSize))(vars->pollers->ioBuffer->getLength() >> 1);
0a7de745
A
863
864 if (vars->maxiobytes < vars->bufferSize) {
f427ee49 865 vars->bufferSize = (typeof(vars->bufferSize))vars->maxiobytes;
0a7de745
A
866 }
867 }while (false);
868
869 if (kIOReturnSuccess != err) {
870 HIBLOG("IOPolledFilePollersSetup(%d) error 0x%x\n", openState, err);
490019cf 871 }
3e170ce0 872
0a7de745 873 return err;
3e170ce0
A
874}
875
876
877/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
878
879IOReturn
880IOPolledFileSeek(IOPolledFileIOVars * vars, uint64_t position)
881{
0a7de745 882 IOPolledFileExtent * extentMap;
3e170ce0 883
0a7de745 884 extentMap = vars->extentMap;
3e170ce0 885
0a7de745 886 vars->position = position;
3e170ce0 887
0a7de745
A
888 if (position > vars->fileSize) {
889 HIBLOG("IOPolledFileSeek: called to seek to 0x%llx greater than file size of 0x%llx\n", vars->position, vars->fileSize);
890 return kIOReturnNoSpace;
891 }
5ba3f43e 892
0a7de745
A
893 while (position >= extentMap->length) {
894 position -= extentMap->length;
895 extentMap++;
896 }
3e170ce0 897
0a7de745
A
898 vars->currentExtent = extentMap;
899 vars->extentRemaining = extentMap->length - position;
900 vars->extentPosition = vars->position - position;
3e170ce0 901
0a7de745
A
902 if (vars->bufferSize <= vars->extentRemaining) {
903 vars->bufferLimit = vars->bufferSize;
904 } else {
f427ee49 905 vars->bufferLimit = ((uint32_t) vars->extentRemaining);
0a7de745 906 }
3e170ce0 907
0a7de745 908 return kIOReturnSuccess;
3e170ce0
A
909}
910
911/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
912
913IOReturn
914IOPolledFileWrite(IOPolledFileIOVars * vars,
0a7de745
A
915 const uint8_t * bytes, IOByteCount size,
916 IOPolledFileCryptVars * cryptvars)
3e170ce0 917{
0a7de745
A
918 IOReturn err = kIOReturnSuccess;
919 IOByteCount copy, original_size = size;
920 bool flush = false;
921
922 do{
923 if (!bytes && !size) {
924 // seek to end of block & flush
925 size = vars->position & (vars->blockSize - 1);
926 if (size) {
927 size = vars->blockSize - size;
928 }
929 flush = true;
0a7de745
A
930 }
931
932 copy = vars->bufferLimit - vars->bufferOffset;
933 if (copy > size) {
934 copy = size;
935 } else {
936 flush = true;
937 }
938
939 if (bytes) {
5c9f4661 940#if KASAN
0a7de745
A
941 /* Since this may copy mach-o segments in bulk, use the nosan variants of bcopy to
942 * avoid triggering global redzone sanitizer violations when accessing
943 * interstices between 'C' structures
944 */
945 __nosan_bcopy(bytes, vars->buffer + vars->bufferHalf + vars->bufferOffset, copy);
5c9f4661 946#else
0a7de745 947 bcopy(bytes, vars->buffer + vars->bufferHalf + vars->bufferOffset, copy);
5c9f4661 948#endif
0a7de745
A
949 bytes += copy;
950 } else {
951 bzero(vars->buffer + vars->bufferHalf + vars->bufferOffset, copy);
952 }
953
954 size -= copy;
955 vars->bufferOffset += copy;
956 vars->position += copy;
957
958 if (flush && vars->bufferOffset) {
959 uint64_t offset = (vars->position - vars->bufferOffset
960 - vars->extentPosition + vars->currentExtent->start);
961 uint32_t length = (vars->bufferOffset);
3e170ce0
A
962
963#if CRYPTO
0a7de745
A
964 if (cryptvars && vars->encryptStart
965 && (vars->position > vars->encryptStart)
966 && ((vars->position - length) < vars->encryptEnd)) {
967 AbsoluteTime startTime, endTime;
968
969 uint64_t encryptLen, encryptStart;
970 encryptLen = vars->position - vars->encryptStart;
971 if (encryptLen > length) {
972 encryptLen = length;
973 }
974 encryptStart = length - encryptLen;
975 if (vars->position > vars->encryptEnd) {
976 encryptLen -= (vars->position - vars->encryptEnd);
977 }
978
979 clock_get_uptime(&startTime);
980
f427ee49 981 assert(encryptLen <= UINT_MAX);
0a7de745
A
982 // encrypt the buffer
983 aes_encrypt_cbc(vars->buffer + vars->bufferHalf + encryptStart,
984 &cryptvars->aes_iv[0],
f427ee49 985 (unsigned int) (encryptLen / AES_BLOCK_SIZE),
0a7de745
A
986 vars->buffer + vars->bufferHalf + encryptStart,
987 &cryptvars->ctx.encrypt);
988
989 clock_get_uptime(&endTime);
990 ADD_ABSOLUTETIME(&vars->cryptTime, &endTime);
991 SUB_ABSOLUTETIME(&vars->cryptTime, &startTime);
992 vars->cryptBytes += encryptLen;
993
994 // save initial vector for following encrypts
995 bcopy(vars->buffer + vars->bufferHalf + encryptStart + encryptLen - AES_BLOCK_SIZE,
996 &cryptvars->aes_iv[0],
997 AES_BLOCK_SIZE);
998 }
3e170ce0
A
999#endif /* CRYPTO */
1000
0a7de745
A
1001 err = IOPolledFilePollersIODone(vars->pollers, true);
1002 if (kIOReturnSuccess != err) {
1003 break;
1004 }
3e170ce0 1005
0a7de745
A
1006 if (vars->position & (vars->blockSize - 1)) {
1007 HIBLOG("misaligned file pos %qx\n", vars->position);
1008 }
3e170ce0
A
1009//if (length != vars->bufferSize) HIBLOG("short write of %qx ends@ %qx\n", length, offset + length);
1010
0a7de745
A
1011 err = IOStartPolledIO(vars->pollers, kIOPolledWrite, vars->bufferHalf, offset, length);
1012 if (kIOReturnSuccess != err) {
1013 HIBLOGFROMPANIC("IOPolledFileWrite(0x%p, 0x%p, %llu, 0x%p) : IOStartPolledIO(0x%p, kIOPolledWrite, %llu, 0x%llx, %d) returned 0x%x\n",
1014 vars, bytes, (uint64_t) original_size, cryptvars, vars->pollers, (uint64_t) vars->bufferHalf, offset, length, err);
1015 break;
1016 }
1017 vars->pollers->io = true;
1018
1019 vars->extentRemaining -= vars->bufferOffset;
1020 if (!vars->extentRemaining) {
1021 vars->currentExtent++;
1022 vars->extentRemaining = vars->currentExtent->length;
1023 vars->extentPosition = vars->position;
1024 }
1025
1026 vars->bufferHalf = vars->bufferHalf ? 0 : vars->bufferSize;
1027 vars->bufferOffset = 0;
1028 if (vars->bufferSize <= vars->extentRemaining) {
1029 vars->bufferLimit = vars->bufferSize;
1030 } else {
f427ee49 1031 vars->bufferLimit = ((uint32_t) vars->extentRemaining);
0a7de745
A
1032 }
1033
1034 if (!vars->extentRemaining) {
1035 err = kIOReturnOverrun;
1036 break;
1037 }
1038
1039 flush = false;
1040 }
1041 }while (size);
1042
1043 return err;
3e170ce0
A
1044}
1045
1046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1047
5ba3f43e
A
1048IOReturn
1049IOPolledFileFlush(IOPolledFileIOVars * vars)
1050{
0a7de745
A
1051 // Only supported by the underlying polled mode driver on embedded currently (expect kIOReturnUnsupported on other platforms)
1052 IOReturn err = kIOReturnSuccess;
1053
1054 err = IOPolledFilePollersIODone(vars->pollers, true);
1055 if (kIOReturnSuccess != err) {
1056 return err;
1057 }
1058
1059 err = IOStartPolledIO(vars->pollers, kIOPolledFlush, 0, 0, 0);
1060 if (kIOReturnSuccess != err) {
1061 HIBLOGFROMPANIC("IOPolledFileFlush(0x%p) : IOStartPolledIO(0x%p, kIOPolledFlush, 0, 0, 0) returned 0x%x\n",
1062 vars, vars->pollers, err);
1063 return err;
1064 }
1065 vars->pollers->io = true;
1066
1067 return err;
5ba3f43e
A
1068}
1069
1070/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1071
3e170ce0
A
1072IOReturn
1073IOPolledFileRead(IOPolledFileIOVars * vars,
0a7de745
A
1074 uint8_t * bytes, IOByteCount size,
1075 IOPolledFileCryptVars * cryptvars)
3e170ce0 1076{
0a7de745
A
1077 IOReturn err = kIOReturnSuccess;
1078 IOByteCount copy;
3e170ce0
A
1079
1080// bytesWritten += size;
1081
0a7de745
A
1082 do{
1083 copy = vars->bufferLimit - vars->bufferOffset;
1084 if (copy > size) {
1085 copy = size;
1086 }
3e170ce0 1087
0a7de745 1088 if (bytes) {
5c9f4661 1089#if KASAN
0a7de745 1090 __nosan_bcopy(vars->buffer + vars->bufferHalf + vars->bufferOffset, bytes, copy);
5c9f4661 1091#else
0a7de745 1092 bcopy(vars->buffer + vars->bufferHalf + vars->bufferOffset, bytes, copy);
5c9f4661 1093#endif
0a7de745
A
1094 bytes += copy;
1095 }
1096 size -= copy;
1097 vars->bufferOffset += copy;
3e170ce0
A
1098// vars->position += copy;
1099
0a7de745
A
1100 if ((vars->bufferOffset == vars->bufferLimit) && (vars->position < vars->readEnd)) {
1101 if (!vars->pollers->io) {
cb323159 1102 cryptvars = NULL;
0a7de745
A
1103 }
1104 err = IOPolledFilePollersIODone(vars->pollers, true);
1105 if (kIOReturnSuccess != err) {
1106 break;
1107 }
1108
1109 if (vars->position & (vars->blockSize - 1)) {
1110 HIBLOG("misaligned file pos %qx\n", vars->position);
1111 }
1112
1113 vars->position += vars->lastRead;
1114 vars->extentRemaining -= vars->lastRead;
1115 vars->bufferLimit = vars->lastRead;
1116
1117 if (!vars->extentRemaining) {
1118 vars->currentExtent++;
1119 vars->extentRemaining = vars->currentExtent->length;
1120 vars->extentPosition = vars->position;
1121 if (!vars->extentRemaining) {
1122 err = kIOReturnOverrun;
1123 break;
1124 }
1125 }
1126
f427ee49
A
1127 uint32_t length;
1128 uint32_t lastReadLength = vars->lastRead;
0a7de745
A
1129 uint64_t offset = (vars->position
1130 - vars->extentPosition + vars->currentExtent->start);
1131 if (vars->extentRemaining <= vars->bufferSize) {
f427ee49 1132 length = ((uint32_t) vars->extentRemaining);
0a7de745
A
1133 } else {
1134 length = vars->bufferSize;
1135 }
1136 if ((length + vars->position) > vars->readEnd) {
f427ee49 1137 length = ((uint32_t) (vars->readEnd - vars->position));
0a7de745
A
1138 }
1139
1140 vars->lastRead = length;
1141 if (length) {
3e170ce0 1142//if (length != vars->bufferSize) HIBLOG("short read of %qx ends@ %qx\n", length, offset + length);
0a7de745
A
1143 err = IOStartPolledIO(vars->pollers, kIOPolledRead, vars->bufferHalf, offset, length);
1144 if (kIOReturnSuccess != err) {
1145 break;
1146 }
1147 vars->pollers->io = true;
1148 }
3e170ce0 1149
0a7de745
A
1150 vars->bufferHalf = vars->bufferHalf ? 0 : vars->bufferSize;
1151 vars->bufferOffset = 0;
3e170ce0
A
1152
1153#if CRYPTO
0a7de745
A
1154 if (cryptvars) {
1155 uint8_t thisVector[AES_BLOCK_SIZE];
1156 AbsoluteTime startTime, endTime;
1157
1158 // save initial vector for following decrypts
1159 bcopy(&cryptvars->aes_iv[0], &thisVector[0], AES_BLOCK_SIZE);
1160 bcopy(vars->buffer + vars->bufferHalf + lastReadLength - AES_BLOCK_SIZE,
1161 &cryptvars->aes_iv[0], AES_BLOCK_SIZE);
1162
1163 // decrypt the buffer
1164 clock_get_uptime(&startTime);
1165
f427ee49 1166 assert(lastReadLength <= UINT_MAX);
0a7de745
A
1167 aes_decrypt_cbc(vars->buffer + vars->bufferHalf,
1168 &thisVector[0],
f427ee49 1169 (unsigned int) (lastReadLength / AES_BLOCK_SIZE),
0a7de745
A
1170 vars->buffer + vars->bufferHalf,
1171 &cryptvars->ctx.decrypt);
1172
1173 clock_get_uptime(&endTime);
1174 ADD_ABSOLUTETIME(&vars->cryptTime, &endTime);
1175 SUB_ABSOLUTETIME(&vars->cryptTime, &startTime);
1176 vars->cryptBytes += lastReadLength;
1177 }
3e170ce0 1178#endif /* CRYPTO */
0a7de745
A
1179 }
1180 }while (size);
3e170ce0 1181
0a7de745 1182 return err;
3e170ce0
A
1183}
1184
1185/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */