+ uint64_t headerPhys;
+ uint64_t mapPhys;
+ uint64_t srcPhys;
+ uint64_t imageReadPhys;
+ uint64_t pageIndexPhys;
+ uint32_t * pageIndexSource;
+ hibernate_page_list_t * map;
+ pal_hib_restore_stage_t stage;
+ uint32_t count;
+ uint32_t ppnum;
+ uint32_t page;
+ uint32_t conflictCount;
+ uint32_t compressedSize;
+ uint32_t uncompressedPages;
+ uint32_t * src;
+ uint32_t sum;
+ uint32_t pageSum;
+ uint32_t nextFree;
+ uint32_t lastImagePage;
+ uint32_t lastMapPage;
+ uint32_t lastPageIndexPage;
+ uint32_t handoffPages;
+ uint32_t handoffPageCount;
+ uint8_t * wkdmScratch;
+ hibernate_scratch_t conflictList;
+ pal_hib_ctx_t palHibCtx;
+
+ uint64_t timeStart;
+ timeStart = rdtsc64();
+
+#if !defined(__arm64__)
+ static_assert(sizeof(IOHibernateImageHeader) == 512);
+#endif /* !defined(__arm64__) */
+
+ headerPhys = ptoa_64(p1);
+
+ if ((kIOHibernateDebugRestoreLogs & gIOHibernateDebugFlags) && !debug_probe()) {
+ gIOHibernateDebugFlags &= ~kIOHibernateDebugRestoreLogs;
+ }
+
+ debug_code(kIOHibernateRestoreCodeImageStart, headerPhys);
+
+ __nosan_memcpy(gIOHibernateCurrentHeader,
+ (void *) pal_hib_map(IMAGE_AREA, headerPhys),
+ sizeof(IOHibernateImageHeader));
+
+ debug_code(kIOHibernateRestoreCodeSignature, gIOHibernateCurrentHeader->signature);
+
+ mapPhys = headerPhys
+ + (offsetof(IOHibernateImageHeader, fileExtentMap)
+ + gIOHibernateCurrentHeader->fileExtentMapSize
+ + ptoa_32(gIOHibernateCurrentHeader->restore1PageCount)
+ + gIOHibernateCurrentHeader->previewSize);
+
+ map = (hibernate_page_list_t *) pal_hib_map(BITMAP_AREA, mapPhys);
+
+
+ // make the rest of the image is safe for atop()
+ uint64_t imageEnd;
+ if (os_add_overflow(headerPhys, gIOHibernateCurrentHeader->image1Size, &imageEnd) || (imageEnd > IO_MAX_PAGE_ADDR)) {
+ HIB_ASSERT(0);
+ }
+
+ lastImagePage = atop_64_ppnum(HIB_ROUND_PAGE(headerPhys + gIOHibernateCurrentHeader->image1Size));
+ lastMapPage = atop_64_ppnum(HIB_ROUND_PAGE(mapPhys + gIOHibernateCurrentHeader->bitmapSize));
+
+ handoffPages = gIOHibernateCurrentHeader->handoffPages;
+ handoffPageCount = gIOHibernateCurrentHeader->handoffPageCount;
+
+ debug_code(kIOHibernateRestoreCodeImageEnd, ptoa_64(lastImagePage));
+ debug_code(kIOHibernateRestoreCodeMapStart, mapPhys);
+ debug_code(kIOHibernateRestoreCodeMapEnd, ptoa_64(lastMapPage));
+
+ debug_code(kIOHibernateRestoreCodeMapVirt, (uintptr_t) map);
+ debug_code(kIOHibernateRestoreCodeHandoffPages, ptoa_64(handoffPages));
+ debug_code(kIOHibernateRestoreCodeHandoffCount, handoffPageCount);
+
+#if defined(__arm64__)
+ // on arm64 we've already done this in pal_hib_resume_tramp
+#else /* !defined(__arm64__) */
+ hibernate_reserve_restore_pages(headerPhys, gIOHibernateCurrentHeader, map);
+#endif /* !defined(__arm64__) */
+
+ nextFree = 0;
+ hibernate_page_list_grab(map, &nextFree);
+
+ pal_hib_resume_init(&palHibCtx, map, &nextFree);
+
+ // allocate scratch space for wkdm
+ wkdmScratch = (uint8_t *)pal_hib_map(WKDM_AREA, ptoa_64(hibernate_page_list_grab(map, &nextFree)));
+
+ sum = gIOHibernateCurrentHeader->actualRestore1Sum;
+ gIOHibernateCurrentHeader->diag[0] = atop_64_ppnum(headerPhys);
+ gIOHibernateCurrentHeader->diag[1] = sum;
+ gIOHibernateCurrentHeader->trampolineTime = 0;
+
+ uncompressedPages = 0;
+ conflictCount = 0;
+
+ compressedSize = PAGE_SIZE;
+ stage = pal_hib_restore_stage_handoff_data;
+ count = 0;
+ srcPhys = 0;
+
+ if (gIOHibernateCurrentHeader->previewSize) {
+ pageIndexPhys = headerPhys
+ + (offsetof(IOHibernateImageHeader, fileExtentMap)
+ + gIOHibernateCurrentHeader->fileExtentMapSize
+ + ptoa_32(gIOHibernateCurrentHeader->restore1PageCount));
+ imageReadPhys = (pageIndexPhys + gIOHibernateCurrentHeader->previewPageListSize);
+ lastPageIndexPage = atop_64_ppnum(HIB_ROUND_PAGE(imageReadPhys));
+ pageIndexSource = (uint32_t *) pal_hib_map(IMAGE2_AREA, pageIndexPhys);
+ } else {
+ pageIndexPhys = 0;
+ lastPageIndexPage = 0;
+ imageReadPhys = (mapPhys + gIOHibernateCurrentHeader->bitmapSize);
+ }
+
+ debug_code(kIOHibernateRestoreCodePageIndexStart, pageIndexPhys);
+ debug_code(kIOHibernateRestoreCodePageIndexEnd, ptoa_64(lastPageIndexPage));
+
+ while (1) {
+ switch (stage) {
+ case pal_hib_restore_stage_handoff_data:
+ // copy handoff data
+ count = srcPhys ? 0 : handoffPageCount;
+ if (!count) {
+ break;
+ }
+ if (count > gIOHibernateHandoffPageCount) {
+ count = gIOHibernateHandoffPageCount;
+ }
+ srcPhys = ptoa_64(handoffPages);
+ break;
+
+ case pal_hib_restore_stage_preview_pages:
+ // copy pageIndexSource pages == preview image data
+ if (!srcPhys) {
+ if (!pageIndexPhys) {
+ break;
+ }
+ srcPhys = imageReadPhys;
+ }
+ ppnum = pageIndexSource[0];
+ count = pageIndexSource[1];
+ pageIndexSource += 2;
+ pageIndexPhys += 2 * sizeof(pageIndexSource[0]);
+ imageReadPhys = srcPhys;
+ break;
+
+ case pal_hib_restore_stage_dram_pages:
+ // copy pages
+ if (!srcPhys) {
+ srcPhys = (mapPhys + gIOHibernateCurrentHeader->bitmapSize);
+ }
+ src = (uint32_t *) pal_hib_map(IMAGE_AREA, srcPhys);
+ ppnum = src[0];
+ count = src[1];
+ srcPhys += 2 * sizeof(*src);
+ imageReadPhys = srcPhys;
+ break;
+ }
+
+
+ if (!count) {
+ if (stage == pal_hib_restore_stage_dram_pages) {
+ break;
+ }
+ stage--;
+ srcPhys = 0;
+ continue;
+ }
+
+ for (page = 0; page < count; page++, ppnum++) {
+ uint32_t tag;
+ int conflicts;
+
+ src = (uint32_t *) pal_hib_map(IMAGE_AREA, srcPhys);
+
+ if (stage == pal_hib_restore_stage_handoff_data) {
+ ppnum = gIOHibernateHandoffPages[page];
+ } else if (stage == pal_hib_restore_stage_dram_pages) {
+ tag = *src++;
+ HIB_ASSERT((tag & ~kIOHibernateTagLength) == kIOHibernateTagSignature);
+// debug_code(kIOHibernateRestoreCodeTag, (uintptr_t) tag);
+ srcPhys += sizeof(*src);
+ compressedSize = kIOHibernateTagLength & tag;
+ HIB_ASSERT(compressedSize <= PAGE_SIZE);
+ }
+
+ conflicts = (ppnum >= atop_64_ppnum(mapPhys)) && (ppnum <= lastMapPage);
+
+ conflicts |= ((ppnum >= atop_64_ppnum(imageReadPhys)) && (ppnum <= lastImagePage));
+
+ if (stage >= pal_hib_restore_stage_handoff_data) {
+ conflicts |= ((ppnum >= atop_64_ppnum(srcPhys)) && (ppnum <= (handoffPages + handoffPageCount - 1)));
+ }
+
+ if (stage >= pal_hib_restore_stage_preview_pages) {
+ conflicts |= ((ppnum >= atop_64_ppnum(pageIndexPhys)) && (ppnum <= lastPageIndexPage));
+ }
+
+ if (!conflicts) {
+ pageSum = store_one_page(gIOHibernateCurrentHeader->processorFlags,
+ src, compressedSize, wkdmScratch, ppnum);
+ if (stage != pal_hib_restore_stage_handoff_data) {
+ sum += pageSum;
+ }
+ uncompressedPages++;
+ } else {
+// debug_code(kIOHibernateRestoreCodeConflictPage, ppnum);
+// debug_code(kIOHibernateRestoreCodeConflictSource, (uintptr_t) src);
+ conflictCount++;
+ if (!conflictList.headPage) {
+ hibernate_scratch_init(&conflictList, map, &nextFree);
+ }
+ hibernate_scratch_write(&conflictList, &ppnum, sizeof(ppnum));
+ hibernate_scratch_write(&conflictList, &compressedSize, sizeof(compressedSize));
+ hibernate_scratch_write(&conflictList, &stage, sizeof(stage));
+ hibernate_scratch_write(&conflictList, src, compressedSize);
+ }
+ srcPhys += ((compressedSize + 3) & ~3);
+ src += ((compressedSize + 3) >> 2);
+ pal_hib_restored_page(&palHibCtx, stage, ppnum);
+ }
+ }
+
+ /* src points to the last page restored, so we need to skip over that */
+ pal_hib_restore_pal_state(src);
+
+ // -- copy back conflicts
+
+ if (conflictCount) {
+ src = (uint32_t *)pal_hib_map(COPY_PAGE_AREA, ptoa_64(hibernate_page_list_grab(map, &nextFree)));
+ hibernate_scratch_start_read(&conflictList);
+ for (uint32_t i = 0; i < conflictCount; i++) {
+ hibernate_scratch_read(&conflictList, &ppnum, sizeof(ppnum));
+ hibernate_scratch_read(&conflictList, &compressedSize, sizeof(compressedSize));
+ hibernate_scratch_read(&conflictList, &stage, sizeof(stage));
+ HIB_ASSERT(compressedSize <= PAGE_SIZE);
+ hibernate_scratch_read(&conflictList, src, compressedSize);
+ pageSum = store_one_page(gIOHibernateCurrentHeader->processorFlags,
+ src, compressedSize, wkdmScratch, ppnum);
+ if (stage != pal_hib_restore_stage_handoff_data) {
+ sum += pageSum;
+ }
+ uncompressedPages++;
+ }
+ }
+
+ pal_hib_patchup(&palHibCtx);