]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOHibernateIO.cpp
034ca65ca33b9a786521a81624763a0629dc1d3e
[apple/xnu.git] / iokit / Kernel / IOHibernateIO.cpp
1 /*
2 * Copyright (c) 2004-2008 Apple Computer, 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
30 /*
31
32 Sleep:
33
34 - PMRootDomain calls IOHibernateSystemSleep() before system sleep
35 (devices awake, normal execution context)
36 - IOHibernateSystemSleep opens the hibernation file (or partition) at the bsd level,
37 grabs its extents and searches for a polling driver willing to work with that IOMedia.
38 The BSD code makes an ioctl to the storage driver to get the partition base offset to
39 the disk, and other ioctls to get the transfer constraints
40 If successful, the file is written to make sure its initially not bootable (in case of
41 later failure) and nvram set to point to the first block of the file. (Has to be done
42 here so blocking is possible in nvram support).
43 hibernate_setup() in osfmk is called to allocate page bitmaps for all dram, and
44 page out any pages it wants to (currently zero, but probably some percentage of memory).
45 Its assumed just allocating pages will cause the VM system to naturally select the best
46 pages for eviction. It also copies processor flags needed for the restore path and sets
47 a flag in the boot processor proc info.
48 gIOHibernateState = kIOHibernateStateHibernating.
49 - Regular sleep progresses - some drivers may inspect the root domain property
50 kIOHibernateStateKey to modify behavior. The platform driver saves state to memory
51 as usual but leaves motherboard I/O on.
52 - Eventually the platform calls ml_ppc_sleep() in the shutdown context on the last cpu,
53 at which point memory is ready to be saved. mapping_hibernate_flush() is called to get
54 all ppc RC bits out of the hash table and caches into the mapping structures.
55 - hibernate_write_image() is called (still in shutdown context, no blocking or preemption).
56 hibernate_page_list_setall() is called to get a bitmap of dram pages that need to be saved.
57 All pages are assumed to be saved (as part of the wired image) unless explicitly subtracted
58 by hibernate_page_list_setall(), avoiding having to find arch dependent low level bits.
59 The image header and block list are written. The header includes the second file extent so
60 only the header block is needed to read the file, regardless of filesystem.
61 The kernel segment "__HIB" is written uncompressed to the image. This segment of code and data
62 (only) is used to decompress the image during wake/boot.
63 Some additional pages are removed from the bitmaps - the buffers used for hibernation.
64 The bitmaps are written to the image.
65 More areas are removed from the bitmaps (after they have been written to the image) - the
66 segment "__HIB" pages and interrupt stack.
67 Each wired page is compressed and written and then each non-wired page. Compression and
68 disk writes are in parallel.
69 The image header is written to the start of the file and the polling driver closed.
70 The machine powers down (or sleeps).
71
72 Boot/Wake:
73
74 - BootX sees the boot-image nvram variable containing the device and block number of the image,
75 reads the header and if the signature is correct proceeds. The boot-image variable is cleared.
76 - BootX reads the portion of the image used for wired pages, to memory. Its assumed this will fit
77 in the OF memory environment, and the image is decrypted. There is no decompression in BootX,
78 that is in the kernel's __HIB section.
79 - BootX copies the "__HIB" section to its correct position in memory, quiesces and calls its entry
80 hibernate_kernel_entrypoint(), passing the location of the image in memory. Translation is off,
81 only code & data in that section is safe to call since all the other wired pages are still
82 compressed in the image.
83 - hibernate_kernel_entrypoint() removes pages occupied by the raw image from the page bitmaps.
84 It uses the bitmaps to work out which pages can be uncompressed from the image to their final
85 location directly, and copies those that can't to interim free pages. When the image has been
86 completed, the copies are uncompressed, overwriting the wired image pages.
87 hibernate_restore_phys_page() (in osfmk since its arch dependent, but part of the "__HIB" section)
88 is used to get pages into place for 64bit.
89 - the reset vector is called (at least on ppc), the kernel proceeds on a normal wake, with some
90 changes conditional on the per proc flag - before VM is turned on the boot cpu, all mappings
91 are removed from the software strutures, and the hash table is reinitialized.
92 - After the platform CPU init code is called, hibernate_machine_init() is called to restore the rest
93 of memory, using the polled mode driver, before other threads can run or any devices are turned on.
94 This reduces the memory usage for BootX and allows decompression in parallel with disk reads,
95 for the remaining non wired pages.
96 - The polling driver is closed down and regular wake proceeds. When the kernel calls iokit to wake
97 (normal execution context) hibernate_teardown() in osmfk is called to release any memory, the file
98 is closed via bsd.
99
100 Polled Mode I/O:
101
102 IOHibernateSystemSleep() finds a polled mode interface to the ATA controller via a property in the
103 registry, specifying an object of calls IOPolledInterface.
104
105 Before the system goes to sleep it searches from the IOMedia object (could be a filesystem or
106 partition) that the image is going to live, looking for polled interface properties. If it finds
107 one the IOMedia object is passed to a "probe" call for the interface to accept or reject. All the
108 interfaces found are kept in an ordered list.
109
110 There is an Open/Close pair of calls made to each of the interfaces at various stages since there are
111 few different contexts things happen in:
112
113 - there is an Open/Close (Preflight) made before any part of the system has slept (I/O is all
114 up and running) and after wake - this is safe to allocate memory and do anything. The device
115 ignores sleep requests from that point since its a waste of time if it goes to sleep and
116 immediately wakes back up for the image write.
117
118 - there is an Open/Close (BeforeSleep) pair made around the image write operations that happen
119 immediately before sleep. These can't block or allocate memory - the I/O system is asleep apart
120 from the low level bits (motherboard I/O etc). There is only one thread running. The close can be
121 used to flush and set the disk to sleep.
122
123 - there is an Open/Close (AfterSleep) pair made around the image read operations that happen
124 immediately after sleep. These can't block or allocate memory. This is happening after the platform
125 expert has woken the low level bits of the system, but most of the I/O system has not. There is only
126 one thread running.
127
128 For the actual I/O, all the ops are with respect to a single IOMemoryDescriptor that was passed
129 (prepared) to the Preflight Open() call. There is a read/write op, buffer offset to the IOMD for
130 the data, an offset to the disk and length (block aligned 64 bit numbers), and completion callback.
131 Each I/O is async but only one is ever outstanding. The polled interface has a checkForWork call
132 that is called for the hardware to check for events, and complete the I/O via the callback.
133 The hibernate path uses the same transfer constraints the regular cluster I/O path in BSD uses
134 to restrict I/O ops.
135 */
136
137 #include <sys/systm.h>
138
139 #include <IOKit/IOWorkLoop.h>
140 #include <IOKit/IOCommandGate.h>
141 #include <IOKit/IOTimerEventSource.h>
142 #include <IOKit/IOPlatformExpert.h>
143 #include <IOKit/IOKitDebug.h>
144 #include <IOKit/IOTimeStamp.h>
145 #include <IOKit/pwr_mgt/RootDomain.h>
146 #include <IOKit/pwr_mgt/IOPMPrivate.h>
147 #include <IOKit/IOMessage.h>
148 #include <IOKit/IODeviceTreeSupport.h>
149 #include <IOKit/IOBSD.h>
150 #include "RootDomainUserClient.h"
151 #include <IOKit/pwr_mgt/IOPowerConnection.h>
152 #include "IOPMPowerStateQueue.h"
153 #include <IOKit/IOBufferMemoryDescriptor.h>
154 #include <IOKit/AppleKeyStoreInterface.h>
155 #include <libkern/crypto/aes.h>
156
157 #include <sys/uio.h>
158 #include <sys/conf.h>
159 #include <sys/stat.h>
160 #include <sys/fcntl.h> // (FWRITE, ...)
161 #include <sys/sysctl.h>
162 #include <sys/kdebug.h>
163
164 #include <IOKit/IOHibernatePrivate.h>
165 #include <IOKit/IOPolledInterface.h>
166 #include <IOKit/IONVRAM.h>
167 #include "IOHibernateInternal.h"
168 #include <libkern/WKdm.h>
169 #include "IOKitKernelInternal.h"
170 #include <pexpert/device_tree.h>
171
172 #include <machine/pal_routines.h>
173 #include <machine/pal_hibernate.h>
174
175 extern "C" addr64_t kvtophys(vm_offset_t va);
176 extern "C" ppnum_t pmap_find_phys(pmap_t pmap, addr64_t va);
177
178 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
179
180 extern unsigned int save_kdebug_enable;
181 extern uint32_t gIOHibernateState;
182 uint32_t gIOHibernateMode;
183 static char gIOHibernateBootSignature[256+1];
184 static char gIOHibernateFilename[MAXPATHLEN+1];
185 static uint32_t gIOHibernateFreeRatio = 0; // free page target (percent)
186 uint32_t gIOHibernateFreeTime = 0*1000; // max time to spend freeing pages (ms)
187 static uint64_t gIOHibernateCompression = 0x80; // default compression 50%
188
189 static IODTNVRAM * gIOOptionsEntry;
190 static IORegistryEntry * gIOChosenEntry;
191 #if defined(__i386__) || defined(__x86_64__)
192 static const OSSymbol * gIOCreateEFIDevicePathSymbol;
193 static const OSSymbol * gIOHibernateRTCVariablesKey;
194 static const OSSymbol * gIOHibernateBoot0082Key;
195 static const OSSymbol * gIOHibernateBootNextKey;
196 static OSData * gIOHibernateBoot0082Data;
197 static OSData * gIOHibernateBootNextData;
198 static OSObject * gIOHibernateBootNextSave;
199 #endif
200
201 static IOLock * gFSLock;
202 static uint32_t gFSState;
203 static IOPolledFileIOVars gFileVars;
204 static IOHibernateVars gIOHibernateVars;
205 static struct kern_direct_file_io_ref_t * gIOHibernateFileRef;
206 static hibernate_cryptvars_t gIOHibernateCryptWakeContext;
207 static hibernate_graphics_t _hibernateGraphics;
208 static hibernate_graphics_t * gIOHibernateGraphicsInfo = &_hibernateGraphics;
209
210 enum
211 {
212 kFSIdle = 0,
213 kFSOpening = 2,
214 kFSOpened = 3,
215 kFSTimedOut = 4,
216 };
217
218 static IOReturn IOHibernateDone(IOHibernateVars * vars);
219
220 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
221
222 enum { kXPRamAudioVolume = 8 };
223 enum { kDefaultIOSize = 128 * 1024 };
224 enum { kVideoMapSize = 32 * 1024 * 1024 };
225
226 #ifndef kIOMediaPreferredBlockSizeKey
227 #define kIOMediaPreferredBlockSizeKey "Preferred Block Size"
228 #endif
229
230 #ifndef kIOBootPathKey
231 #define kIOBootPathKey "bootpath"
232 #endif
233 #ifndef kIOSelectedBootDeviceKey
234 #define kIOSelectedBootDeviceKey "boot-device"
235 #endif
236
237
238 enum { kIOHibernateMinPollersNeeded = 2 };
239
240 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
241
242 // copy from phys addr to MD
243
244 static IOReturn
245 IOMemoryDescriptorWriteFromPhysical(IOMemoryDescriptor * md,
246 IOByteCount offset, addr64_t bytes, IOByteCount length)
247 {
248 addr64_t srcAddr = bytes;
249 IOByteCount remaining;
250
251 remaining = length = min(length, md->getLength() - offset);
252 while (remaining) { // (process another target segment?)
253 addr64_t dstAddr64;
254 IOByteCount dstLen;
255
256 dstAddr64 = md->getPhysicalSegment(offset, &dstLen, kIOMemoryMapperNone);
257 if (!dstAddr64)
258 break;
259
260 // Clip segment length to remaining
261 if (dstLen > remaining)
262 dstLen = remaining;
263
264 #if 1
265 bcopy_phys(srcAddr, dstAddr64, dstLen);
266 #else
267 copypv(srcAddr, dstAddr64, dstLen,
268 cppvPsnk | cppvFsnk | cppvNoRefSrc | cppvNoModSnk | cppvKmap);
269 #endif
270 srcAddr += dstLen;
271 offset += dstLen;
272 remaining -= dstLen;
273 }
274
275 assert(!remaining);
276
277 return remaining ? kIOReturnUnderrun : kIOReturnSuccess;
278 }
279
280 // copy from MD to phys addr
281
282 static IOReturn
283 IOMemoryDescriptorReadToPhysical(IOMemoryDescriptor * md,
284 IOByteCount offset, addr64_t bytes, IOByteCount length)
285 {
286 addr64_t dstAddr = bytes;
287 IOByteCount remaining;
288
289 remaining = length = min(length, md->getLength() - offset);
290 while (remaining) { // (process another target segment?)
291 addr64_t srcAddr64;
292 IOByteCount dstLen;
293
294 srcAddr64 = md->getPhysicalSegment(offset, &dstLen, kIOMemoryMapperNone);
295 if (!srcAddr64)
296 break;
297
298 // Clip segment length to remaining
299 if (dstLen > remaining)
300 dstLen = remaining;
301
302 #if 1
303 bcopy_phys(srcAddr64, dstAddr, dstLen);
304 #else
305 copypv(srcAddr, dstAddr64, dstLen,
306 cppvPsnk | cppvFsnk | cppvNoRefSrc | cppvNoModSnk | cppvKmap);
307 #endif
308 dstAddr += dstLen;
309 offset += dstLen;
310 remaining -= dstLen;
311 }
312
313 assert(!remaining);
314
315 return remaining ? kIOReturnUnderrun : kIOReturnSuccess;
316 }
317
318 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
319
320 void
321 hibernate_set_page_state(hibernate_page_list_t * page_list, hibernate_page_list_t * page_list_wired,
322 vm_offset_t ppnum, vm_offset_t count, uint32_t kind)
323 {
324 count += ppnum;
325 switch (kind)
326 {
327 case kIOHibernatePageStateUnwiredSave:
328 // unwired save
329 for (; ppnum < count; ppnum++)
330 {
331 hibernate_page_bitset(page_list, FALSE, ppnum);
332 hibernate_page_bitset(page_list_wired, TRUE, ppnum);
333 }
334 break;
335 case kIOHibernatePageStateWiredSave:
336 // wired save
337 for (; ppnum < count; ppnum++)
338 {
339 hibernate_page_bitset(page_list, FALSE, ppnum);
340 hibernate_page_bitset(page_list_wired, FALSE, ppnum);
341 }
342 break;
343 case kIOHibernatePageStateFree:
344 // free page
345 for (; ppnum < count; ppnum++)
346 {
347 hibernate_page_bitset(page_list, TRUE, ppnum);
348 hibernate_page_bitset(page_list_wired, TRUE, ppnum);
349 }
350 break;
351 default:
352 panic("hibernate_set_page_state");
353 }
354 }
355
356 static vm_offset_t
357 hibernate_page_list_iterate(hibernate_page_list_t * list, vm_offset_t * pPage)
358 {
359 uint32_t page = *pPage;
360 uint32_t count;
361 hibernate_bitmap_t * bitmap;
362
363 while ((bitmap = hibernate_page_bitmap_pin(list, &page)))
364 {
365 count = hibernate_page_bitmap_count(bitmap, TRUE, page);
366 if (!count)
367 break;
368 page += count;
369 if (page <= bitmap->last_page)
370 break;
371 }
372
373 *pPage = page;
374 if (bitmap)
375 count = hibernate_page_bitmap_count(bitmap, FALSE, page);
376 else
377 count = 0;
378
379 return (count);
380 }
381
382 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
383
384 static IOReturn
385 IOHibernatePollerProbe(IOPolledFileIOVars * vars, IOService * target)
386 {
387 IOReturn err = kIOReturnError;
388 int32_t idx;
389 IOPolledInterface * poller;
390
391 for (idx = vars->pollers->getCount() - 1; idx >= 0; idx--)
392 {
393 poller = (IOPolledInterface *) vars->pollers->getObject(idx);
394 err = poller->probe(target);
395 if (err)
396 {
397 HIBLOG("IOPolledInterface::probe[%d] 0x%x\n", idx, err);
398 break;
399 }
400 }
401
402 return (err);
403 }
404
405 static IOReturn
406 IOHibernatePollerOpen(IOPolledFileIOVars * vars, uint32_t state, IOMemoryDescriptor * md)
407 {
408 IOReturn err = kIOReturnError;
409 int32_t idx;
410 IOPolledInterface * poller;
411
412 for (idx = vars->pollers->getCount() - 1; idx >= 0; idx--)
413 {
414 poller = (IOPolledInterface *) vars->pollers->getObject(idx);
415 err = poller->open(state, md);
416 if (err)
417 {
418 HIBLOG("IOPolledInterface::open[%d] 0x%x\n", idx, err);
419 break;
420 }
421 }
422
423 return (err);
424 }
425
426 static IOReturn
427 IOHibernatePollerClose(IOPolledFileIOVars * vars, uint32_t state)
428 {
429 IOReturn err = kIOReturnError;
430 int32_t idx;
431 IOPolledInterface * poller;
432
433 for (idx = 0;
434 (poller = (IOPolledInterface *) vars->pollers->getObject(idx));
435 idx++)
436 {
437 err = poller->close(state);
438 if (err)
439 HIBLOG("IOPolledInterface::close[%d] 0x%x\n", idx, err);
440 }
441
442 return (err);
443 }
444
445 static void
446 IOHibernatePollerIOComplete(void * target,
447 void * parameter,
448 IOReturn status,
449 UInt64 actualByteCount)
450 {
451 IOPolledFileIOVars * vars = (IOPolledFileIOVars *) parameter;
452
453 vars->ioStatus = status;
454 }
455
456 static IOReturn
457 IOHibernatePollerIO(IOPolledFileIOVars * vars,
458 uint32_t operation, uint32_t bufferOffset,
459 uint64_t deviceOffset, uint64_t length)
460 {
461
462 IOReturn err = kIOReturnError;
463 IOPolledInterface * poller;
464 IOPolledCompletion completion;
465
466 completion.target = 0;
467 completion.action = &IOHibernatePollerIOComplete;
468 completion.parameter = vars;
469
470 vars->ioStatus = -1;
471
472 poller = (IOPolledInterface *) vars->pollers->getObject(0);
473 err = poller->startIO(operation, bufferOffset, deviceOffset + vars->block0, length, completion);
474 if (err)
475 HIBLOG("IOPolledInterface::startIO[%d] 0x%x\n", 0, err);
476
477 return (err);
478 }
479
480 static IOReturn
481 IOHibernatePollerIODone(IOPolledFileIOVars * vars, bool abortable)
482 {
483 IOReturn err = kIOReturnSuccess;
484 int32_t idx = 0;
485 IOPolledInterface * poller;
486
487 while (-1 == vars->ioStatus)
488 {
489 for (idx = 0;
490 (poller = (IOPolledInterface *) vars->pollers->getObject(idx));
491 idx++)
492 {
493 IOReturn newErr;
494 newErr = poller->checkForWork();
495 if ((newErr == kIOReturnAborted) && !abortable)
496 newErr = kIOReturnSuccess;
497 if (kIOReturnSuccess == err)
498 err = newErr;
499 }
500 }
501
502 if ((kIOReturnSuccess == err) && abortable && hibernate_should_abort())
503 {
504 err = kIOReturnAborted;
505 HIBLOG("IOPolledInterface::checkForWork sw abort\n");
506 }
507
508 if (err)
509 {
510 HIBLOG("IOPolledInterface::checkForWork[%d] 0x%x\n", idx, err);
511 }
512 else
513 {
514 err = vars->ioStatus;
515 if (kIOReturnSuccess != err)
516 HIBLOG("IOPolledInterface::ioStatus 0x%x\n", err);
517 }
518
519 return (err);
520 }
521
522 IOReturn
523 IOPolledInterface::checkAllForWork(void)
524 {
525 IOReturn err = kIOReturnNotReady;
526 int32_t idx;
527 IOPolledInterface * poller;
528
529 IOHibernateVars * vars = &gIOHibernateVars;
530
531 if (!vars->fileVars || !vars->fileVars->pollers)
532 return (err);
533
534 for (idx = 0;
535 (poller = (IOPolledInterface *) vars->fileVars->pollers->getObject(idx));
536 idx++)
537 {
538 err = poller->checkForWork();
539 if (err)
540 HIBLOG("IOPolledInterface::checkAllForWork[%d] 0x%x\n", idx, err);
541 }
542
543 return (err);
544 }
545
546 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
547
548 struct _OpenFileContext
549 {
550 OSData * extents;
551 uint64_t size;
552 };
553
554 static void
555 file_extent_callback(void * ref, uint64_t start, uint64_t length)
556 {
557 _OpenFileContext * ctx = (_OpenFileContext *) ref;
558 IOPolledFileExtent extent;
559
560 extent.start = start;
561 extent.length = length;
562
563 ctx->extents->appendBytes(&extent, sizeof(extent));
564 ctx->size += length;
565 }
566
567 static IOService *
568 IOCopyMediaForDev(dev_t device)
569 {
570 OSDictionary * matching;
571 OSNumber * num;
572 OSIterator * iter;
573 IOService * result = 0;
574
575 matching = IOService::serviceMatching("IOMedia");
576 if (!matching)
577 return (0);
578 do
579 {
580 num = OSNumber::withNumber(major(device), 32);
581 if (!num)
582 break;
583 matching->setObject(kIOBSDMajorKey, num);
584 num->release();
585 num = OSNumber::withNumber(minor(device), 32);
586 if (!num)
587 break;
588 matching->setObject(kIOBSDMinorKey, num);
589 num->release();
590 if (!num)
591 break;
592 iter = IOService::getMatchingServices(matching);
593 if (iter)
594 {
595 result = (IOService *) iter->getNextObject();
596 result->retain();
597 iter->release();
598 }
599 }
600 while (false);
601 matching->release();
602
603 return (result);
604 }
605
606 IOReturn
607 IOPolledFileOpen( const char * filename, uint64_t setFileSize,
608 IOBufferMemoryDescriptor * ioBuffer,
609 IOPolledFileIOVars ** fileVars, OSData ** fileExtents,
610 OSData ** imagePath, uint8_t * volumeCryptKey)
611 {
612 IOReturn err = kIOReturnSuccess;
613 IOPolledFileIOVars * vars;
614 _OpenFileContext ctx;
615 OSData * extentsData;
616 OSNumber * num;
617 IOService * part = 0;
618 OSString * keyUUID = 0;
619 OSString * keyStoreUUID = 0;
620 dev_t block_dev;
621 dev_t hibernate_image_dev;
622 uint64_t maxiobytes;
623 AbsoluteTime startTime, endTime;
624 uint64_t nsec;
625
626 vars = IONew(IOPolledFileIOVars, 1);
627 if (!vars) return (kIOReturnNoMemory);
628 bzero(vars, sizeof(*vars));
629
630 do
631 {
632 HIBLOG("sizeof(IOHibernateImageHeader) == %ld\n", sizeof(IOHibernateImageHeader));
633 if (sizeof(IOHibernateImageHeader) != 512)
634 continue;
635
636 vars->io = false;
637 vars->buffer = (uint8_t *) ioBuffer->getBytesNoCopy();
638 vars->bufferHalf = 0;
639 vars->bufferOffset = 0;
640 vars->bufferSize = ioBuffer->getLength() >> 1;
641
642 extentsData = OSData::withCapacity(32);
643 ctx.extents = extentsData;
644 ctx.size = 0;
645 clock_get_uptime(&startTime);
646 vars->fileRef = kern_open_file_for_direct_io(filename,
647 &file_extent_callback, &ctx,
648 setFileSize,
649 // write file:
650 0, (caddr_t) gIOHibernateCurrentHeader,
651 sizeof(IOHibernateImageHeader),
652 // results
653 &block_dev,
654 &hibernate_image_dev,
655 &vars->block0,
656 &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) err = kIOReturnNoSpace;
668
669 IOLockLock(gFSLock);
670 if (kFSOpening != gFSState) err = kIOReturnTimeout;
671 IOLockUnlock(gFSLock);
672
673 HIBLOG("kern_open_file_for_direct_io(%d) took %qd ms\n", err, nsec / 1000000ULL);
674 if (kIOReturnSuccess != err) break;
675
676 if (kIOHibernateModeSSDInvert & gIOHibernateMode)
677 vars->flags ^= kIOHibernateOptionSSD;
678
679 HIBLOG("Opened file %s, size %qd, partition base 0x%qx, maxio %qx ssd %d\n", filename, ctx.size,
680 vars->block0, maxiobytes, kIOHibernateOptionSSD & vars->flags);
681 if (ctx.size < 1*1024*1024) // check against image size estimate!
682 {
683 err = kIOReturnNoSpace;
684 break;
685 }
686
687 vars->fileSize = ctx.size;
688 if (maxiobytes < vars->bufferSize)
689 vars->bufferSize = maxiobytes;
690
691 vars->extentMap = (IOPolledFileExtent *) extentsData->getBytesNoCopy();
692
693 part = IOCopyMediaForDev(block_dev);
694 if (!part)
695 break;
696
697 err = part->callPlatformFunction(PLATFORM_FUNCTION_GET_MEDIA_ENCRYPTION_KEY_UUID, false,
698 (void *) &keyUUID, (void *) &keyStoreUUID, NULL, NULL);
699 if ((kIOReturnSuccess == err) && keyUUID && keyStoreUUID)
700 {
701 // IOLog("got volume key %s\n", keyStoreUUID->getCStringNoCopy());
702 uuid_t volumeKeyUUID;
703 aks_volume_key_t vek;
704 static IOService * sKeyStore;
705 static const OSSymbol * sAKSGetKey;
706
707 if (!sAKSGetKey)
708 sAKSGetKey = OSSymbol::withCStringNoCopy(AKS_PLATFORM_FUNCTION_GETKEY);
709 if (!sKeyStore)
710 sKeyStore = (IOService *) IORegistryEntry::fromPath(AKS_SERVICE_PATH, gIOServicePlane);
711 if (sKeyStore)
712 err = uuid_parse(keyStoreUUID->getCStringNoCopy(), volumeKeyUUID);
713 else
714 err = kIOReturnNoResources;
715 if (kIOReturnSuccess == err)
716 err = sKeyStore->callPlatformFunction(sAKSGetKey, true, volumeKeyUUID, &vek, NULL, NULL);
717 if (kIOReturnSuccess != err)
718 IOLog("volume key err 0x%x\n", err);
719 else
720 {
721 size_t bytes = (kIOHibernateAESKeySize / 8);
722 if (vek.key.keybytecount < bytes)
723 bytes = vek.key.keybytecount;
724 bcopy(&vek.key.keybytes[0], volumeCryptKey, bytes);
725 }
726 bzero(&vek, sizeof(vek));
727 }
728 part->release();
729
730 part = IOCopyMediaForDev(hibernate_image_dev);
731 if (!part)
732 break;
733
734 IORegistryEntry * next;
735 IORegistryEntry * child;
736 OSData * data;
737
738 vars->pollers = OSArray::withCapacity(4);
739 if (!vars->pollers)
740 break;
741
742 vars->blockSize = 512;
743 next = part;
744 do
745 {
746 IOPolledInterface * poller;
747 OSObject * obj;
748
749 obj = next->getProperty(kIOPolledInterfaceSupportKey);
750 if (kOSBooleanFalse == obj)
751 {
752 vars->pollers->flushCollection();
753 break;
754 }
755 else if ((poller = OSDynamicCast(IOPolledInterface, obj)))
756 vars->pollers->setObject(poller);
757 if ((num = OSDynamicCast(OSNumber, next->getProperty(kIOMediaPreferredBlockSizeKey))))
758 vars->blockSize = num->unsigned32BitValue();
759 child = next;
760 }
761 while ((next = child->getParentEntry(gIOServicePlane))
762 && child->isParent(next, gIOServicePlane, true));
763
764 HIBLOG("hibernate image major %d, minor %d, blocksize %ld, pollers %d\n",
765 major(hibernate_image_dev), minor(hibernate_image_dev), (long)vars->blockSize, vars->pollers->getCount());
766 if (vars->pollers->getCount() < kIOHibernateMinPollersNeeded)
767 continue;
768
769 err = IOHibernatePollerProbe(vars, (IOService *) part);
770 if (kIOReturnSuccess != err)
771 break;
772
773 err = IOHibernatePollerOpen(vars, kIOPolledPreflightState, ioBuffer);
774 if (kIOReturnSuccess != err)
775 break;
776
777 vars->media = part;
778 next = part;
779 while (next)
780 {
781 next->setProperty(kIOPolledInterfaceActiveKey, kOSBooleanTrue);
782 next = next->getParentEntry(gIOServicePlane);
783 }
784
785 *fileVars = vars;
786 *fileExtents = extentsData;
787
788 // make imagePath
789
790 if ((extentsData->getLength() >= sizeof(IOPolledFileExtent)))
791 {
792 char str2[24 + sizeof(uuid_string_t) + 2];
793
794 #if defined(__i386__) || defined(__x86_64__)
795 if (!gIOCreateEFIDevicePathSymbol)
796 gIOCreateEFIDevicePathSymbol = OSSymbol::withCString("CreateEFIDevicePath");
797
798 if (keyUUID)
799 snprintf(str2, sizeof(str2), "%qx:%s",
800 vars->extentMap[0].start, keyUUID->getCStringNoCopy());
801 else
802 snprintf(str2, sizeof(str2), "%qx", vars->extentMap[0].start);
803
804 err = IOService::getPlatform()->callPlatformFunction(
805 gIOCreateEFIDevicePathSymbol, false,
806 (void *) part, (void *) str2,
807 (void *) (uintptr_t) true, (void *) &data);
808 #else
809 char str1[256];
810 int len = sizeof(str1);
811
812 if (!part->getPath(str1, &len, gIODTPlane))
813 err = kIOReturnNotFound;
814 else
815 {
816 snprintf(str2, sizeof(str2), ",%qx", vars->extentMap[0].start);
817 // (strip the plane name)
818 char * tail = strchr(str1, ':');
819 if (!tail)
820 tail = str1 - 1;
821 data = OSData::withBytes(tail + 1, strlen(tail + 1));
822 data->appendBytes(str2, strlen(str2));
823 }
824 #endif
825 if (kIOReturnSuccess == err)
826 *imagePath = data;
827 else
828 HIBLOG("error 0x%x getting path\n", err);
829 }
830 }
831 while (false);
832
833 if (kIOReturnSuccess != err)
834 {
835 HIBLOG("error 0x%x opening hibernation file\n", err);
836 if (vars->fileRef)
837 {
838 kern_close_file_for_direct_io(vars->fileRef, 0, 0, 0, 0, 0);
839 vars->fileRef = NULL;
840 }
841 }
842
843 if (part)
844 part->release();
845
846 return (err);
847 }
848
849 IOReturn
850 IOPolledFileClose( IOPolledFileIOVars * vars )
851 {
852 if (vars->pollers)
853 {
854 IOHibernatePollerClose(vars, kIOPolledPostflightState);
855 vars->pollers->release();
856 }
857
858 bzero(vars, sizeof(IOPolledFileIOVars));
859
860 return (kIOReturnSuccess);
861 }
862
863 static IOReturn
864 IOPolledFileSeek(IOPolledFileIOVars * vars, uint64_t position)
865 {
866 IOPolledFileExtent * extentMap;
867
868 extentMap = vars->extentMap;
869
870 vars->position = position;
871
872 while (position >= extentMap->length)
873 {
874 position -= extentMap->length;
875 extentMap++;
876 }
877
878 vars->currentExtent = extentMap;
879 vars->extentRemaining = extentMap->length - position;
880 vars->extentPosition = vars->position - position;
881
882 if (vars->bufferSize <= vars->extentRemaining)
883 vars->bufferLimit = vars->bufferSize;
884 else
885 vars->bufferLimit = vars->extentRemaining;
886
887 return (kIOReturnSuccess);
888 }
889
890 static IOReturn
891 IOPolledFileWrite(IOPolledFileIOVars * vars,
892 const uint8_t * bytes, IOByteCount size,
893 hibernate_cryptvars_t * cryptvars)
894 {
895 IOReturn err = kIOReturnSuccess;
896 IOByteCount copy;
897 bool flush = false;
898
899 do
900 {
901 if (!bytes && !size)
902 {
903 // seek to end of block & flush
904 size = vars->position & (vars->blockSize - 1);
905 if (size)
906 size = vars->blockSize - size;
907 flush = true;
908 // use some garbage for the fill
909 bytes = vars->buffer + vars->bufferOffset;
910 }
911
912 copy = vars->bufferLimit - vars->bufferOffset;
913 if (copy > size)
914 copy = size;
915 else
916 flush = true;
917
918 if (bytes)
919 {
920 bcopy(bytes, vars->buffer + vars->bufferHalf + vars->bufferOffset, copy);
921 bytes += copy;
922 }
923 else
924 bzero(vars->buffer + vars->bufferHalf + vars->bufferOffset, copy);
925
926 size -= copy;
927 vars->bufferOffset += copy;
928 vars->position += copy;
929
930 if (flush && vars->bufferOffset)
931 {
932 uint64_t offset = (vars->position - vars->bufferOffset
933 - vars->extentPosition + vars->currentExtent->start);
934 uint32_t length = (vars->bufferOffset);
935
936 #if CRYPTO
937 if (cryptvars && vars->encryptStart
938 && (vars->position > vars->encryptStart)
939 && ((vars->position - length) < vars->encryptEnd))
940 {
941 AbsoluteTime startTime, endTime;
942
943 uint64_t encryptLen, encryptStart;
944 encryptLen = vars->position - vars->encryptStart;
945 if (encryptLen > length)
946 encryptLen = length;
947 encryptStart = length - encryptLen;
948 if (vars->position > vars->encryptEnd)
949 encryptLen -= (vars->position - vars->encryptEnd);
950
951 clock_get_uptime(&startTime);
952
953 // encrypt the buffer
954 aes_encrypt_cbc(vars->buffer + vars->bufferHalf + encryptStart,
955 &cryptvars->aes_iv[0],
956 encryptLen / AES_BLOCK_SIZE,
957 vars->buffer + vars->bufferHalf + encryptStart,
958 &cryptvars->ctx.encrypt);
959
960 clock_get_uptime(&endTime);
961 ADD_ABSOLUTETIME(&vars->cryptTime, &endTime);
962 SUB_ABSOLUTETIME(&vars->cryptTime, &startTime);
963 vars->cryptBytes += encryptLen;
964
965 // save initial vector for following encrypts
966 bcopy(vars->buffer + vars->bufferHalf + encryptStart + encryptLen - AES_BLOCK_SIZE,
967 &cryptvars->aes_iv[0],
968 AES_BLOCK_SIZE);
969 }
970 #endif /* CRYPTO */
971
972 if (vars->io)
973 {
974 err = IOHibernatePollerIODone(vars, true);
975 if (kIOReturnSuccess != err)
976 break;
977 }
978
979 if (vars->position & (vars->blockSize - 1)) HIBLOG("misaligned file pos %qx\n", vars->position);
980 //if (length != vars->bufferSize) HIBLOG("short write of %qx ends@ %qx\n", length, offset + length);
981
982 err = IOHibernatePollerIO(vars, kIOPolledWrite, vars->bufferHalf, offset, length);
983 if (kIOReturnSuccess != err)
984 break;
985 vars->io = true;
986
987 vars->extentRemaining -= vars->bufferOffset;
988 if (!vars->extentRemaining)
989 {
990 vars->currentExtent++;
991 vars->extentRemaining = vars->currentExtent->length;
992 vars->extentPosition = vars->position;
993 if (!vars->extentRemaining)
994 {
995 err = kIOReturnOverrun;
996 break;
997 }
998 }
999
1000 vars->bufferHalf = vars->bufferHalf ? 0 : vars->bufferSize;
1001 vars->bufferOffset = 0;
1002 if (vars->bufferSize <= vars->extentRemaining)
1003 vars->bufferLimit = vars->bufferSize;
1004 else
1005 vars->bufferLimit = vars->extentRemaining;
1006
1007 flush = false;
1008 }
1009 }
1010 while (size);
1011
1012 return (err);
1013 }
1014
1015 static IOReturn
1016 IOPolledFileRead(IOPolledFileIOVars * vars,
1017 uint8_t * bytes, IOByteCount size,
1018 hibernate_cryptvars_t * cryptvars)
1019 {
1020 IOReturn err = kIOReturnSuccess;
1021 IOByteCount copy;
1022
1023 // bytesWritten += size;
1024
1025 do
1026 {
1027 copy = vars->bufferLimit - vars->bufferOffset;
1028 if (copy > size)
1029 copy = size;
1030
1031 if (bytes)
1032 {
1033 bcopy(vars->buffer + vars->bufferHalf + vars->bufferOffset, bytes, copy);
1034 bytes += copy;
1035 }
1036 size -= copy;
1037 vars->bufferOffset += copy;
1038 // vars->position += copy;
1039
1040 if ((vars->bufferOffset == vars->bufferLimit) && (vars->position < vars->readEnd))
1041 {
1042 if (vars->io)
1043 {
1044 err = IOHibernatePollerIODone(vars, false);
1045 if (kIOReturnSuccess != err)
1046 break;
1047 }
1048 else
1049 cryptvars = 0;
1050
1051 if (vars->position & (vars->blockSize - 1)) HIBLOG("misaligned file pos %qx\n", vars->position);
1052
1053 vars->position += vars->lastRead;
1054 vars->extentRemaining -= vars->lastRead;
1055 vars->bufferLimit = vars->lastRead;
1056
1057 if (!vars->extentRemaining)
1058 {
1059 vars->currentExtent++;
1060 vars->extentRemaining = vars->currentExtent->length;
1061 vars->extentPosition = vars->position;
1062 if (!vars->extentRemaining)
1063 {
1064 err = kIOReturnOverrun;
1065 break;
1066 }
1067 }
1068
1069 uint64_t length;
1070 uint64_t lastReadLength = vars->lastRead;
1071 uint64_t offset = (vars->position
1072 - vars->extentPosition + vars->currentExtent->start);
1073 if (vars->extentRemaining <= vars->bufferSize)
1074 length = vars->extentRemaining;
1075 else
1076 length = vars->bufferSize;
1077 if ((length + vars->position) > vars->readEnd)
1078 length = vars->readEnd - vars->position;
1079
1080 vars->lastRead = length;
1081 if (length)
1082 {
1083 //if (length != vars->bufferSize) HIBLOG("short read of %qx ends@ %qx\n", length, offset + length);
1084 err = IOHibernatePollerIO(vars, kIOPolledRead, vars->bufferHalf, offset, length);
1085 if (kIOReturnSuccess != err)
1086 break;
1087 vars->io = true;
1088 }
1089
1090 vars->bufferHalf = vars->bufferHalf ? 0 : vars->bufferSize;
1091 vars->bufferOffset = 0;
1092
1093 #if CRYPTO
1094 if (cryptvars)
1095 {
1096 uint8_t thisVector[AES_BLOCK_SIZE];
1097 AbsoluteTime startTime, endTime;
1098
1099 // save initial vector for following decrypts
1100 bcopy(&cryptvars->aes_iv[0], &thisVector[0], AES_BLOCK_SIZE);
1101 bcopy(vars->buffer + vars->bufferHalf + lastReadLength - AES_BLOCK_SIZE,
1102 &cryptvars->aes_iv[0], AES_BLOCK_SIZE);
1103
1104 // decrypt the buffer
1105 clock_get_uptime(&startTime);
1106
1107 aes_decrypt_cbc(vars->buffer + vars->bufferHalf,
1108 &thisVector[0],
1109 lastReadLength / AES_BLOCK_SIZE,
1110 vars->buffer + vars->bufferHalf,
1111 &cryptvars->ctx.decrypt);
1112
1113 clock_get_uptime(&endTime);
1114 ADD_ABSOLUTETIME(&vars->cryptTime, &endTime);
1115 SUB_ABSOLUTETIME(&vars->cryptTime, &startTime);
1116 vars->cryptBytes += lastReadLength;
1117 }
1118 #endif /* CRYPTO */
1119 }
1120 }
1121 while (size);
1122
1123 return (err);
1124 }
1125
1126 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1127
1128 IOReturn
1129 IOHibernateSystemSleep(void)
1130 {
1131 IOReturn err;
1132 OSData * data;
1133 OSObject * obj;
1134 OSString * str;
1135 OSNumber * num;
1136 bool dsSSD;
1137 IOHibernateVars * vars;
1138
1139 gIOHibernateState = kIOHibernateStateInactive;
1140
1141 if (!gIOChosenEntry)
1142 gIOChosenEntry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
1143
1144 gIOHibernateDebugFlags = 0;
1145 if (kIOLogHibernate & gIOKitDebug)
1146 gIOHibernateDebugFlags |= kIOHibernateDebugRestoreLogs;
1147
1148 if (IOService::getPMRootDomain()->getHibernateSettings(
1149 &gIOHibernateMode, &gIOHibernateFreeRatio, &gIOHibernateFreeTime))
1150 {
1151 if (kIOHibernateModeSleep & gIOHibernateMode)
1152 // default to discard clean for safe sleep
1153 gIOHibernateMode ^= (kIOHibernateModeDiscardCleanInactive
1154 | kIOHibernateModeDiscardCleanActive);
1155 }
1156
1157 if ((obj = IOService::getPMRootDomain()->copyProperty(kIOHibernateFileKey)))
1158 {
1159 if ((str = OSDynamicCast(OSString, obj)))
1160 strlcpy(gIOHibernateFilename, str->getCStringNoCopy(),
1161 sizeof(gIOHibernateFilename));
1162 obj->release();
1163 }
1164
1165 if (!gIOHibernateMode || !gIOHibernateFilename[0])
1166 return (kIOReturnUnsupported);
1167
1168 HIBLOG("hibernate image path: %s\n", gIOHibernateFilename);
1169
1170 vars = IONew(IOHibernateVars, 1);
1171 if (!vars) return (kIOReturnNoMemory);
1172 bzero(vars, sizeof(*vars));
1173
1174 IOLockLock(gFSLock);
1175 if (kFSIdle != gFSState)
1176 {
1177 HIBLOG("hibernate file busy\n");
1178 IOLockUnlock(gFSLock);
1179 IODelete(vars, IOHibernateVars, 1);
1180 return (kIOReturnBusy);
1181 }
1182 gFSState = kFSOpening;
1183 IOLockUnlock(gFSLock);
1184
1185 do
1186 {
1187 vars->srcBuffer = IOBufferMemoryDescriptor::withOptions(kIODirectionOutIn,
1188 4 * page_size, page_size);
1189 vars->ioBuffer = IOBufferMemoryDescriptor::withOptions(kIODirectionOutIn,
1190 2 * kDefaultIOSize, page_size);
1191
1192 vars->handoffBuffer = IOBufferMemoryDescriptor::withOptions(kIODirectionOutIn,
1193 ptoa_64(gIOHibernateHandoffPageCount), page_size);
1194
1195 if (!vars->srcBuffer || !vars->ioBuffer || !vars->handoffBuffer)
1196 {
1197 err = kIOReturnNoMemory;
1198 break;
1199 }
1200
1201 if ((obj = IOService::getPMRootDomain()->copyProperty(kIOHibernateFileMinSizeKey)))
1202 {
1203 if ((num = OSDynamicCast(OSNumber, obj))) vars->fileMinSize = num->unsigned64BitValue();
1204 obj->release();
1205 }
1206 if ((obj = IOService::getPMRootDomain()->copyProperty(kIOHibernateFileMaxSizeKey)))
1207 {
1208 if ((num = OSDynamicCast(OSNumber, obj))) vars->fileMaxSize = num->unsigned64BitValue();
1209 obj->release();
1210 }
1211
1212 boolean_t encryptedswap = true;
1213 uint32_t pageCount;
1214 AbsoluteTime startTime, endTime;
1215 uint64_t nsec;
1216
1217 bzero(gIOHibernateCurrentHeader, sizeof(IOHibernateImageHeader));
1218 gIOHibernateCurrentHeader->debugFlags = gIOHibernateDebugFlags;
1219 gIOHibernateCurrentHeader->signature = kIOHibernateHeaderInvalidSignature;
1220
1221 dsSSD = (kOSBooleanTrue == IOService::getPMRootDomain()->getProperty(kIOPMDeepSleepEnabledKey));
1222 clock_get_uptime(&startTime);
1223 err = hibernate_setup(gIOHibernateCurrentHeader,
1224 gIOHibernateFreeRatio, gIOHibernateFreeTime,
1225 dsSSD,
1226 &vars->page_list, &vars->page_list_wired, &vars->page_list_pal);
1227 clock_get_uptime(&endTime);
1228 SUB_ABSOLUTETIME(&endTime, &startTime);
1229 absolutetime_to_nanoseconds(endTime, &nsec);
1230 HIBLOG("hibernate_setup(%d) took %qd ms\n", err, nsec / 1000000ULL);
1231
1232 uint64_t setFileSize = 0;
1233
1234 if (vars->fileMinSize || (kIOHibernateModeFileResize & gIOHibernateMode))
1235 {
1236 hibernate_page_list_setall(vars->page_list,
1237 vars->page_list_wired,
1238 vars->page_list_pal,
1239 true /* preflight */,
1240 &pageCount);
1241 PE_Video consoleInfo;
1242 bzero(&consoleInfo, sizeof(consoleInfo));
1243 IOService::getPlatform()->getConsoleInfo(&consoleInfo);
1244
1245 // estimate: 5% increase in pages compressed
1246 // screen preview 2 images compressed 50%
1247 setFileSize = ((ptoa_64((105 * pageCount) / 100) * gIOHibernateCompression) >> 8)
1248 + vars->page_list->list_size
1249 + (consoleInfo.v_width * consoleInfo.v_height * 4);
1250
1251 HIBLOG("hibernate_page_list_setall preflight pageCount %d est comp %qd setfile %qd min %qd\n",
1252 pageCount, (100ULL * gIOHibernateCompression) >> 8,
1253 setFileSize, vars->fileMinSize);
1254
1255 if (!(kIOHibernateModeFileResize & gIOHibernateMode)
1256 && (setFileSize < vars->fileMinSize))
1257 {
1258 setFileSize = vars->fileMinSize;
1259 }
1260 }
1261
1262 // open & invalidate the image file
1263
1264 err = IOPolledFileOpen(gIOHibernateFilename, setFileSize, vars->ioBuffer,
1265 &vars->fileVars, &vars->fileExtents, &data,
1266 &vars->volumeCryptKey[0]);
1267 if (KERN_SUCCESS != err)
1268 {
1269 HIBLOG("IOPolledFileOpen(%x)\n", err);
1270 break;
1271 }
1272
1273 dsSSD = ((0 != (kIOHibernateOptionSSD & vars->fileVars->flags))
1274 && (kOSBooleanTrue == IOService::getPMRootDomain()->getProperty(kIOPMDeepSleepEnabledKey)));
1275 if (dsSSD)
1276 {
1277 gIOHibernateCurrentHeader->options |=
1278 kIOHibernateOptionSSD
1279 | kIOHibernateOptionColor;
1280
1281 #if defined(__i386__) || defined(__x86_64__)
1282 if (!uuid_is_null(vars->volumeCryptKey) &&
1283 (kOSBooleanTrue != IOService::getPMRootDomain()->getProperty(kIOPMDestroyFVKeyOnStandbyKey)))
1284 {
1285 uintptr_t smcVars[2];
1286 smcVars[0] = sizeof(vars->volumeCryptKey);
1287 smcVars[1] = (uintptr_t)(void *) &gIOHibernateVars.volumeCryptKey[0];
1288
1289 IOService::getPMRootDomain()->setProperty(kIOHibernateSMCVariablesKey, smcVars, sizeof(smcVars));
1290 bzero(smcVars, sizeof(smcVars));
1291 }
1292 #endif
1293 }
1294 else
1295 {
1296 gIOHibernateCurrentHeader->options |= kIOHibernateOptionProgress;
1297 }
1298
1299
1300 if (KERN_SUCCESS != err)
1301 break;
1302
1303 if (encryptedswap || !uuid_is_null(vars->volumeCryptKey))
1304 gIOHibernateMode ^= kIOHibernateModeEncrypt;
1305
1306 if (kIOHibernateOptionProgress & gIOHibernateCurrentHeader->options)
1307 {
1308 vars->videoAllocSize = kVideoMapSize;
1309 if (KERN_SUCCESS != kmem_alloc_pageable(kernel_map, &vars->videoMapping, vars->videoAllocSize))
1310 vars->videoMapping = 0;
1311 }
1312
1313 // generate crypt keys
1314 for (uint32_t i = 0; i < sizeof(vars->wiredCryptKey); i++)
1315 vars->wiredCryptKey[i] = random();
1316 for (uint32_t i = 0; i < sizeof(vars->cryptKey); i++)
1317 vars->cryptKey[i] = random();
1318
1319 // set nvram
1320
1321 IORegistryEntry * regEntry;
1322 if (!gIOOptionsEntry)
1323 {
1324 regEntry = IORegistryEntry::fromPath("/options", gIODTPlane);
1325 gIOOptionsEntry = OSDynamicCast(IODTNVRAM, regEntry);
1326 if (regEntry && !gIOOptionsEntry)
1327 regEntry->release();
1328 }
1329
1330 if (gIOOptionsEntry)
1331 {
1332 const OSSymbol * sym;
1333
1334 sym = OSSymbol::withCStringNoCopy(kIOHibernateBootImageKey);
1335 if (sym)
1336 {
1337 gIOOptionsEntry->setProperty(sym, data);
1338 sym->release();
1339 }
1340 data->release();
1341
1342 #if defined(__i386__) || defined(__x86_64__)
1343 struct AppleRTCHibernateVars
1344 {
1345 uint8_t signature[4];
1346 uint32_t revision;
1347 uint8_t booterSignature[20];
1348 uint8_t wiredCryptKey[16];
1349 };
1350 AppleRTCHibernateVars rtcVars;
1351
1352 rtcVars.signature[0] = 'A';
1353 rtcVars.signature[1] = 'A';
1354 rtcVars.signature[2] = 'P';
1355 rtcVars.signature[3] = 'L';
1356 rtcVars.revision = 1;
1357 bcopy(&vars->wiredCryptKey[0], &rtcVars.wiredCryptKey[0], sizeof(rtcVars.wiredCryptKey));
1358 if (gIOHibernateBootSignature[0])
1359 {
1360 char c;
1361 uint8_t value = 0;
1362 for (uint32_t i = 0;
1363 (c = gIOHibernateBootSignature[i]) && (i < (sizeof(rtcVars.booterSignature) << 1));
1364 i++)
1365 {
1366 if (c >= 'a')
1367 c -= 'a' - 10;
1368 else if (c >= 'A')
1369 c -= 'A' - 10;
1370 else if (c >= '0')
1371 c -= '0';
1372 else
1373 continue;
1374 value = (value << 4) | c;
1375 if (i & 1)
1376 rtcVars.booterSignature[i >> 1] = value;
1377 }
1378 }
1379 data = OSData::withBytes(&rtcVars, sizeof(rtcVars));
1380 if (data)
1381 {
1382 if (!gIOHibernateRTCVariablesKey)
1383 gIOHibernateRTCVariablesKey = OSSymbol::withCStringNoCopy(kIOHibernateRTCVariablesKey);
1384 if (gIOHibernateRTCVariablesKey)
1385 IOService::getPMRootDomain()->setProperty(gIOHibernateRTCVariablesKey, data);
1386
1387 if( gIOOptionsEntry )
1388 {
1389 if( gIOHibernateMode & kIOHibernateModeSwitch )
1390 {
1391 const OSSymbol *sym;
1392 sym = OSSymbol::withCStringNoCopy(kIOHibernateBootSwitchVarsKey);
1393 if( sym )
1394 {
1395 gIOOptionsEntry->setProperty(sym, data); /* intentional insecure backup of rtc boot vars */
1396 sym->release();
1397 }
1398 }
1399 }
1400
1401 data->release();
1402 }
1403 if (gIOChosenEntry)
1404 {
1405 data = OSDynamicCast(OSData, gIOChosenEntry->getProperty(kIOHibernateMachineSignatureKey));
1406 if (data)
1407 gIOHibernateCurrentHeader->machineSignature = *((UInt32 *)data->getBytesNoCopy());
1408 {
1409 // set BootNext
1410
1411 if (!gIOHibernateBoot0082Data)
1412 {
1413 data = OSDynamicCast(OSData, gIOChosenEntry->getProperty("boot-device-path"));
1414 if (data)
1415 {
1416 // AppleNVRAM_EFI_LOAD_OPTION
1417 struct {
1418 uint32_t Attributes;
1419 uint16_t FilePathLength;
1420 uint16_t Desc;
1421 } loadOptionHeader;
1422 loadOptionHeader.Attributes = 1;
1423 loadOptionHeader.FilePathLength = data->getLength();
1424 loadOptionHeader.Desc = 0;
1425 gIOHibernateBoot0082Data = OSData::withCapacity(sizeof(loadOptionHeader) + loadOptionHeader.FilePathLength);
1426 if (gIOHibernateBoot0082Data)
1427 {
1428 gIOHibernateBoot0082Data->appendBytes(&loadOptionHeader, sizeof(loadOptionHeader));
1429 gIOHibernateBoot0082Data->appendBytes(data);
1430 }
1431 }
1432 }
1433 if (!gIOHibernateBoot0082Key)
1434 gIOHibernateBoot0082Key = OSSymbol::withCString("8BE4DF61-93CA-11D2-AA0D-00E098032B8C:Boot0082");
1435 if (!gIOHibernateBootNextKey)
1436 gIOHibernateBootNextKey = OSSymbol::withCString("8BE4DF61-93CA-11D2-AA0D-00E098032B8C:BootNext");
1437 if (!gIOHibernateBootNextData)
1438 {
1439 uint16_t bits = 0x0082;
1440 gIOHibernateBootNextData = OSData::withBytes(&bits, sizeof(bits));
1441 }
1442 if (gIOHibernateBoot0082Key && gIOHibernateBoot0082Data && gIOHibernateBootNextKey && gIOHibernateBootNextData)
1443 {
1444 gIOHibernateBootNextSave = gIOOptionsEntry->copyProperty(gIOHibernateBootNextKey);
1445 gIOOptionsEntry->setProperty(gIOHibernateBoot0082Key, gIOHibernateBoot0082Data);
1446 gIOOptionsEntry->setProperty(gIOHibernateBootNextKey, gIOHibernateBootNextData);
1447 }
1448 }
1449 }
1450 #else /* !i386 && !x86_64 */
1451 if (kIOHibernateModeEncrypt & gIOHibernateMode)
1452 {
1453 data = OSData::withBytes(&vars->wiredCryptKey[0], sizeof(vars->wiredCryptKey));
1454 sym = OSSymbol::withCStringNoCopy(kIOHibernateBootImageKeyKey);
1455 if (sym && data)
1456 gIOOptionsEntry->setProperty(sym, data);
1457 if (sym)
1458 sym->release();
1459 if (data)
1460 data->release();
1461 if (false && gIOHibernateBootSignature[0])
1462 {
1463 data = OSData::withCapacity(16);
1464 sym = OSSymbol::withCStringNoCopy(kIOHibernateBootSignatureKey);
1465 if (sym && data)
1466 {
1467 char c;
1468 uint8_t value = 0;
1469 for (uint32_t i = 0; (c = gIOHibernateBootSignature[i]); i++)
1470 {
1471 if (c >= 'a')
1472 c -= 'a' - 10;
1473 else if (c >= 'A')
1474 c -= 'A' - 10;
1475 else if (c >= '0')
1476 c -= '0';
1477 else
1478 continue;
1479 value = (value << 4) | c;
1480 if (i & 1)
1481 data->appendBytes(&value, sizeof(value));
1482 }
1483 gIOOptionsEntry->setProperty(sym, data);
1484 }
1485 if (sym)
1486 sym->release();
1487 if (data)
1488 data->release();
1489 }
1490 }
1491 if (!vars->haveFastBoot)
1492 {
1493 // set boot volume to zero
1494 IODTPlatformExpert * platform = OSDynamicCast(IODTPlatformExpert, IOService::getPlatform());
1495 if (platform && (kIOReturnSuccess == platform->readXPRAM(kXPRamAudioVolume,
1496 &vars->saveBootAudioVolume, sizeof(vars->saveBootAudioVolume))))
1497 {
1498 uint8_t newVolume;
1499 newVolume = vars->saveBootAudioVolume & 0xf8;
1500 platform->writeXPRAM(kXPRamAudioVolume,
1501 &newVolume, sizeof(newVolume));
1502 }
1503 }
1504 #endif /* !i386 && !x86_64 */
1505 }
1506 // --
1507
1508 }
1509 while (false);
1510
1511 IOLockLock(gFSLock);
1512 if ((kIOReturnSuccess == err) && (kFSOpening == gFSState))
1513 {
1514 gFSState = kFSOpened;
1515 gIOHibernateVars = *vars;
1516 gFileVars = *vars->fileVars;
1517 gIOHibernateVars.fileVars = &gFileVars;
1518 gIOHibernateFileRef = gFileVars.fileRef;
1519 gIOHibernateCurrentHeader->signature = kIOHibernateHeaderSignature;
1520 gIOHibernateState = kIOHibernateStateHibernating;
1521 }
1522 else
1523 {
1524 HIBLOG("hibernate file close due timeout\n");
1525 if (vars->fileVars && vars->fileVars->fileRef) kern_close_file_for_direct_io(vars->fileVars->fileRef, 0, 0, 0, 0, 0);
1526 IOHibernateDone(vars);
1527 gFSState = kFSIdle;
1528 }
1529 IOLockUnlock(gFSLock);
1530
1531 if (vars->fileVars) IODelete(vars->fileVars, IOPolledFileIOVars, 1);
1532 IODelete(vars, IOHibernateVars, 1);
1533
1534 return (err);
1535 }
1536
1537 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1538
1539 DECLARE_IOHIBERNATEPROGRESSALPHA
1540
1541 static void
1542 ProgressInit(hibernate_graphics_t * display, uint8_t * screen, uint8_t * saveunder, uint32_t savelen)
1543 {
1544 uint32_t rowBytes, pixelShift;
1545 uint32_t x, y;
1546 int32_t blob;
1547 uint32_t alpha, in, color, result;
1548 uint8_t * out;
1549 uint32_t saveindex[kIOHibernateProgressCount] = { 0 };
1550
1551 rowBytes = display->rowBytes;
1552 pixelShift = display->depth >> 4;
1553 if (pixelShift < 1) return;
1554
1555 screen += ((display->width
1556 - kIOHibernateProgressCount * (kIOHibernateProgressWidth + kIOHibernateProgressSpacing)) << (pixelShift - 1))
1557 + (display->height - kIOHibernateProgressOriginY - kIOHibernateProgressHeight) * rowBytes;
1558
1559 for (y = 0; y < kIOHibernateProgressHeight; y++)
1560 {
1561 out = screen + y * rowBytes;
1562 for (blob = 0; blob < kIOHibernateProgressCount; blob++)
1563 {
1564 color = blob ? kIOHibernateProgressDarkGray : kIOHibernateProgressMidGray;
1565 for (x = 0; x < kIOHibernateProgressWidth; x++)
1566 {
1567 alpha = gIOHibernateProgressAlpha[y][x];
1568 result = color;
1569 if (alpha)
1570 {
1571 if (0xff != alpha)
1572 {
1573 if (1 == pixelShift)
1574 {
1575 in = *((uint16_t *)out) & 0x1f; // 16
1576 in = (in << 3) | (in >> 2);
1577 }
1578 else
1579 in = *((uint32_t *)out) & 0xff; // 32
1580 saveunder[blob * kIOHibernateProgressSaveUnderSize + saveindex[blob]++] = in;
1581 result = ((255 - alpha) * in + alpha * result + 0xff) >> 8;
1582 }
1583 if (1 == pixelShift)
1584 {
1585 result >>= 3;
1586 *((uint16_t *)out) = (result << 10) | (result << 5) | result; // 16
1587 }
1588 else
1589 *((uint32_t *)out) = (result << 16) | (result << 8) | result; // 32
1590 }
1591 out += (1 << pixelShift);
1592 }
1593 out += (kIOHibernateProgressSpacing << pixelShift);
1594 }
1595 }
1596 }
1597
1598
1599 static void
1600 ProgressUpdate(hibernate_graphics_t * display, uint8_t * screen, int32_t firstBlob, int32_t select)
1601 {
1602 uint32_t rowBytes, pixelShift;
1603 uint32_t x, y;
1604 int32_t blob, lastBlob;
1605 uint32_t alpha, in, color, result;
1606 uint8_t * out;
1607 uint32_t saveindex[kIOHibernateProgressCount] = { 0 };
1608
1609 pixelShift = display->depth >> 4;
1610 if (pixelShift < 1)
1611 return;
1612
1613 rowBytes = display->rowBytes;
1614
1615 screen += ((display->width
1616 - kIOHibernateProgressCount * (kIOHibernateProgressWidth + kIOHibernateProgressSpacing)) << (pixelShift - 1))
1617 + (display->height - kIOHibernateProgressOriginY - kIOHibernateProgressHeight) * rowBytes;
1618
1619 lastBlob = (select < kIOHibernateProgressCount) ? select : (kIOHibernateProgressCount - 1);
1620
1621 screen += (firstBlob * (kIOHibernateProgressWidth + kIOHibernateProgressSpacing)) << pixelShift;
1622
1623 for (y = 0; y < kIOHibernateProgressHeight; y++)
1624 {
1625 out = screen + y * rowBytes;
1626 for (blob = firstBlob; blob <= lastBlob; blob++)
1627 {
1628 color = (blob < select) ? kIOHibernateProgressLightGray : kIOHibernateProgressMidGray;
1629 for (x = 0; x < kIOHibernateProgressWidth; x++)
1630 {
1631 alpha = gIOHibernateProgressAlpha[y][x];
1632 result = color;
1633 if (alpha)
1634 {
1635 if (0xff != alpha)
1636 {
1637 in = display->progressSaveUnder[blob][saveindex[blob]++];
1638 result = ((255 - alpha) * in + alpha * result + 0xff) / 255;
1639 }
1640 if (1 == pixelShift)
1641 {
1642 result >>= 3;
1643 *((uint16_t *)out) = (result << 10) | (result << 5) | result; // 16
1644 }
1645 else
1646 *((uint32_t *)out) = (result << 16) | (result << 8) | result; // 32
1647 }
1648 out += (1 << pixelShift);
1649 }
1650 out += (kIOHibernateProgressSpacing << pixelShift);
1651 }
1652 }
1653 }
1654
1655 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1656
1657 IOReturn
1658 IOHibernateIOKitSleep(void)
1659 {
1660 IOReturn ret = kIOReturnSuccess;
1661 IOLockLock(gFSLock);
1662 if (kFSOpening == gFSState)
1663 {
1664 gFSState = kFSTimedOut;
1665 HIBLOG("hibernate file open timed out\n");
1666 ret = kIOReturnTimeout;
1667 }
1668 IOLockUnlock(gFSLock);
1669 return (ret);
1670 }
1671
1672 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1673
1674 IOReturn
1675 IOHibernateSystemHasSlept(void)
1676 {
1677 IOReturn ret = kIOReturnSuccess;
1678 IOHibernateVars * vars = &gIOHibernateVars;
1679 OSObject * obj = 0;
1680 OSData * data;
1681
1682 IOLockLock(gFSLock);
1683 if ((kFSOpened != gFSState) && gIOHibernateMode)
1684 {
1685 ret = kIOReturnTimeout;
1686 }
1687 IOLockUnlock(gFSLock);
1688 if (kIOReturnSuccess != ret) return (ret);
1689
1690 if (gIOHibernateMode) obj = IOService::getPMRootDomain()->copyProperty(kIOHibernatePreviewBufferKey);
1691 vars->previewBuffer = OSDynamicCast(IOMemoryDescriptor, obj);
1692 if (obj && !vars->previewBuffer)
1693 obj->release();
1694
1695 vars->consoleMapping = NULL;
1696 if (vars->previewBuffer && (kIOReturnSuccess != vars->previewBuffer->prepare()))
1697 {
1698 vars->previewBuffer->release();
1699 vars->previewBuffer = 0;
1700 }
1701
1702 if ((kIOHibernateOptionProgress & gIOHibernateCurrentHeader->options)
1703 && vars->previewBuffer
1704 && (data = OSDynamicCast(OSData,
1705 IOService::getPMRootDomain()->getProperty(kIOHibernatePreviewActiveKey))))
1706 {
1707 UInt32 flags = *((UInt32 *)data->getBytesNoCopy());
1708 HIBPRINT("kIOHibernatePreviewActiveKey %08lx\n", (long)flags);
1709
1710 IOService::getPMRootDomain()->removeProperty(kIOHibernatePreviewActiveKey);
1711
1712 if (kIOHibernatePreviewUpdates & flags)
1713 {
1714 PE_Video consoleInfo;
1715 hibernate_graphics_t * graphicsInfo = gIOHibernateGraphicsInfo;
1716
1717 IOService::getPlatform()->getConsoleInfo(&consoleInfo);
1718
1719 graphicsInfo->width = consoleInfo.v_width;
1720 graphicsInfo->height = consoleInfo.v_height;
1721 graphicsInfo->rowBytes = consoleInfo.v_rowBytes;
1722 graphicsInfo->depth = consoleInfo.v_depth;
1723 vars->consoleMapping = (uint8_t *) consoleInfo.v_baseAddr;
1724
1725 HIBPRINT("video %p %d %d %d\n",
1726 vars->consoleMapping, graphicsInfo->depth,
1727 graphicsInfo->width, graphicsInfo->height);
1728 if (vars->consoleMapping)
1729 ProgressInit(graphicsInfo, vars->consoleMapping,
1730 &graphicsInfo->progressSaveUnder[0][0], sizeof(graphicsInfo->progressSaveUnder));
1731 }
1732 }
1733
1734 if (gIOOptionsEntry)
1735 gIOOptionsEntry->sync();
1736
1737 return (ret);
1738 }
1739
1740 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1741
1742 static DeviceTreeNode *
1743 MergeDeviceTree(DeviceTreeNode * entry, IORegistryEntry * regEntry)
1744 {
1745 DeviceTreeNodeProperty * prop;
1746 DeviceTreeNode * child;
1747 IORegistryEntry * childRegEntry;
1748 const char * nameProp;
1749 unsigned int propLen, idx;
1750
1751 prop = (DeviceTreeNodeProperty *) (entry + 1);
1752 for (idx = 0; idx < entry->nProperties; idx++)
1753 {
1754 if (regEntry && (0 != strcmp("name", prop->name)))
1755 {
1756 regEntry->setProperty((const char *) prop->name, (void *) (prop + 1), prop->length);
1757 // HIBPRINT("%s: %s, %d\n", regEntry->getName(), prop->name, prop->length);
1758 }
1759 prop = (DeviceTreeNodeProperty *) (((uintptr_t)(prop + 1)) + ((prop->length + 3) & ~3));
1760 }
1761
1762 child = (DeviceTreeNode *) prop;
1763 for (idx = 0; idx < entry->nChildren; idx++)
1764 {
1765 if (kSuccess != DTGetProperty(child, "name", (void **) &nameProp, &propLen))
1766 panic("no name");
1767 childRegEntry = regEntry ? regEntry->childFromPath(nameProp, gIODTPlane) : NULL;
1768 // HIBPRINT("%s == %p\n", nameProp, childRegEntry);
1769 child = MergeDeviceTree(child, childRegEntry);
1770 }
1771 return (child);
1772 }
1773
1774 IOReturn
1775 IOHibernateSystemWake(void)
1776 {
1777 if (kFSOpened == gFSState)
1778 {
1779 IOHibernateDone(&gIOHibernateVars);
1780 }
1781 else
1782 {
1783 IOService::getPMRootDomain()->removeProperty(kIOHibernateOptionsKey);
1784 IOService::getPMRootDomain()->removeProperty(kIOHibernateGfxStatusKey);
1785 }
1786 return (kIOReturnSuccess);
1787 }
1788
1789 static IOReturn
1790 IOHibernateDone(IOHibernateVars * vars)
1791 {
1792 IORegistryEntry * next;
1793
1794 hibernate_teardown(vars->page_list, vars->page_list_wired, vars->page_list_pal);
1795
1796 if (vars->videoMapping)
1797 {
1798 if (vars->videoMapSize)
1799 // remove mappings
1800 IOUnmapPages(kernel_map, vars->videoMapping, vars->videoMapSize);
1801 if (vars->videoAllocSize)
1802 // dealloc range
1803 kmem_free(kernel_map, trunc_page(vars->videoMapping), vars->videoAllocSize);
1804 }
1805
1806 if (vars->previewBuffer)
1807 {
1808 vars->previewBuffer->release();
1809 vars->previewBuffer = 0;
1810 }
1811
1812 if (kIOHibernateStateWakingFromHibernate == gIOHibernateState)
1813 {
1814 IOService::getPMRootDomain()->setProperty(kIOHibernateOptionsKey,
1815 gIOHibernateCurrentHeader->options, 32);
1816 }
1817 else
1818 {
1819 IOService::getPMRootDomain()->removeProperty(kIOHibernateOptionsKey);
1820 }
1821
1822 if ((kIOHibernateStateWakingFromHibernate == gIOHibernateState)
1823 && (kIOHibernateGfxStatusUnknown != gIOHibernateGraphicsInfo->gfxStatus))
1824 {
1825 IOService::getPMRootDomain()->setProperty(kIOHibernateGfxStatusKey,
1826 &gIOHibernateGraphicsInfo->gfxStatus,
1827 sizeof(gIOHibernateGraphicsInfo->gfxStatus));
1828 }
1829 else
1830 {
1831 IOService::getPMRootDomain()->removeProperty(kIOHibernateGfxStatusKey);
1832 }
1833
1834 if (vars->fileVars)
1835 {
1836 if ((next = vars->fileVars->media)) do
1837 {
1838 next->removeProperty(kIOPolledInterfaceActiveKey);
1839 next = next->getParentEntry(gIOServicePlane);
1840 }
1841 while (next);
1842 IOPolledFileClose(vars->fileVars);
1843 }
1844
1845 // invalidate nvram properties - (gIOOptionsEntry != 0) => nvram was touched
1846
1847 #if defined(__i386__) || defined(__x86_64__)
1848 IOService::getPMRootDomain()->removeProperty(gIOHibernateRTCVariablesKey);
1849 IOService::getPMRootDomain()->removeProperty(kIOHibernateSMCVariablesKey);
1850
1851 /*
1852 * Hibernate variable is written to NVRAM on platforms in which RtcRam
1853 * is not backed by coin cell. Remove Hibernate data from NVRAM.
1854 */
1855 if (gIOOptionsEntry) {
1856
1857 if (gIOHibernateRTCVariablesKey) {
1858 if (gIOOptionsEntry->getProperty(gIOHibernateRTCVariablesKey)) {
1859 gIOOptionsEntry->removeProperty(gIOHibernateRTCVariablesKey);
1860 }
1861 }
1862
1863 if (gIOHibernateBootNextKey)
1864 {
1865 if (gIOHibernateBootNextSave)
1866 {
1867 gIOOptionsEntry->setProperty(gIOHibernateBootNextKey, gIOHibernateBootNextSave);
1868 gIOHibernateBootNextSave->release();
1869 gIOHibernateBootNextSave = NULL;
1870 }
1871 else
1872 gIOOptionsEntry->removeProperty(gIOHibernateBootNextKey);
1873 }
1874 gIOOptionsEntry->sync();
1875 }
1876 #endif
1877
1878 if (vars->srcBuffer)
1879 vars->srcBuffer->release();
1880 if (vars->ioBuffer)
1881 vars->ioBuffer->release();
1882 bzero(&gIOHibernateHandoffPages[0], gIOHibernateHandoffPageCount * sizeof(gIOHibernateHandoffPages[0]));
1883 if (vars->handoffBuffer)
1884 {
1885 if (kIOHibernateStateWakingFromHibernate == gIOHibernateState)
1886 {
1887 IOHibernateHandoff * handoff;
1888 bool done = false;
1889 for (handoff = (IOHibernateHandoff *) vars->handoffBuffer->getBytesNoCopy();
1890 !done;
1891 handoff = (IOHibernateHandoff *) &handoff->data[handoff->bytecount])
1892 {
1893 HIBPRINT("handoff %p, %x, %x\n", handoff, handoff->type, handoff->bytecount);
1894 uint8_t * data = &handoff->data[0];
1895 switch (handoff->type)
1896 {
1897 case kIOHibernateHandoffTypeEnd:
1898 done = true;
1899 break;
1900
1901 case kIOHibernateHandoffTypeDeviceTree:
1902 MergeDeviceTree((DeviceTreeNode *) data, IOService::getServiceRoot());
1903 break;
1904
1905 case kIOHibernateHandoffTypeKeyStore:
1906 #if defined(__i386__) || defined(__x86_64__)
1907 {
1908 IOBufferMemoryDescriptor *
1909 md = IOBufferMemoryDescriptor::withBytes(data, handoff->bytecount, kIODirectionOutIn);
1910 if (md)
1911 {
1912 IOSetKeyStoreData(md);
1913 }
1914 }
1915 #endif
1916 break;
1917
1918 default:
1919 done = (kIOHibernateHandoffType != (handoff->type & 0xFFFF0000));
1920 break;
1921 }
1922 }
1923 }
1924 vars->handoffBuffer->release();
1925 }
1926 if (vars->fileExtents)
1927 vars->fileExtents->release();
1928
1929 bzero(vars, sizeof(*vars));
1930
1931 // gIOHibernateState = kIOHibernateStateInactive; // leave it for post wake code to see
1932
1933 return (kIOReturnSuccess);
1934 }
1935
1936 IOReturn
1937 IOHibernateSystemPostWake(void)
1938 {
1939 struct kern_direct_file_io_ref_t * fileRef;
1940
1941 if (kFSOpened == gFSState)
1942 {
1943 // invalidate & close the image file
1944 gIOHibernateCurrentHeader->signature = kIOHibernateHeaderInvalidSignature;
1945 if ((fileRef = gIOHibernateFileRef))
1946 {
1947 gIOHibernateFileRef = 0;
1948 kern_close_file_for_direct_io(fileRef,
1949 0, (caddr_t) gIOHibernateCurrentHeader,
1950 sizeof(IOHibernateImageHeader),
1951 sizeof(IOHibernateImageHeader),
1952 gIOHibernateCurrentHeader->imageSize);
1953 }
1954 gFSState = kFSIdle;
1955 }
1956 return (kIOReturnSuccess);
1957 }
1958
1959 bool IOHibernateWasScreenLocked(void)
1960 {
1961 bool ret = false;
1962 if ((kIOHibernateStateWakingFromHibernate == gIOHibernateState) && gIOChosenEntry)
1963 {
1964 OSData *
1965 data = OSDynamicCast(OSData, gIOChosenEntry->getProperty(kIOScreenLockStateKey));
1966 if (data) switch (*((uint32_t *)data->getBytesNoCopy()))
1967 {
1968 case kIOScreenLockLocked:
1969 case kIOScreenLockFileVaultDialog:
1970 ret = true;
1971 break;
1972 case kIOScreenLockNoLock:
1973 case kIOScreenLockUnlocked:
1974 default:
1975 ret = false;
1976 break;
1977 }
1978 }
1979 return (ret);
1980 }
1981
1982 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1983
1984 SYSCTL_STRING(_kern, OID_AUTO, hibernatefile,
1985 CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
1986 gIOHibernateFilename, sizeof(gIOHibernateFilename), "");
1987 SYSCTL_STRING(_kern, OID_AUTO, bootsignature,
1988 CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
1989 gIOHibernateBootSignature, sizeof(gIOHibernateBootSignature), "");
1990 SYSCTL_UINT(_kern, OID_AUTO, hibernatemode,
1991 CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
1992 &gIOHibernateMode, 0, "");
1993
1994 void
1995 IOHibernateSystemInit(IOPMrootDomain * rootDomain)
1996 {
1997 OSData * data = OSData::withBytesNoCopy(&gIOHibernateState, sizeof(gIOHibernateState));
1998 if (data)
1999 {
2000 rootDomain->setProperty(kIOHibernateStateKey, data);
2001 data->release();
2002 }
2003
2004 if (PE_parse_boot_argn("hfile", gIOHibernateFilename, sizeof(gIOHibernateFilename)))
2005 gIOHibernateMode = kIOHibernateModeOn;
2006 else
2007 gIOHibernateFilename[0] = 0;
2008
2009 sysctl_register_oid(&sysctl__kern_hibernatefile);
2010 sysctl_register_oid(&sysctl__kern_bootsignature);
2011 sysctl_register_oid(&sysctl__kern_hibernatemode);
2012
2013 gFSLock = IOLockAlloc();
2014 }
2015
2016
2017 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2018
2019 static void
2020 hibernate_setup_for_wake(void)
2021 {
2022 }
2023
2024 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2025
2026 #define C_ASSERT(e) typedef char __C_ASSERT__[(e) ? 1 : -1]
2027
2028 static bool
2029 no_encrypt_page(vm_offset_t ppnum)
2030 {
2031 if (pmap_is_noencrypt((ppnum_t)ppnum) == TRUE)
2032 {
2033 return true;
2034 }
2035 return false;
2036 }
2037
2038 uint32_t wired_pages_encrypted = 0;
2039 uint32_t dirty_pages_encrypted = 0;
2040 uint32_t wired_pages_clear = 0;
2041
2042 static void
2043 hibernate_pal_callback(void *vars_arg, vm_offset_t addr)
2044 {
2045 IOHibernateVars *vars = (IOHibernateVars *)vars_arg;
2046 /* Make sure it's not in either of the save lists */
2047 hibernate_set_page_state(vars->page_list, vars->page_list_wired, atop_64(addr), 1, kIOHibernatePageStateFree);
2048
2049 /* Set it in the bitmap of pages owned by the PAL */
2050 hibernate_page_bitset(vars->page_list_pal, TRUE, atop_64(addr));
2051 }
2052
2053 static struct hibernate_cryptvars_t *local_cryptvars;
2054
2055 extern "C" int
2056 hibernate_pal_write(void *buffer, size_t size)
2057 {
2058 IOHibernateVars * vars = &gIOHibernateVars;
2059
2060 IOReturn err = IOPolledFileWrite(vars->fileVars, (const uint8_t *)buffer, size, local_cryptvars);
2061 if (kIOReturnSuccess != err) {
2062 kprintf("epic hibernate fail! %d\n", err);
2063 return err;
2064 }
2065
2066 return 0;
2067 }
2068
2069
2070 extern "C" uint32_t
2071 hibernate_write_image(void)
2072 {
2073 IOHibernateImageHeader * header = gIOHibernateCurrentHeader;
2074 IOHibernateVars * vars = &gIOHibernateVars;
2075 IOPolledFileExtent * fileExtents;
2076
2077 C_ASSERT(sizeof(IOHibernateImageHeader) == 512);
2078
2079 uint32_t pageCount, pagesDone;
2080 IOReturn err;
2081 vm_offset_t ppnum, page;
2082 IOItemCount count;
2083 uint8_t * src;
2084 uint8_t * data;
2085 IOByteCount pageCompressedSize;
2086 uint64_t compressedSize, uncompressedSize;
2087 uint64_t image1Size = 0;
2088 uint32_t bitmap_size;
2089 bool iterDone, pollerOpen, needEncrypt;
2090 uint32_t restore1Sum, sum, sum1, sum2;
2091 uint32_t tag;
2092 uint32_t pageType;
2093 uint32_t pageAndCount[2];
2094 addr64_t phys64;
2095 IOByteCount segLen;
2096
2097 AbsoluteTime startTime, endTime;
2098 AbsoluteTime allTime, compTime;
2099 uint64_t compBytes;
2100 uint64_t nsec;
2101 uint32_t lastProgressStamp = 0;
2102 uint32_t progressStamp;
2103 uint32_t blob, lastBlob = (uint32_t) -1L;
2104
2105 hibernate_cryptvars_t _cryptvars;
2106 hibernate_cryptvars_t * cryptvars = 0;
2107
2108 wired_pages_encrypted = 0;
2109 dirty_pages_encrypted = 0;
2110 wired_pages_clear = 0;
2111
2112 if (!vars->fileVars || !vars->fileVars->pollers || !vars->fileExtents)
2113 return (false /* sleep */ );
2114
2115 if (kIOHibernateModeSleep & gIOHibernateMode)
2116 kdebug_enable = save_kdebug_enable;
2117
2118 KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 1) | DBG_FUNC_START, 0, 0, 0, 0, 0);
2119 IOService::getPMRootDomain()->tracePoint(kIOPMTracePointHibernate);
2120
2121 restore1Sum = sum1 = sum2 = 0;
2122
2123 hibernate_pal_prepare();
2124
2125 #if CRYPTO
2126 // encryption data. "iv" is the "initial vector".
2127 if (kIOHibernateModeEncrypt & gIOHibernateMode)
2128 {
2129 static const unsigned char first_iv[AES_BLOCK_SIZE]
2130 = { 0xa3, 0x63, 0x65, 0xa9, 0x0b, 0x71, 0x7b, 0x1c,
2131 0xdf, 0x9e, 0x5f, 0x32, 0xd7, 0x61, 0x63, 0xda };
2132
2133 cryptvars = &gIOHibernateCryptWakeContext;
2134 bzero(cryptvars, sizeof(hibernate_cryptvars_t));
2135 aes_encrypt_key(vars->cryptKey,
2136 kIOHibernateAESKeySize,
2137 &cryptvars->ctx.encrypt);
2138 aes_decrypt_key(vars->cryptKey,
2139 kIOHibernateAESKeySize,
2140 &cryptvars->ctx.decrypt);
2141
2142 cryptvars = &_cryptvars;
2143 bzero(cryptvars, sizeof(hibernate_cryptvars_t));
2144 for (pageCount = 0; pageCount < sizeof(vars->wiredCryptKey); pageCount++)
2145 vars->wiredCryptKey[pageCount] ^= vars->volumeCryptKey[pageCount];
2146 bzero(&vars->volumeCryptKey[0], sizeof(vars->volumeCryptKey));
2147 aes_encrypt_key(vars->wiredCryptKey,
2148 kIOHibernateAESKeySize,
2149 &cryptvars->ctx.encrypt);
2150
2151 bcopy(&first_iv[0], &cryptvars->aes_iv[0], AES_BLOCK_SIZE);
2152 bzero(&vars->wiredCryptKey[0], sizeof(vars->wiredCryptKey));
2153 bzero(&vars->cryptKey[0], sizeof(vars->cryptKey));
2154
2155 local_cryptvars = cryptvars;
2156 }
2157 #endif /* CRYPTO */
2158
2159 hibernate_setup_for_wake();
2160
2161 hibernate_page_list_setall(vars->page_list,
2162 vars->page_list_wired,
2163 vars->page_list_pal,
2164 false /* !preflight */,
2165 &pageCount);
2166
2167 HIBLOG("hibernate_page_list_setall found pageCount %d\n", pageCount);
2168
2169 fileExtents = (IOPolledFileExtent *) vars->fileExtents->getBytesNoCopy();
2170
2171 #if 0
2172 count = vars->fileExtents->getLength() / sizeof(IOPolledFileExtent);
2173 for (page = 0; page < count; page++)
2174 {
2175 HIBLOG("fileExtents[%d] %qx, %qx (%qx)\n", page,
2176 fileExtents[page].start, fileExtents[page].length,
2177 fileExtents[page].start + fileExtents[page].length);
2178 }
2179 #endif
2180
2181 needEncrypt = (0 != (kIOHibernateModeEncrypt & gIOHibernateMode));
2182 AbsoluteTime_to_scalar(&compTime) = 0;
2183 compBytes = 0;
2184
2185 clock_get_uptime(&allTime);
2186 IOService::getPMRootDomain()->pmStatsRecordEvent(
2187 kIOPMStatsHibernateImageWrite | kIOPMStatsEventStartFlag, allTime);
2188
2189 do
2190 {
2191 compressedSize = 0;
2192 uncompressedSize = 0;
2193
2194 IOPolledFileSeek(vars->fileVars, sizeof(IOHibernateImageHeader));
2195
2196 HIBLOG("IOHibernatePollerOpen, ml_get_interrupts_enabled %d\n",
2197 ml_get_interrupts_enabled());
2198 err = IOHibernatePollerOpen(vars->fileVars, kIOPolledBeforeSleepState, vars->ioBuffer);
2199 HIBLOG("IOHibernatePollerOpen(%x)\n", err);
2200 pollerOpen = (kIOReturnSuccess == err);
2201 if (!pollerOpen)
2202 break;
2203
2204 // copy file block extent list if larger than header
2205
2206 count = vars->fileExtents->getLength();
2207 if (count > sizeof(header->fileExtentMap))
2208 {
2209 count -= sizeof(header->fileExtentMap);
2210 err = IOPolledFileWrite(vars->fileVars,
2211 ((uint8_t *) &fileExtents[0]) + sizeof(header->fileExtentMap), count, cryptvars);
2212 if (kIOReturnSuccess != err)
2213 break;
2214 }
2215
2216 uintptr_t hibernateBase;
2217 uintptr_t hibernateEnd;
2218
2219 hibernateBase = HIB_BASE; /* Defined in PAL headers */
2220
2221 hibernateEnd = (segHIBB + segSizeHIB);
2222
2223 // copy out restore1 code
2224
2225 for (count = 0;
2226 (phys64 = vars->handoffBuffer->getPhysicalSegment(count, &segLen, kIOMemoryMapperNone));
2227 count += segLen)
2228 {
2229 for (pagesDone = 0; pagesDone < atop_32(segLen); pagesDone++)
2230 {
2231 gIOHibernateHandoffPages[atop_32(count) + pagesDone] = atop_64(phys64) + pagesDone;
2232 }
2233 }
2234
2235 page = atop_32(kvtophys(hibernateBase));
2236 count = atop_32(round_page(hibernateEnd) - hibernateBase);
2237 header->restore1CodePhysPage = page;
2238 header->restore1CodeVirt = hibernateBase;
2239 header->restore1PageCount = count;
2240 header->restore1CodeOffset = ((uintptr_t) &hibernate_machine_entrypoint) - hibernateBase;
2241 header->restore1StackOffset = ((uintptr_t) &gIOHibernateRestoreStackEnd[0]) - 64 - hibernateBase;
2242
2243 // sum __HIB seg, with zeros for the stack
2244 src = (uint8_t *) trunc_page(hibernateBase);
2245 for (page = 0; page < count; page++)
2246 {
2247 if ((src < &gIOHibernateRestoreStack[0]) || (src >= &gIOHibernateRestoreStackEnd[0]))
2248 restore1Sum += hibernate_sum_page(src, header->restore1CodeVirt + page);
2249 else
2250 restore1Sum += 0x00000000;
2251 src += page_size;
2252 }
2253 sum1 = restore1Sum;
2254
2255 // write the __HIB seg, with zeros for the stack
2256
2257 src = (uint8_t *) trunc_page(hibernateBase);
2258 count = ((uintptr_t) &gIOHibernateRestoreStack[0]) - trunc_page(hibernateBase);
2259 if (count)
2260 {
2261 err = IOPolledFileWrite(vars->fileVars, src, count, cryptvars);
2262 if (kIOReturnSuccess != err)
2263 break;
2264 }
2265 err = IOPolledFileWrite(vars->fileVars,
2266 (uint8_t *) 0,
2267 &gIOHibernateRestoreStackEnd[0] - &gIOHibernateRestoreStack[0],
2268 cryptvars);
2269 if (kIOReturnSuccess != err)
2270 break;
2271 src = &gIOHibernateRestoreStackEnd[0];
2272 count = round_page(hibernateEnd) - ((uintptr_t) src);
2273 if (count)
2274 {
2275 err = IOPolledFileWrite(vars->fileVars, src, count, cryptvars);
2276 if (kIOReturnSuccess != err)
2277 break;
2278 }
2279
2280 vars->fileVars->encryptStart = (vars->fileVars->position & ~(AES_BLOCK_SIZE - 1));
2281 vars->fileVars->encryptEnd = UINT64_MAX;
2282 HIBLOG("encryptStart %qx\n", vars->fileVars->encryptStart);
2283
2284 // write the preview buffer
2285
2286 if (vars->previewBuffer)
2287 {
2288 ppnum = 0;
2289 count = 0;
2290 do
2291 {
2292 phys64 = vars->previewBuffer->getPhysicalSegment(count, &segLen, kIOMemoryMapperNone);
2293 pageAndCount[0] = atop_64(phys64);
2294 pageAndCount[1] = atop_32(segLen);
2295 err = IOPolledFileWrite(vars->fileVars,
2296 (const uint8_t *) &pageAndCount, sizeof(pageAndCount),
2297 cryptvars);
2298 if (kIOReturnSuccess != err)
2299 break;
2300 count += segLen;
2301 ppnum += sizeof(pageAndCount);
2302 }
2303 while (phys64);
2304 if (kIOReturnSuccess != err)
2305 break;
2306
2307 src = (uint8_t *) vars->previewBuffer->getPhysicalSegment(0, NULL, _kIOMemorySourceSegment);
2308
2309 ((hibernate_preview_t *)src)->lockTime = gIOConsoleLockTime;
2310
2311 count = vars->previewBuffer->getLength();
2312
2313 header->previewPageListSize = ppnum;
2314 header->previewSize = count + ppnum;
2315
2316 for (page = 0; page < count; page += page_size)
2317 {
2318 phys64 = vars->previewBuffer->getPhysicalSegment(page, NULL, kIOMemoryMapperNone);
2319 sum1 += hibernate_sum_page(src + page, atop_64(phys64));
2320 }
2321 err = IOPolledFileWrite(vars->fileVars, src, count, cryptvars);
2322 if (kIOReturnSuccess != err)
2323 break;
2324 }
2325
2326 // mark areas for no save
2327
2328 for (count = 0;
2329 (phys64 = vars->ioBuffer->getPhysicalSegment(count, &segLen, kIOMemoryMapperNone));
2330 count += segLen)
2331 {
2332 hibernate_set_page_state(vars->page_list, vars->page_list_wired,
2333 atop_64(phys64), atop_32(segLen),
2334 kIOHibernatePageStateFree);
2335 pageCount -= atop_32(segLen);
2336 }
2337
2338 for (count = 0;
2339 (phys64 = vars->srcBuffer->getPhysicalSegment(count, &segLen, kIOMemoryMapperNone));
2340 count += segLen)
2341 {
2342 hibernate_set_page_state(vars->page_list, vars->page_list_wired,
2343 atop_64(phys64), atop_32(segLen),
2344 kIOHibernatePageStateFree);
2345 pageCount -= atop_32(segLen);
2346 }
2347
2348 // copy out bitmap of pages available for trashing during restore
2349
2350 bitmap_size = vars->page_list_wired->list_size;
2351 src = (uint8_t *) vars->page_list_wired;
2352 err = IOPolledFileWrite(vars->fileVars, src, bitmap_size, cryptvars);
2353 if (kIOReturnSuccess != err)
2354 break;
2355
2356 // mark more areas for no save, but these are not available
2357 // for trashing during restore
2358
2359 hibernate_page_list_set_volatile(vars->page_list, vars->page_list_wired, &pageCount);
2360
2361
2362 page = atop_32(KERNEL_IMAGE_TO_PHYS(hibernateBase));
2363 count = atop_32(round_page(KERNEL_IMAGE_TO_PHYS(hibernateEnd))) - page;
2364 hibernate_set_page_state(vars->page_list, vars->page_list_wired,
2365 page, count,
2366 kIOHibernatePageStateFree);
2367 pageCount -= count;
2368
2369 if (vars->previewBuffer) for (count = 0;
2370 (phys64 = vars->previewBuffer->getPhysicalSegment(count, &segLen, kIOMemoryMapperNone));
2371 count += segLen)
2372 {
2373 hibernate_set_page_state(vars->page_list, vars->page_list_wired,
2374 atop_64(phys64), atop_32(segLen),
2375 kIOHibernatePageStateFree);
2376 pageCount -= atop_32(segLen);
2377 }
2378
2379 for (count = 0;
2380 (phys64 = vars->handoffBuffer->getPhysicalSegment(count, &segLen, kIOMemoryMapperNone));
2381 count += segLen)
2382 {
2383 hibernate_set_page_state(vars->page_list, vars->page_list_wired,
2384 atop_64(phys64), atop_32(segLen),
2385 kIOHibernatePageStateFree);
2386 pageCount -= atop_32(segLen);
2387 }
2388
2389 (void)hibernate_pal_callback;
2390
2391 src = (uint8_t *) vars->srcBuffer->getBytesNoCopy();
2392
2393 pagesDone = 0;
2394 lastBlob = 0;
2395
2396 HIBLOG("bitmap_size 0x%x, previewSize 0x%x, writing %d pages @ 0x%llx\n",
2397 bitmap_size, header->previewSize,
2398 pageCount, vars->fileVars->position);
2399
2400 enum
2401 // pageType
2402 {
2403 kWired = 0x02,
2404 kEncrypt = 0x01,
2405 kWiredEncrypt = kWired | kEncrypt,
2406 kWiredClear = kWired,
2407 kUnwiredEncrypt = kEncrypt
2408 };
2409
2410 for (pageType = kWiredEncrypt; pageType >= kUnwiredEncrypt; pageType--)
2411 {
2412 if (kUnwiredEncrypt == pageType)
2413 {
2414 // start unwired image
2415 vars->fileVars->encryptStart = (vars->fileVars->position & ~(((uint64_t)AES_BLOCK_SIZE) - 1));
2416 vars->fileVars->encryptEnd = UINT64_MAX;
2417 HIBLOG("encryptStart %qx\n", vars->fileVars->encryptStart);
2418 bcopy(&cryptvars->aes_iv[0],
2419 &gIOHibernateCryptWakeContext.aes_iv[0],
2420 sizeof(cryptvars->aes_iv));
2421 cryptvars = &gIOHibernateCryptWakeContext;
2422 }
2423 for (iterDone = false, ppnum = 0; !iterDone; )
2424 {
2425 count = hibernate_page_list_iterate((kWired & pageType)
2426 ? vars->page_list_wired : vars->page_list,
2427 &ppnum);
2428 // kprintf("[%d](%x : %x)\n", pageType, ppnum, count);
2429 iterDone = !count;
2430
2431 if (count && (kWired & pageType) && needEncrypt)
2432 {
2433 uint32_t checkIndex;
2434 for (checkIndex = 0;
2435 (checkIndex < count)
2436 && (((kEncrypt & pageType) == 0) == no_encrypt_page(ppnum + checkIndex));
2437 checkIndex++)
2438 {}
2439 if (!checkIndex)
2440 {
2441 ppnum++;
2442 continue;
2443 }
2444 count = checkIndex;
2445 }
2446
2447 switch (pageType)
2448 {
2449 case kWiredEncrypt: wired_pages_encrypted += count; break;
2450 case kWiredClear: wired_pages_clear += count; break;
2451 case kUnwiredEncrypt: dirty_pages_encrypted += count; break;
2452 }
2453
2454 if (iterDone && (kWiredEncrypt == pageType)) {/* not yet end of wired list */}
2455 else
2456 {
2457 pageAndCount[0] = ppnum;
2458 pageAndCount[1] = count;
2459 err = IOPolledFileWrite(vars->fileVars,
2460 (const uint8_t *) &pageAndCount, sizeof(pageAndCount),
2461 cryptvars);
2462 if (kIOReturnSuccess != err)
2463 break;
2464 }
2465
2466 for (page = ppnum; page < (ppnum + count); page++)
2467 {
2468 err = IOMemoryDescriptorWriteFromPhysical(vars->srcBuffer, 0, ptoa_64(page), page_size);
2469 if (err)
2470 {
2471 HIBLOG("IOMemoryDescriptorWriteFromPhysical %d [%ld] %x\n", __LINE__, (long)page, err);
2472 break;
2473 }
2474
2475 sum = hibernate_sum_page(src, page);
2476 if (kWired & pageType)
2477 sum1 += sum;
2478 else
2479 sum2 += sum;
2480
2481 clock_get_uptime(&startTime);
2482
2483 pageCompressedSize = WKdm_compress ((WK_word*) src, (WK_word*) (src + page_size), PAGE_SIZE_IN_WORDS);
2484
2485 clock_get_uptime(&endTime);
2486 ADD_ABSOLUTETIME(&compTime, &endTime);
2487 SUB_ABSOLUTETIME(&compTime, &startTime);
2488 compBytes += page_size;
2489
2490 if (kIOHibernateModeEncrypt & gIOHibernateMode)
2491 pageCompressedSize = (pageCompressedSize + AES_BLOCK_SIZE - 1) & ~(AES_BLOCK_SIZE - 1);
2492
2493 if (pageCompressedSize > page_size)
2494 {
2495 // HIBLOG("------------lose: %d\n", pageCompressedSize);
2496 pageCompressedSize = page_size;
2497 }
2498
2499 if (pageCompressedSize != page_size)
2500 data = (src + page_size);
2501 else
2502 data = src;
2503
2504 tag = pageCompressedSize | kIOHibernateTagSignature;
2505 err = IOPolledFileWrite(vars->fileVars, (const uint8_t *) &tag, sizeof(tag), cryptvars);
2506 if (kIOReturnSuccess != err)
2507 break;
2508
2509 err = IOPolledFileWrite(vars->fileVars, data, (pageCompressedSize + 3) & ~3, cryptvars);
2510 if (kIOReturnSuccess != err)
2511 break;
2512
2513 compressedSize += pageCompressedSize;
2514 if (pageCompressedSize)
2515 uncompressedSize += page_size;
2516 pagesDone++;
2517
2518 if (vars->consoleMapping && (0 == (1023 & pagesDone)))
2519 {
2520 blob = ((pagesDone * kIOHibernateProgressCount) / pageCount);
2521 if (blob != lastBlob)
2522 {
2523 ProgressUpdate(gIOHibernateGraphicsInfo, vars->consoleMapping, lastBlob, blob);
2524 lastBlob = blob;
2525 }
2526 }
2527 if (0 == (8191 & pagesDone))
2528 {
2529 clock_get_uptime(&endTime);
2530 SUB_ABSOLUTETIME(&endTime, &allTime);
2531 absolutetime_to_nanoseconds(endTime, &nsec);
2532 progressStamp = nsec / 750000000ULL;
2533 if (progressStamp != lastProgressStamp)
2534 {
2535 lastProgressStamp = progressStamp;
2536 HIBPRINT("pages %d (%d%%)\n", pagesDone, (100 * pagesDone) / pageCount);
2537 }
2538 }
2539 }
2540 if (kIOReturnSuccess != err)
2541 break;
2542 ppnum = page;
2543 }
2544
2545 if (kIOReturnSuccess != err)
2546 break;
2547
2548 if ((kEncrypt & pageType))
2549 {
2550 vars->fileVars->encryptEnd = ((vars->fileVars->position + 511) & ~511ULL);
2551 HIBLOG("encryptEnd %qx\n", vars->fileVars->encryptEnd);
2552 }
2553
2554 if (kWiredEncrypt != pageType)
2555 {
2556 // end of image1/2 - fill to next block
2557 err = IOPolledFileWrite(vars->fileVars, 0, 0, cryptvars);
2558 if (kIOReturnSuccess != err)
2559 break;
2560 }
2561 if (kWiredClear == pageType)
2562 {
2563 // enlarge wired image for test
2564 // err = IOPolledFileWrite(vars->fileVars, 0, 0x60000000, cryptvars);
2565
2566 // end wired image
2567 header->encryptStart = vars->fileVars->encryptStart;
2568 header->encryptEnd = vars->fileVars->encryptEnd;
2569 image1Size = vars->fileVars->position;
2570 HIBLOG("image1Size 0x%qx, encryptStart1 0x%qx, End1 0x%qx\n",
2571 image1Size, header->encryptStart, header->encryptEnd);
2572 }
2573 }
2574 if (kIOReturnSuccess != err)
2575 break;
2576
2577 // Header:
2578
2579 header->imageSize = vars->fileVars->position;
2580 header->image1Size = image1Size;
2581 header->bitmapSize = bitmap_size;
2582 header->pageCount = pageCount;
2583
2584 header->restore1Sum = restore1Sum;
2585 header->image1Sum = sum1;
2586 header->image2Sum = sum2;
2587 header->sleepTime = gIOLastSleepTime.tv_sec;
2588
2589 header->compression = (compressedSize << 8) / uncompressedSize;
2590 gIOHibernateCompression = header->compression;
2591
2592 count = vars->fileExtents->getLength();
2593 if (count > sizeof(header->fileExtentMap))
2594 {
2595 header->fileExtentMapSize = count;
2596 count = sizeof(header->fileExtentMap);
2597 }
2598 else
2599 header->fileExtentMapSize = sizeof(header->fileExtentMap);
2600 bcopy(&fileExtents[0], &header->fileExtentMap[0], count);
2601
2602 header->deviceBase = vars->fileVars->block0;
2603
2604 IOPolledFileSeek(vars->fileVars, 0);
2605 err = IOPolledFileWrite(vars->fileVars,
2606 (uint8_t *) header, sizeof(IOHibernateImageHeader),
2607 cryptvars);
2608 if (kIOReturnSuccess != err)
2609 break;
2610 err = IOPolledFileWrite(vars->fileVars, 0, 0, cryptvars);
2611 if (kIOReturnSuccess != err)
2612 break;
2613 err = IOHibernatePollerIODone(vars->fileVars, true);
2614 if (kIOReturnSuccess != err)
2615 break;
2616 }
2617 while (false);
2618
2619 clock_get_uptime(&endTime);
2620
2621 IOService::getPMRootDomain()->pmStatsRecordEvent(
2622 kIOPMStatsHibernateImageWrite | kIOPMStatsEventStopFlag, endTime);
2623
2624 SUB_ABSOLUTETIME(&endTime, &allTime);
2625 absolutetime_to_nanoseconds(endTime, &nsec);
2626 HIBLOG("all time: %qd ms, ",
2627 nsec / 1000000ULL);
2628
2629 absolutetime_to_nanoseconds(compTime, &nsec);
2630 HIBLOG("comp bytes: %qd time: %qd ms %qd Mb/s, ",
2631 compBytes,
2632 nsec / 1000000ULL,
2633 nsec ? (((compBytes * 1000000000ULL) / 1024 / 1024) / nsec) : 0);
2634
2635 absolutetime_to_nanoseconds(vars->fileVars->cryptTime, &nsec);
2636 HIBLOG("crypt bytes: %qd time: %qd ms %qd Mb/s, ",
2637 vars->fileVars->cryptBytes,
2638 nsec / 1000000ULL,
2639 nsec ? (((vars->fileVars->cryptBytes * 1000000000ULL) / 1024 / 1024) / nsec) : 0);
2640
2641 HIBLOG("\nimage %qd (%lld%%), uncompressed %qd (%d), compressed %qd (%d%%), sum1 %x, sum2 %x\n",
2642 header->imageSize, (header->imageSize * 100) / vars->fileVars->fileSize,
2643 uncompressedSize, atop_32(uncompressedSize), compressedSize,
2644 uncompressedSize ? ((int) ((compressedSize * 100ULL) / uncompressedSize)) : 0,
2645 sum1, sum2);
2646
2647 HIBLOG("wired_pages_encrypted %d, wired_pages_clear %d, dirty_pages_encrypted %d\n",
2648 wired_pages_encrypted, wired_pages_clear, dirty_pages_encrypted);
2649
2650 if (vars->fileVars->io)
2651 (void) IOHibernatePollerIODone(vars->fileVars, false);
2652
2653 if (pollerOpen)
2654 IOHibernatePollerClose(vars->fileVars, kIOPolledBeforeSleepState);
2655
2656 if (vars->consoleMapping)
2657 ProgressUpdate(gIOHibernateGraphicsInfo,
2658 vars->consoleMapping, 0, kIOHibernateProgressCount);
2659
2660 HIBLOG("hibernate_write_image done(%x)\n", err);
2661
2662 // should we come back via regular wake, set the state in memory.
2663 gIOHibernateState = kIOHibernateStateInactive;
2664
2665 KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 1) | DBG_FUNC_END,
2666 wired_pages_encrypted, wired_pages_clear, dirty_pages_encrypted, 0, 0);
2667
2668 if (kIOReturnSuccess == err)
2669 {
2670 if (kIOHibernateModeSleep & gIOHibernateMode)
2671 {
2672 return (kIOHibernatePostWriteSleep);
2673 }
2674 else if(kIOHibernateModeRestart & gIOHibernateMode)
2675 {
2676 return (kIOHibernatePostWriteRestart);
2677 }
2678 else
2679 {
2680 /* by default, power down */
2681 return (kIOHibernatePostWriteHalt);
2682 }
2683 }
2684 else if (kIOReturnAborted == err)
2685 {
2686 return (kIOHibernatePostWriteWake);
2687 }
2688 else
2689 {
2690 /* on error, sleep */
2691 return (kIOHibernatePostWriteSleep);
2692 }
2693 }
2694
2695 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2696
2697 extern "C" void
2698 hibernate_machine_init(void)
2699 {
2700 IOReturn err;
2701 uint32_t sum;
2702 uint32_t pagesDone;
2703 uint32_t pagesRead = 0;
2704 AbsoluteTime startTime, compTime;
2705 AbsoluteTime allTime, endTime;
2706 uint64_t compBytes;
2707 uint64_t nsec;
2708 uint32_t lastProgressStamp = 0;
2709 uint32_t progressStamp;
2710 hibernate_cryptvars_t * cryptvars = 0;
2711
2712 IOHibernateVars * vars = &gIOHibernateVars;
2713
2714 if (!vars->fileVars || !vars->fileVars->pollers || !vars->fileExtents)
2715 return;
2716
2717 sum = gIOHibernateCurrentHeader->actualImage1Sum;
2718 pagesDone = gIOHibernateCurrentHeader->actualUncompressedPages;
2719
2720 HIBLOG("hibernate_machine_init: state %d, image pages %d, sum was %x, image1Size %qx, conflictCount %d, nextFree %x\n",
2721 gIOHibernateState, pagesDone, sum, gIOHibernateCurrentHeader->image1Size,
2722 gIOHibernateCurrentHeader->conflictCount, gIOHibernateCurrentHeader->nextFree);
2723
2724 if (kIOHibernateStateWakingFromHibernate != gIOHibernateState)
2725 {
2726 HIBLOG("regular wake\n");
2727 return;
2728 }
2729
2730 HIBPRINT("diag %x %x %x %x\n",
2731 gIOHibernateCurrentHeader->diag[0], gIOHibernateCurrentHeader->diag[1],
2732 gIOHibernateCurrentHeader->diag[2], gIOHibernateCurrentHeader->diag[3]);
2733
2734 HIBLOG("restore times %qd, %qd, %qd ms, tsc 0x%qx scale 0x%x\n",
2735 (((gIOHibernateCurrentHeader->restoreTime1 * pal_rtc_nanotime_info.scale) >> 32) / 1000000),
2736 (((gIOHibernateCurrentHeader->restoreTime2 * pal_rtc_nanotime_info.scale) >> 32) / 1000000),
2737 (((gIOHibernateCurrentHeader->restoreTime3 * pal_rtc_nanotime_info.scale) >> 32) / 1000000),
2738 gIOHibernateCurrentHeader->restoreTime1, pal_rtc_nanotime_info.scale);
2739
2740 if ((kIOHibernateModeDiscardCleanActive | kIOHibernateModeDiscardCleanInactive) & gIOHibernateMode)
2741 hibernate_page_list_discard(vars->page_list);
2742
2743 cryptvars = (kIOHibernateModeEncrypt & gIOHibernateMode) ? &gIOHibernateCryptWakeContext : 0;
2744
2745 if (gIOHibernateCurrentHeader->handoffPageCount > gIOHibernateHandoffPageCount)
2746 panic("handoff overflow");
2747
2748 IOHibernateHandoff * handoff;
2749 bool done = false;
2750 bool foundCryptData = false;
2751
2752 for (handoff = (IOHibernateHandoff *) vars->handoffBuffer->getBytesNoCopy();
2753 !done;
2754 handoff = (IOHibernateHandoff *) &handoff->data[handoff->bytecount])
2755 {
2756 // HIBPRINT("handoff %p, %x, %x\n", handoff, handoff->type, handoff->bytecount);
2757 uint8_t * data = &handoff->data[0];
2758 switch (handoff->type)
2759 {
2760 case kIOHibernateHandoffTypeEnd:
2761 done = true;
2762 break;
2763
2764 case kIOHibernateHandoffTypeGraphicsInfo:
2765 bcopy(data, gIOHibernateGraphicsInfo, sizeof(*gIOHibernateGraphicsInfo));
2766 break;
2767
2768 case kIOHibernateHandoffTypeCryptVars:
2769 if (cryptvars)
2770 {
2771 hibernate_cryptwakevars_t *
2772 wakevars = (hibernate_cryptwakevars_t *) &handoff->data[0];
2773 bcopy(&wakevars->aes_iv[0], &cryptvars->aes_iv[0], sizeof(cryptvars->aes_iv));
2774 }
2775 foundCryptData = true;
2776 bzero(data, handoff->bytecount);
2777 break;
2778
2779 case kIOHibernateHandoffTypeMemoryMap:
2780
2781 clock_get_uptime(&allTime);
2782
2783 hibernate_newruntime_map(data, handoff->bytecount,
2784 gIOHibernateCurrentHeader->systemTableOffset);
2785
2786 clock_get_uptime(&endTime);
2787
2788 SUB_ABSOLUTETIME(&endTime, &allTime);
2789 absolutetime_to_nanoseconds(endTime, &nsec);
2790
2791 HIBLOG("hibernate_newruntime_map time: %qd ms, ", nsec / 1000000ULL);
2792
2793 break;
2794
2795 case kIOHibernateHandoffTypeDeviceTree:
2796 {
2797 // DTEntry chosen = NULL;
2798 // HIBPRINT("DTLookupEntry %d\n", DTLookupEntry((const DTEntry) data, "/chosen", &chosen));
2799 }
2800 break;
2801
2802 default:
2803 done = (kIOHibernateHandoffType != (handoff->type & 0xFFFF0000));
2804 break;
2805 }
2806 }
2807 if (cryptvars && !foundCryptData)
2808 panic("hibernate handoff");
2809
2810 HIBPRINT("video %x %d %d %d status %x\n",
2811 gIOHibernateGraphicsInfo->physicalAddress, gIOHibernateGraphicsInfo->depth,
2812 gIOHibernateGraphicsInfo->width, gIOHibernateGraphicsInfo->height, gIOHibernateGraphicsInfo->gfxStatus);
2813
2814 if (vars->videoMapping && gIOHibernateGraphicsInfo->physicalAddress)
2815 {
2816 vars->videoMapSize = round_page(gIOHibernateGraphicsInfo->height
2817 * gIOHibernateGraphicsInfo->rowBytes);
2818 IOMapPages(kernel_map,
2819 vars->videoMapping, gIOHibernateGraphicsInfo->physicalAddress,
2820 vars->videoMapSize, kIOMapInhibitCache );
2821 }
2822
2823 if (vars->videoMapSize)
2824 ProgressUpdate(gIOHibernateGraphicsInfo,
2825 (uint8_t *) vars->videoMapping, 0, kIOHibernateProgressCount);
2826
2827 uint8_t * src = (uint8_t *) vars->srcBuffer->getBytesNoCopy();
2828 uint32_t decoOffset;
2829
2830 clock_get_uptime(&allTime);
2831 AbsoluteTime_to_scalar(&compTime) = 0;
2832 compBytes = 0;
2833
2834 HIBLOG("IOHibernatePollerOpen(), ml_get_interrupts_enabled %d\n", ml_get_interrupts_enabled());
2835 err = IOHibernatePollerOpen(vars->fileVars, kIOPolledAfterSleepState, 0);
2836 HIBLOG("IOHibernatePollerOpen(%x)\n", err);
2837
2838 IOPolledFileSeek(vars->fileVars, gIOHibernateCurrentHeader->image1Size);
2839
2840 // kick off the read ahead
2841 vars->fileVars->io = false;
2842 vars->fileVars->bufferHalf = 0;
2843 vars->fileVars->bufferLimit = 0;
2844 vars->fileVars->lastRead = 0;
2845 vars->fileVars->readEnd = gIOHibernateCurrentHeader->imageSize;
2846 vars->fileVars->bufferOffset = vars->fileVars->bufferLimit;
2847 vars->fileVars->cryptBytes = 0;
2848 AbsoluteTime_to_scalar(&vars->fileVars->cryptTime) = 0;
2849
2850 err = IOPolledFileRead(vars->fileVars, 0, 0, cryptvars);
2851 vars->fileVars->bufferOffset = vars->fileVars->bufferLimit;
2852 // --
2853
2854 HIBLOG("hibernate_machine_init reading\n");
2855
2856 uint32_t * header = (uint32_t *) src;
2857 sum = 0;
2858
2859 while (kIOReturnSuccess == err)
2860 {
2861 unsigned int count;
2862 unsigned int page;
2863 uint32_t tag;
2864 vm_offset_t ppnum, compressedSize;
2865
2866 err = IOPolledFileRead(vars->fileVars, src, 8, cryptvars);
2867 if (kIOReturnSuccess != err)
2868 break;
2869
2870 ppnum = header[0];
2871 count = header[1];
2872
2873 // HIBPRINT("(%x, %x)\n", ppnum, count);
2874
2875 if (!count)
2876 break;
2877
2878 for (page = 0; page < count; page++)
2879 {
2880 err = IOPolledFileRead(vars->fileVars, (uint8_t *) &tag, 4, cryptvars);
2881 if (kIOReturnSuccess != err)
2882 break;
2883
2884 compressedSize = kIOHibernateTagLength & tag;
2885 if (kIOHibernateTagSignature != (tag & ~kIOHibernateTagLength))
2886 {
2887 err = kIOReturnIPCError;
2888 break;
2889 }
2890
2891 if (!compressedSize)
2892 {
2893 ppnum++;
2894 pagesDone++;
2895 continue;
2896 }
2897
2898 err = IOPolledFileRead(vars->fileVars, src, (compressedSize + 3) & ~3, cryptvars);
2899 if (kIOReturnSuccess != err)
2900 break;
2901
2902 if (compressedSize < page_size)
2903 {
2904 decoOffset = page_size;
2905
2906 clock_get_uptime(&startTime);
2907 WKdm_decompress((WK_word*) src, (WK_word*) (src + decoOffset), PAGE_SIZE_IN_WORDS);
2908 clock_get_uptime(&endTime);
2909 ADD_ABSOLUTETIME(&compTime, &endTime);
2910 SUB_ABSOLUTETIME(&compTime, &startTime);
2911
2912 compBytes += page_size;
2913 }
2914 else
2915 decoOffset = 0;
2916
2917 sum += hibernate_sum_page((src + decoOffset), ppnum);
2918
2919 err = IOMemoryDescriptorReadToPhysical(vars->srcBuffer, decoOffset, ptoa_64(ppnum), page_size);
2920 if (err)
2921 {
2922 HIBLOG("IOMemoryDescriptorReadToPhysical [%ld] %x\n", (long)ppnum, err);
2923 break;
2924 }
2925
2926 ppnum++;
2927 pagesDone++;
2928 pagesRead++;
2929
2930 if (0 == (8191 & pagesDone))
2931 {
2932 clock_get_uptime(&endTime);
2933 SUB_ABSOLUTETIME(&endTime, &allTime);
2934 absolutetime_to_nanoseconds(endTime, &nsec);
2935 progressStamp = nsec / 750000000ULL;
2936 if (progressStamp != lastProgressStamp)
2937 {
2938 lastProgressStamp = progressStamp;
2939 HIBPRINT("pages %d (%d%%)\n", pagesDone,
2940 (100 * pagesDone) / gIOHibernateCurrentHeader->pageCount);
2941 }
2942 }
2943 }
2944 }
2945 if ((kIOReturnSuccess == err) && (pagesDone == gIOHibernateCurrentHeader->actualUncompressedPages))
2946 err = kIOReturnLockedRead;
2947
2948 if (kIOReturnSuccess != err)
2949 panic("Hibernate restore error %x", err);
2950
2951 gIOHibernateCurrentHeader->actualImage2Sum = sum;
2952 gIOHibernateCompression = gIOHibernateCurrentHeader->compression;
2953
2954 if (vars->fileVars->io)
2955 (void) IOHibernatePollerIODone(vars->fileVars, false);
2956
2957 err = IOHibernatePollerClose(vars->fileVars, kIOPolledAfterSleepState);
2958
2959 clock_get_uptime(&endTime);
2960
2961 IOService::getPMRootDomain()->pmStatsRecordEvent(
2962 kIOPMStatsHibernateImageRead | kIOPMStatsEventStartFlag, allTime);
2963 IOService::getPMRootDomain()->pmStatsRecordEvent(
2964 kIOPMStatsHibernateImageRead | kIOPMStatsEventStopFlag, endTime);
2965
2966 SUB_ABSOLUTETIME(&endTime, &allTime);
2967 absolutetime_to_nanoseconds(endTime, &nsec);
2968
2969 HIBLOG("hibernate_machine_init pagesDone %d sum2 %x, time: %qd ms, ",
2970 pagesDone, sum, nsec / 1000000ULL);
2971
2972 absolutetime_to_nanoseconds(compTime, &nsec);
2973 HIBLOG("comp bytes: %qd time: %qd ms %qd Mb/s, ",
2974 compBytes,
2975 nsec / 1000000ULL,
2976 nsec ? (((compBytes * 1000000000ULL) / 1024 / 1024) / nsec) : 0);
2977
2978 absolutetime_to_nanoseconds(vars->fileVars->cryptTime, &nsec);
2979 HIBLOG("crypt bytes: %qd time: %qd ms %qd Mb/s\n",
2980 vars->fileVars->cryptBytes,
2981 nsec / 1000000ULL,
2982 nsec ? (((vars->fileVars->cryptBytes * 1000000000ULL) / 1024 / 1024) / nsec) : 0);
2983
2984 KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 2) | DBG_FUNC_NONE, pagesRead, pagesDone, 0, 0, 0);
2985 }
2986
2987 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2988
2989 void IOHibernateSystemRestart(void)
2990 {
2991 static uint8_t noteStore[32] __attribute__((aligned(32)));
2992 IORegistryEntry * regEntry;
2993 const OSSymbol * sym;
2994 OSData * noteProp;
2995 OSData * data;
2996 uintptr_t * smcVars;
2997 uint8_t * smcBytes;
2998 size_t len;
2999 addr64_t element;
3000
3001 data = OSDynamicCast(OSData, IOService::getPMRootDomain()->getProperty(kIOHibernateSMCVariablesKey));
3002 if (!data) return;
3003
3004 smcVars = (typeof(smcVars)) data->getBytesNoCopy();
3005 smcBytes = (typeof(smcBytes)) smcVars[1];
3006 len = smcVars[0];
3007 if (len > sizeof(noteStore)) len = sizeof(noteStore);
3008 noteProp = OSData::withCapacity(3 * sizeof(element));
3009 if (!noteProp) return;
3010 element = len;
3011 noteProp->appendBytes(&element, sizeof(element));
3012 element = crc32(0, smcBytes, len);
3013 noteProp->appendBytes(&element, sizeof(element));
3014
3015 bcopy(smcBytes, noteStore, len);
3016 element = (addr64_t) &noteStore[0];
3017 element = (element & page_mask) | ptoa_64(pmap_find_phys(kernel_pmap, element));
3018 noteProp->appendBytes(&element, sizeof(element));
3019
3020 if (!gIOOptionsEntry)
3021 {
3022 regEntry = IORegistryEntry::fromPath("/options", gIODTPlane);
3023 gIOOptionsEntry = OSDynamicCast(IODTNVRAM, regEntry);
3024 if (regEntry && !gIOOptionsEntry)
3025 regEntry->release();
3026 }
3027
3028 sym = OSSymbol::withCStringNoCopy(kIOHibernateBootNoteKey);
3029 if (gIOOptionsEntry && sym) gIOOptionsEntry->setProperty(sym, noteProp);
3030 if (noteProp) noteProp->release();
3031 if (sym) sym->release();
3032 }
3033
3034
3035