+ uint64_t dst = ptoa_64(ppnum);
+ uint8_t scratch[WKdm_SCRATCH_BUF_SIZE_INTERNAL] __attribute__ ((aligned(16)));
+
+ if (compressedSize != PAGE_SIZE) {
+ dst = pal_hib_map(DEST_COPY_AREA, dst);
+ if (compressedSize != 4) {
+ WKdm_decompress_new((WK_word*) src, (WK_word*)(uintptr_t)dst, (WK_word*) &scratch[0], compressedSize);
+ } else {
+ size_t i;
+ uint32_t s, *d;
+
+ s = *src;
+ d = (uint32_t *)(uintptr_t)dst;
+ if (!s) {
+ __nosan_bzero((void *) dst, PAGE_SIZE);
+ } else {
+ for (i = 0; i < (PAGE_SIZE / sizeof(int32_t)); i++) {
+ *d++ = s;
+ }
+ }
+ }
+ } else {
+ dst = hibernate_restore_phys_page((uint64_t) (uintptr_t) src, dst, PAGE_SIZE, procFlags);
+ }
+
+ return hibernate_sum_page((uint8_t *)(uintptr_t)dst, ppnum);
+}
+
+long
+hibernate_kernel_entrypoint(uint32_t p1,
+ uint32_t p2, uint32_t p3, uint32_t p4)
+{
+ uint64_t headerPhys;
+ uint64_t mapPhys;
+ uint64_t srcPhys;
+ uint64_t imageReadPhys;
+ uint64_t pageIndexPhys;
+ uint32_t * pageIndexSource;
+ hibernate_page_list_t * map;
+ uint32_t stage;
+ uint32_t count;
+ uint32_t ppnum;
+ uint32_t page;
+ uint32_t conflictCount;
+ uint32_t compressedSize;
+ uint32_t uncompressedPages;
+ uint32_t copyPageListHeadPage;
+ uint32_t pageListPage;
+ uint32_t * copyPageList;
+ uint32_t * src;
+ uint32_t copyPageIndex;
+ uint32_t sum;
+ uint32_t pageSum;
+ uint32_t nextFree;
+ uint32_t lastImagePage;
+ uint32_t lastMapPage;
+ uint32_t lastPageIndexPage;
+ uint32_t handoffPages;
+ uint32_t handoffPageCount;
+
+ uint64_t timeStart;
+ timeStart = rdtsc64();
+
+ static_assert(sizeof(IOHibernateImageHeader) == 512);
+
+ 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);
+
+ lastImagePage = atop_64(headerPhys + gIOHibernateCurrentHeader->image1Size);
+ lastMapPage = atop_64(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);
+
+ // knock all the image pages to be used out of free map
+ for (ppnum = atop_64(headerPhys); ppnum <= lastImagePage; ppnum++) {
+ hibernate_page_bitset(map, FALSE, ppnum);
+ }
+ // knock all the handoff pages to be used out of free map
+ for (ppnum = handoffPages; ppnum < (handoffPages + handoffPageCount); ppnum++) {
+ hibernate_page_bitset(map, FALSE, ppnum);
+ }
+
+ nextFree = 0;
+ hibernate_page_list_grab(map, &nextFree);
+
+ sum = gIOHibernateCurrentHeader->actualRestore1Sum;
+ gIOHibernateCurrentHeader->diag[0] = atop_64(headerPhys);
+ gIOHibernateCurrentHeader->diag[1] = sum;
+ gIOHibernateCurrentHeader->trampolineTime = 0;
+
+ uncompressedPages = 0;
+ conflictCount = 0;
+ copyPageListHeadPage = 0;
+ copyPageList = 0;
+ copyPageIndex = PAGE_SIZE >> 2;
+
+ compressedSize = PAGE_SIZE;
+ stage = 2;
+ 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(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 2:
+ // copy handoff data
+ count = srcPhys ? 0 : handoffPageCount;
+ if (!count) {
+ break;
+ }
+ if (count > gIOHibernateHandoffPageCount) {
+ count = gIOHibernateHandoffPageCount;
+ }
+ srcPhys = ptoa_64(handoffPages);
+ break;
+
+ case 1:
+ // 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 0:
+ // 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) {
+ 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 (2 == stage) {
+ ppnum = gIOHibernateHandoffPages[page];
+ } else if (!stage) {
+ tag = *src++;
+// debug_code(kIOHibernateRestoreCodeTag, (uintptr_t) tag);
+ srcPhys += sizeof(*src);
+ compressedSize = kIOHibernateTagLength & tag;
+ }
+
+ conflicts = (ppnum >= atop_64(mapPhys)) && (ppnum <= lastMapPage);
+
+ conflicts |= ((ppnum >= atop_64(imageReadPhys)) && (ppnum <= lastImagePage));
+
+ if (stage >= 2) {
+ conflicts |= ((ppnum >= atop_64(srcPhys)) && (ppnum <= (handoffPages + handoffPageCount - 1)));
+ }
+
+ if (stage >= 1) {
+ conflicts |= ((ppnum >= atop_64(pageIndexPhys)) && (ppnum <= lastPageIndexPage));
+ }
+
+ if (!conflicts) {
+ pageSum = store_one_page(gIOHibernateCurrentHeader->processorFlags,
+ src, compressedSize, 0, ppnum);
+ if (stage != 2) {
+ sum += pageSum;
+ }
+ uncompressedPages++;
+ } else {
+ uint32_t bufferPage = 0;
+ uint32_t * dst;
+
+// debug_code(kIOHibernateRestoreCodeConflictPage, ppnum);
+// debug_code(kIOHibernateRestoreCodeConflictSource, (uintptr_t) src);
+ conflictCount++;
+ if (compressedSize) {
+ // alloc new buffer page
+ bufferPage = hibernate_page_list_grab(map, &nextFree);
+ dst = (uint32_t *)pal_hib_map(DEST_COPY_AREA, ptoa_64(bufferPage));
+ __nosan_memcpy(dst, src, compressedSize);
+ }
+ if (copyPageIndex > ((PAGE_SIZE >> 2) - 3)) {
+ // alloc new copy list page
+ pageListPage = hibernate_page_list_grab(map, &nextFree);
+ // link to current
+ if (copyPageList) {
+ copyPageList[1] = pageListPage;
+ } else {
+ copyPageListHeadPage = pageListPage;
+ }
+ copyPageList = (uint32_t *)pal_hib_map(SRC_COPY_AREA,
+ ptoa_64(pageListPage));
+ copyPageList[1] = 0;
+ copyPageIndex = 2;
+ }
+ copyPageList[copyPageIndex++] = ppnum;
+ copyPageList[copyPageIndex++] = bufferPage;
+ copyPageList[copyPageIndex++] = (compressedSize | (stage << 24));
+ copyPageList[0] = copyPageIndex;
+ }
+ srcPhys += ((compressedSize + 3) & ~3);
+ src += ((compressedSize + 3) >> 2);
+ }
+ }
+
+ /* src points to the last page restored, so we need to skip over that */
+ hibernateRestorePALState(src);
+
+ // -- copy back conflicts
+
+ pageListPage = copyPageListHeadPage;
+ while (pageListPage) {
+ copyPageList = (uint32_t *)pal_hib_map(COPY_PAGE_AREA, ptoa_64(pageListPage));
+ for (copyPageIndex = 2; copyPageIndex < copyPageList[0]; copyPageIndex += 3) {
+ ppnum = copyPageList[copyPageIndex + 0];
+ srcPhys = ptoa_64(copyPageList[copyPageIndex + 1]);
+ src = (uint32_t *) pal_hib_map(SRC_COPY_AREA, srcPhys);
+ compressedSize = copyPageList[copyPageIndex + 2];
+ stage = compressedSize >> 24;
+ compressedSize &= 0x1FFF;
+ pageSum = store_one_page(gIOHibernateCurrentHeader->processorFlags,
+ src, compressedSize, 0, ppnum);
+ if (stage != 2) {
+ sum += pageSum;
+ }
+ uncompressedPages++;
+ }
+ pageListPage = copyPageList[1];
+ }
+
+ pal_hib_patchup();
+
+ // -- image has been destroyed...