+
+#if defined(__i386__) || defined(__x86_64__)
+IOReturn IOPMrootDomain::restartWithStackshot()
+{
+ if ((swd_flags & SWD_WDOG_ENABLED) == 0)
+ return kIOReturnError;
+
+ takeStackshot(true, true, false);
+
+ return kIOReturnSuccess;
+}
+
+void IOPMrootDomain::sleepWakeDebugTrig(bool wdogTrigger)
+{
+ takeStackshot(wdogTrigger, false, false);
+}
+
+void IOPMrootDomain::takeStackshot(bool wdogTrigger, bool isOSXWatchdog, bool isSpinDump)
+{
+ swd_hdr * hdr = NULL;
+ addr64_t data[3];
+ int wdog_panic = -1;
+ int stress_rack = -1;
+ int cnt = 0;
+ pid_t pid = 0;
+ kern_return_t kr = KERN_SUCCESS;
+ uint32_t flags;
+
+ char * dstAddr;
+ uint32_t size;
+ uint32_t bytesRemaining;
+ unsigned bytesWritten = 0;
+ unsigned totalBytes = 0;
+ unsigned int len;
+ OSString * UUIDstring = NULL;
+ uint64_t code;
+ IOMemoryMap * logBufMap = NULL;
+
+
+ uint32_t bufSize;
+ uint32_t initialStackSize;
+
+ if (isSpinDump) {
+ if (_systemTransitionType != kSystemTransitionSleep &&
+ _systemTransitionType != kSystemTransitionWake)
+ return;
+ } else {
+ if ( kIOSleepWakeWdogOff & gIOKitDebug )
+ return;
+ }
+
+ if (wdogTrigger) {
+ PE_parse_boot_argn("swd_panic", &wdog_panic, sizeof(wdog_panic));
+ PE_parse_boot_argn("stress-rack", &stress_rack, sizeof(stress_rack));
+ if ((wdog_panic == 1) || (stress_rack == 1)) {
+ // If boot-arg specifies to panic then panic.
+ panic("Sleep/Wake hang detected");
+ return;
+ }
+ else if (swd_flags & SWD_BOOT_BY_SW_WDOG) {
+ // If current boot is due to this watch dog trigger restart in previous boot,
+ // then don't trigger again until at least 1 successful sleep & wake.
+ if (!(sleepCnt && (displayWakeCnt || darkWakeCnt))) {
+ IOLog("Shutting down due to repeated Sleep/Wake failures\n");
+ if (!tasksSuspended) {
+ tasksSuspended = TRUE;
+ tasks_system_suspend(true);
+ }
+ PEHaltRestart(kPEHaltCPU);
+ return;
+ }
+ }
+
+ }
+
+ if (isSpinDump) {
+ if (gSpinDumpBufferFull)
+ return;
+ if (swd_spindump_buffer == NULL) {
+ sleepWakeDebugSpinDumpMemAlloc();
+ if (swd_spindump_buffer == NULL) return;
+ }
+
+ bufSize = SWD_SPINDUMP_SIZE;
+ initialStackSize = SWD_INITIAL_SPINDUMP_SIZE;
+ } else {
+ if (sleepWakeDebugIsWdogEnabled() == false)
+ return;
+
+ if (swd_buffer == NULL) {
+ sleepWakeDebugMemAlloc();
+ if (swd_buffer == NULL) return;
+ }
+
+ bufSize = SWD_BUF_SIZE;
+ initialStackSize = SWD_INITIAL_STACK_SIZE;
+ }
+
+ if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock))
+ return;
+
+ if (isSpinDump) {
+ hdr = (swd_hdr *)swd_spindump_buffer;
+ }
+ else {
+ hdr = (swd_hdr *)swd_buffer;
+ }
+
+ memset(hdr->UUID, 0x20, sizeof(hdr->UUID));
+ if ((UUIDstring = OSDynamicCast(OSString, getProperty(kIOPMSleepWakeUUIDKey))) != NULL ) {
+
+ if (wdogTrigger || (!UUIDstring->isEqualTo(hdr->UUID))) {
+ const char *str = UUIDstring->getCStringNoCopy();
+ snprintf(hdr->UUID, sizeof(hdr->UUID), "UUID: %s", str);
+ }
+ else {
+ DLOG("Data for current UUID already exists\n");
+ goto exit;
+ }
+ }
+
+ dstAddr = (char*)hdr + hdr->spindump_offset;
+ bytesRemaining = bufSize - hdr->spindump_offset;
+
+ /* if AppleOSXWatchdog triggered the stackshot, set the flag in the heaer */
+ hdr->is_osx_watchdog = isOSXWatchdog;
+
+ DLOG("Taking snapshot. bytesRemaining: %d\n", bytesRemaining);
+
+ flags = STACKSHOT_KCDATA_FORMAT|STACKSHOT_NO_IO_STATS|STACKSHOT_SAVE_KEXT_LOADINFO;
+ while (kr == KERN_SUCCESS) {
+
+ if (cnt == 0) {
+ /*
+ * Take stackshot of all process on first sample. Size is restricted
+ * to SWD_INITIAL_STACK_SIZE
+ */
+ pid = -1;
+ size = (bytesRemaining > initialStackSize) ? initialStackSize : bytesRemaining;
+ flags |= STACKSHOT_ACTIVE_KERNEL_THREADS_ONLY;
+ }
+ else {
+ /* Take sample of kernel threads only */
+ pid = 0;
+ size = bytesRemaining;
+ }
+
+ kr = stack_snapshot_from_kernel(pid, dstAddr, size, flags, 0, &bytesWritten);
+ DLOG("stack_snapshot_from_kernel returned 0x%x. pid: %d bufsize:0x%x flags:0x%x bytesWritten: %d\n",
+ kr, pid, size, flags, bytesWritten);
+ if (kr == KERN_INSUFFICIENT_BUFFER_SIZE) {
+ if (pid == -1) {
+ // Insufficient buffer when trying to take stackshot of user & kernel space threads.
+ // Continue to take stackshot of just kernel threads
+ ++cnt;
+ kr = KERN_SUCCESS;
+ continue;
+ }
+ else if (totalBytes == 0) {
+ MSG("Failed to get stackshot(0x%x) bufsize:0x%x flags:0x%x\n", kr, size, flags);
+ }
+ }
+
+ dstAddr += bytesWritten;
+ totalBytes += bytesWritten;
+ bytesRemaining -= bytesWritten;
+
+ if (++cnt == 10) {
+ break;
+ }
+ IOSleep(10); // 10 ms
+ }
+
+ hdr->spindump_size = (bufSize - bytesRemaining - hdr->spindump_offset);
+
+
+ memset(hdr->spindump_status, 0x20, sizeof(hdr->spindump_status));
+ code = pmTracer->getPMStatusCode();
+ memset(hdr->PMStatusCode, 0x20, sizeof(hdr->PMStatusCode));
+ snprintf(hdr->PMStatusCode, sizeof(hdr->PMStatusCode), "\nCode: %08x %08x",
+ (uint32_t)((code >> 32) & 0xffffffff), (uint32_t)(code & 0xffffffff));
+ memset(hdr->reason, 0x20, sizeof(hdr->reason));
+ if (isSpinDump) {
+ snprintf(hdr->reason, sizeof(hdr->reason), "\nStackshot reason: PSC Delay\n\n");
+ gRootDomain->swd_lock = 0;
+ gSpinDumpBufferFull = true;
+ return;
+ }
+ snprintf(hdr->reason, sizeof(hdr->reason), "\nStackshot reason: Watchdog\n\n");
+
+
+ data[0] = round_page(sizeof(swd_hdr) + hdr->spindump_size);
+ /* Header & rootdomain log is constantly changing and is not covered by CRC */
+ data[1] = hdr->crc = crc32(0, ((char*)swd_buffer+hdr->spindump_offset), hdr->spindump_size);
+ data[2] = kvtophys((vm_offset_t)swd_buffer);
+ len = sizeof(addr64_t)*3;
+ DLOG("bytes: 0x%llx crc:0x%llx paddr:0x%llx\n",
+ data[0], data[1], data[2]);
+
+ if (PEWriteNVRAMProperty(kIOSleepWakeDebugKey, data, len) == false)
+ {
+ DLOG("Failed to update nvram boot-args\n");
+ goto exit;
+ }
+
+exit:
+
+ gRootDomain->swd_lock = 0;
+
+ if (wdogTrigger) {
+ IOLog("Restarting to collect Sleep wake debug logs\n");
+ if (!tasksSuspended) {
+ tasksSuspended = TRUE;
+ tasks_system_suspend(true);
+ }
+
+ PEHaltRestart(kPERestartCPU);
+ }
+ else {
+ logBufMap = sleepWakeDebugRetrieve();
+ if (logBufMap) {
+ sleepWakeDebugDumpFromMem(logBufMap);
+ logBufMap->release();
+ logBufMap = 0;
+ }
+ }
+}
+
+void IOPMrootDomain::sleepWakeDebugMemAlloc( )
+{
+ vm_size_t size = SWD_BUF_SIZE;
+
+ swd_hdr *hdr = NULL;
+
+ IOBufferMemoryDescriptor *memDesc = NULL;
+
+
+ if ( kIOSleepWakeWdogOff & gIOKitDebug )
+ return;
+
+ if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock))
+ return;
+
+ // Try allocating above 4GB. If that fails, try at 2GB
+ memDesc = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(
+ kernel_task, kIOMemoryPhysicallyContiguous|kIOMemoryMapperNone,
+ size, 0xFFFFFFFF00000000ULL);
+ if (!memDesc) {
+ memDesc = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(
+ kernel_task, kIOMemoryPhysicallyContiguous|kIOMemoryMapperNone,
+ size, 0xFFFFFFFF10000000ULL);
+ }
+
+ if (memDesc == NULL)
+ {
+ DLOG("Failed to allocate Memory descriptor for sleepWake debug\n");
+ goto exit;
+ }
+
+
+ hdr = (swd_hdr *)memDesc->getBytesNoCopy();
+ memset(hdr, 0, sizeof(swd_hdr));
+
+ hdr->signature = SWD_HDR_SIGNATURE;
+ hdr->alloc_size = size;
+
+ hdr->spindump_offset = sizeof(swd_hdr);
+ swd_buffer = (void *)hdr;
+ swd_memDesc = memDesc;
+ DLOG("SleepWake debug buffer size:0x%x spindump offset:0x%x\n", hdr->alloc_size, hdr->spindump_offset);
+
+exit:
+ gRootDomain->swd_lock = 0;
+}
+
+void IOPMrootDomain::sleepWakeDebugSpinDumpMemAlloc( )
+{
+ vm_size_t size = SWD_SPINDUMP_SIZE;
+
+ swd_hdr *hdr = NULL;
+
+ IOBufferMemoryDescriptor *memDesc = NULL;
+
+ if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock))
+ return;
+
+ memDesc = IOBufferMemoryDescriptor::inTaskWithOptions(
+ kernel_task, kIODirectionIn|kIOMemoryMapperNone,
+ SWD_SPINDUMP_SIZE);
+
+ if (memDesc == NULL)
+ {
+ DLOG("Failed to allocate Memory descriptor for sleepWake debug spindump\n");
+ goto exit;
+ }
+
+
+ hdr = (swd_hdr *)memDesc->getBytesNoCopy();
+ memset(hdr, 0, sizeof(swd_hdr));
+
+ hdr->signature = SWD_HDR_SIGNATURE;
+ hdr->alloc_size = size;
+
+ hdr->spindump_offset = sizeof(swd_hdr);
+ swd_spindump_buffer = (void *)hdr;
+
+exit:
+ gRootDomain->swd_lock = 0;
+}
+
+void IOPMrootDomain::sleepWakeDebugEnableWdog()
+{
+ swd_flags |= SWD_WDOG_ENABLED;
+ if (!swd_buffer)
+ sleepWakeDebugMemAlloc();
+}
+
+bool IOPMrootDomain::sleepWakeDebugIsWdogEnabled()
+{
+ return ((swd_flags & SWD_WDOG_ENABLED) &&
+ !systemBooting && !systemShutdown && !gWillShutdown);
+}
+
+void IOPMrootDomain::sleepWakeDebugSaveSpinDumpFile()
+{
+ swd_hdr *hdr = NULL;
+ errno_t error = EIO;
+
+ if (swd_spindump_buffer && gSpinDumpBufferFull) {
+ hdr = (swd_hdr *)swd_spindump_buffer;
+
+ error = sleepWakeDebugSaveFile("/var/tmp/SleepWakeDelayStacks.dump",
+ (char*)hdr+hdr->spindump_offset, hdr->spindump_size);
+
+ if (error) return;
+
+ sleepWakeDebugSaveFile("/var/tmp/SleepWakeDelayLog.dump",
+ (char*)hdr+offsetof(swd_hdr, UUID),
+ sizeof(swd_hdr)-offsetof(swd_hdr, UUID));
+
+ gSpinDumpBufferFull = false;
+ }
+}
+
+errno_t IOPMrootDomain::sleepWakeDebugSaveFile(const char *name, char *buf, int len)
+{
+ struct vnode *vp = NULL;
+ vfs_context_t ctx = vfs_context_create(vfs_context_current());
+ kauth_cred_t cred = vfs_context_ucred(ctx);
+ struct vnode_attr va;
+ errno_t error = EIO;
+
+ if (vnode_open(name, (O_CREAT | FWRITE | O_NOFOLLOW),
+ S_IRUSR|S_IRGRP|S_IROTH, VNODE_LOOKUP_NOFOLLOW, &vp, ctx) != 0)
+ {
+ IOLog("Failed to open the file %s\n", name);
+ swd_flags |= SWD_FILEOP_ERROR;
+ goto exit;
+ }
+ VATTR_INIT(&va);
+ VATTR_WANTED(&va, va_nlink);
+ /* Don't dump to non-regular files or files with links. */
+ if (vp->v_type != VREG ||
+ vnode_getattr(vp, &va, ctx) || va.va_nlink != 1) {
+ IOLog("Bailing as this is not a regular file\n");
+ swd_flags |= SWD_FILEOP_ERROR;
+ goto exit;
+ }
+ VATTR_INIT(&va);
+ VATTR_SET(&va, va_data_size, 0);
+ vnode_setattr(vp, &va, ctx);
+
+
+ if (buf != NULL) {
+ error = vn_rdwr(UIO_WRITE, vp, buf, len, 0,
+ UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, vfs_context_proc(ctx));
+ if (error != 0) {
+ IOLog("Failed to save sleep wake log. err 0x%x\n", error);
+ swd_flags |= SWD_FILEOP_ERROR;
+ }
+ else {
+ DLOG("Saved %d bytes to file %s\n",len, name);
+ }
+ }
+
+exit:
+ if (vp) vnode_close(vp, FWRITE, ctx);
+ if (ctx) vfs_context_rele(ctx);
+
+ return error;
+
+}
+
+errno_t IOPMrootDomain::sleepWakeDebugCopyFile(
+ struct vnode *srcVp,
+ vfs_context_t srcCtx,
+ char *tmpBuf, uint64_t tmpBufSize,
+ uint64_t srcOffset,
+ const char *dstFname,
+ uint64_t numBytes,
+ uint32_t crc)
+{
+ struct vnode *vp = NULL;
+ vfs_context_t ctx = vfs_context_create(vfs_context_current());
+ struct vnode_attr va;
+ errno_t error = EIO;
+ uint64_t bytesToRead, bytesToWrite;
+ uint64_t readFileOffset, writeFileOffset, srcDataOffset;
+ uint32_t newcrc = 0;
+
+ if (vnode_open(dstFname, (O_CREAT | FWRITE | O_NOFOLLOW),
+ S_IRUSR|S_IRGRP|S_IROTH, VNODE_LOOKUP_NOFOLLOW, &vp, ctx) != 0)
+ {
+ IOLog("Failed to open the file %s\n", dstFname);
+ swd_flags |= SWD_FILEOP_ERROR;
+ goto exit;
+ }
+ VATTR_INIT(&va);
+ VATTR_WANTED(&va, va_nlink);
+ /* Don't dump to non-regular files or files with links. */
+ if (vp->v_type != VREG ||
+ vnode_getattr(vp, &va, ctx) || va.va_nlink != 1) {
+ IOLog("Bailing as this is not a regular file\n");
+ swd_flags |= SWD_FILEOP_ERROR;
+ goto exit;
+ }
+ VATTR_INIT(&va);
+ VATTR_SET(&va, va_data_size, 0);
+ vnode_setattr(vp, &va, ctx);
+
+ writeFileOffset = 0;
+ while(numBytes) {
+ bytesToRead = (round_page(numBytes) > tmpBufSize) ? tmpBufSize : round_page(numBytes);
+ readFileOffset = trunc_page(srcOffset);
+
+ DLOG("Read file (numBytes:0x%llx offset:0x%llx)\n", bytesToRead, readFileOffset);
+ error = vn_rdwr(UIO_READ, srcVp, tmpBuf, bytesToRead, readFileOffset,
+ UIO_SYSSPACE, IO_SKIP_ENCRYPTION|IO_SYNC|IO_NODELOCKED|IO_UNIT|IO_NOCACHE,
+ vfs_context_ucred(srcCtx), (int *) 0,
+ vfs_context_proc(srcCtx));
+ if (error) {
+ IOLog("Failed to read file(numBytes:0x%llx)\n", bytesToRead);
+ swd_flags |= SWD_FILEOP_ERROR;
+ break;
+ }
+
+ srcDataOffset = (uint64_t)tmpBuf + (srcOffset - readFileOffset);
+ bytesToWrite = bytesToRead - (srcOffset - readFileOffset);
+ if (bytesToWrite > numBytes) bytesToWrite = numBytes;
+
+ if (crc) {
+ newcrc = crc32(newcrc, (void *)srcDataOffset, bytesToWrite);
+ }
+ DLOG("Write file (numBytes:0x%llx offset:0x%llx)\n", bytesToWrite, writeFileOffset);
+ error = vn_rdwr(UIO_WRITE, vp, (char *)srcDataOffset, bytesToWrite, writeFileOffset,
+ UIO_SYSSPACE, IO_SYNC|IO_NODELOCKED|IO_UNIT,
+ vfs_context_ucred(ctx), (int *) 0,
+ vfs_context_proc(ctx));
+ if (error) {
+ IOLog("Failed to write file(numBytes:0x%llx)\n", bytesToWrite);
+ swd_flags |= SWD_FILEOP_ERROR;
+ break;
+ }
+
+ writeFileOffset += bytesToWrite;
+ numBytes -= bytesToWrite;
+ srcOffset += bytesToWrite;
+
+ }
+ if (crc != newcrc) {
+ /* Set stackshot size to 0 if crc doesn't match */
+ VATTR_INIT(&va);
+ VATTR_SET(&va, va_data_size, 0);
+ vnode_setattr(vp, &va, ctx);
+
+ IOLog("CRC check failed. expected:0x%x actual:0x%x\n", crc, newcrc);
+ swd_flags |= SWD_DATA_CRC_ERROR;
+ error = EFAULT;
+ }
+exit:
+ if (vp) {
+ error = vnode_close(vp, FWRITE, ctx);
+ DLOG("vnode_close on file %s returned 0x%x\n",dstFname, error);
+ }
+ if (ctx) vfs_context_rele(ctx);
+
+ return error;
+
+
+
+}
+uint32_t IOPMrootDomain::checkForValidDebugData(const char *fname, vfs_context_t *ctx,
+ void *tmpBuf, struct vnode **vp)
+{
+ int rc;
+ uint64_t hdrOffset;
+ uint32_t error = 0;
+
+ struct vnode_attr va;
+ IOHibernateImageHeader *imageHdr;
+
+ *vp = NULL;
+ if (vnode_open(fname, (FREAD | O_NOFOLLOW), 0,
+ VNODE_LOOKUP_NOFOLLOW, vp, *ctx) != 0)
+ {
+ DMSG("sleepWakeDebugDumpFromFile: Failed to open the file %s\n", fname);
+ goto err;
+ }
+ VATTR_INIT(&va);
+ VATTR_WANTED(&va, va_nlink);
+ VATTR_WANTED(&va, va_data_alloc);
+ if ((*vp)->v_type != VREG ||
+ vnode_getattr((*vp), &va, *ctx) || va.va_nlink != 1) {
+ IOLog("sleepWakeDebugDumpFromFile: Bailing as %s is not a regular file\n", fname);
+ error = SWD_FILEOP_ERROR;
+ goto err;
+ }
+
+ /* Read the sleepimage file header */
+ rc = vn_rdwr(UIO_READ, *vp, (char *)tmpBuf, round_page(sizeof(IOHibernateImageHeader)), 0,
+ UIO_SYSSPACE, IO_SKIP_ENCRYPTION|IO_SYNC|IO_NODELOCKED|IO_UNIT|IO_NOCACHE,
+ vfs_context_ucred(*ctx), (int *) 0,
+ vfs_context_proc(*ctx));
+ if (rc != 0) {
+ IOLog("sleepWakeDebugDumpFromFile: Failed to read header size %llu(rc=%d) from %s\n",
+ mach_vm_round_page(sizeof(IOHibernateImageHeader)), rc, fname);
+ error = SWD_FILEOP_ERROR;
+ goto err;
+ }
+
+ imageHdr = ((IOHibernateImageHeader *)tmpBuf);
+ if (imageHdr->signature != kIOHibernateHeaderDebugDataSignature) {
+ IOLog("sleepWakeDebugDumpFromFile: File %s header has unexpected value 0x%x\n",
+ fname, imageHdr->signature);
+ error = SWD_HDR_SIGNATURE_ERROR;
+ goto err;
+ }
+
+ /* Sleep/Wake debug header(swd_hdr) is at the beggining of the second block */
+ hdrOffset = imageHdr->deviceBlockSize;
+ if (hdrOffset + sizeof(swd_hdr) >= va.va_data_alloc) {
+ IOLog("sleepWakeDebugDumpFromFile: header is crossing file size(0x%llx) in file %s\n",
+ va.va_data_alloc, fname);
+ error = SWD_HDR_SIZE_ERROR;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ if (*vp) vnode_close(*vp, FREAD, *ctx);
+ *vp = NULL;
+
+ return error;
+}
+
+void IOPMrootDomain::sleepWakeDebugDumpFromFile( )
+{
+#if HIBERNATION
+ int rc;
+ char hibernateFilename[MAXPATHLEN+1];
+ void *tmpBuf;
+ swd_hdr *hdr = NULL;
+ uint32_t stacksSize, logSize;
+ uint64_t tmpBufSize;
+ uint64_t hdrOffset, stacksOffset, logOffset;
+ errno_t error = EIO;
+ OSObject *obj = NULL;
+ OSString *str = NULL;
+ OSNumber *failStat = NULL;
+ struct vnode *vp = NULL;
+ vfs_context_t ctx = NULL;
+ const char *stacksFname, *logFname;
+
+ IOBufferMemoryDescriptor *tmpBufDesc = NULL;
+
+ DLOG("sleepWakeDebugDumpFromFile\n");
+ if ((swd_flags & SWD_LOGS_IN_FILE) == 0)
+ return;
+
+ if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock))
+ return;
+
+
+ /* Allocate a temp buffer to copy data between files */
+ tmpBufSize = 2*4096;
+ tmpBufDesc = IOBufferMemoryDescriptor::
+ inTaskWithOptions(kernel_task, kIODirectionOutIn | kIOMemoryMapperNone,
+ tmpBufSize, PAGE_SIZE);
+
+ if (!tmpBufDesc) {
+ DMSG("sleepWakeDebugDumpFromFile: Fail to allocate temp buf\n");
+ goto exit;
+ }
+
+ tmpBuf = tmpBufDesc->getBytesNoCopy();
+
+ ctx = vfs_context_create(vfs_context_current());
+
+ /* First check if 'kSleepWakeStackBinFilename' has valid data */
+ swd_flags |= checkForValidDebugData(kSleepWakeStackBinFilename, &ctx, tmpBuf, &vp);
+ if (vp == NULL) {
+ /* Check if the debug data is saved to hibernation file */
+ hibernateFilename[0] = 0;
+ if ((obj = copyProperty(kIOHibernateFileKey)))
+ {
+ if ((str = OSDynamicCast(OSString, obj)))
+ strlcpy(hibernateFilename, str->getCStringNoCopy(),
+ sizeof(hibernateFilename));
+ obj->release();
+ }
+ if (!hibernateFilename[0]) {
+ DMSG("sleepWakeDebugDumpFromFile: Failed to get hibernation file name\n");
+ goto exit;
+ }
+
+ swd_flags |= checkForValidDebugData(hibernateFilename, &ctx, tmpBuf, &vp);
+ if (vp == NULL) {
+ DMSG("sleepWakeDebugDumpFromFile: No valid debug data is found\n");
+ goto exit;
+ }
+ DLOG("Getting SW Stacks image from file %s\n", hibernateFilename);
+ }
+ else {
+ DLOG("Getting SW Stacks image from file %s\n", kSleepWakeStackBinFilename);
+ }
+
+ hdrOffset = ((IOHibernateImageHeader *)tmpBuf)->deviceBlockSize;
+
+ DLOG("Reading swd_hdr len 0x%llx offset 0x%lx\n", mach_vm_round_page(sizeof(swd_hdr)), trunc_page(hdrOffset));
+ /* Read the sleep/wake debug header(swd_hdr) */
+ rc = vn_rdwr(UIO_READ, vp, (char *)tmpBuf, round_page(sizeof(swd_hdr)), trunc_page(hdrOffset),
+ UIO_SYSSPACE, IO_SKIP_ENCRYPTION|IO_SYNC|IO_NODELOCKED|IO_UNIT|IO_NOCACHE,
+ vfs_context_ucred(ctx), (int *) 0,
+ vfs_context_proc(ctx));
+ if (rc != 0) {
+ DMSG("sleepWakeDebugDumpFromFile: Failed to debug read header size %llu. rc=%d\n",
+ mach_vm_round_page(sizeof(swd_hdr)), rc);
+ swd_flags |= SWD_FILEOP_ERROR;
+ goto exit;
+ }
+
+ hdr = (swd_hdr *)((char *)tmpBuf + (hdrOffset - trunc_page(hdrOffset)));
+ if ((hdr->signature != SWD_HDR_SIGNATURE) || (hdr->alloc_size > SWD_BUF_SIZE) ||
+ (hdr->spindump_offset > SWD_BUF_SIZE) || (hdr->spindump_size > SWD_BUF_SIZE)) {
+ DMSG("sleepWakeDebugDumpFromFile: Invalid data in debug header. sign:0x%x size:0x%x spindump_offset:0x%x spindump_size:0x%x\n",
+ hdr->signature, hdr->alloc_size, hdr->spindump_offset, hdr->spindump_size);
+ swd_flags |= SWD_BUF_SIZE_ERROR;
+ goto exit;
+ }
+ stacksSize = hdr->spindump_size;
+
+ /* Get stacks & log offsets in the image file */
+ stacksOffset = hdrOffset + hdr->spindump_offset;
+ logOffset = hdrOffset + offsetof(swd_hdr, UUID);
+ logSize = sizeof(swd_hdr)-offsetof(swd_hdr, UUID);
+ stacksFname = getDumpStackFilename(hdr);
+ logFname = getDumpLogFilename(hdr);
+
+ error = sleepWakeDebugCopyFile(vp, ctx, (char *)tmpBuf, tmpBufSize, stacksOffset,
+ stacksFname, stacksSize, hdr->crc);
+ if (error == EFAULT) {
+ DMSG("sleepWakeDebugDumpFromFile: Stackshot CRC doesn't match\n");
+ goto exit;
+ }
+ error = sleepWakeDebugCopyFile(vp, ctx, (char *)tmpBuf, tmpBufSize, logOffset,
+ logFname, logSize, 0);
+ if (error) {
+ DMSG("sleepWakeDebugDumpFromFile: Failed to write the log file(0x%x)\n", error);
+ goto exit;
+ }
+exit:
+ if (error) {
+ // Write just the SleepWakeLog.dump with failure code
+ uint64_t fcode = 0;
+ const char *fname;
+ swd_hdr hdrCopy;
+ char *offset = NULL;
+ int size;
+
+ hdr = &hdrCopy;
+ if (swd_flags & SWD_BOOT_BY_SW_WDOG) {
+ failStat = OSDynamicCast(OSNumber, getProperty(kIOPMSleepWakeFailureCodeKey));
+ fcode = failStat->unsigned64BitValue();
+ fname = kSleepWakeLogFilename;
+ }
+ else {
+ fname = kAppleOSXWatchdogLogFilename;
+ }
+
+ offset = (char*)hdr+offsetof(swd_hdr, UUID);
+ size = sizeof(swd_hdr)-offsetof(swd_hdr, UUID);
+ memset(offset, 0x20, size); // Fill with spaces
+
+
+ snprintf(hdr->spindump_status, sizeof(hdr->spindump_status), "\nstatus: 0x%x", swd_flags);
+ snprintf(hdr->PMStatusCode, sizeof(hdr->PMStatusCode), "\nCode: 0x%llx", fcode);
+ snprintf(hdr->reason, sizeof(hdr->reason), "\nStackshot reason: Watchdog\n\n");
+ sleepWakeDebugSaveFile(fname, offset, size);
+
+ }
+ gRootDomain->swd_lock = 0;
+
+ if (vp) vnode_close(vp, FREAD, ctx);
+ if (ctx) vfs_context_rele(ctx);
+ if (tmpBufDesc) tmpBufDesc->release();
+#endif /* HIBERNATION */
+}
+
+void IOPMrootDomain::sleepWakeDebugDumpFromMem(IOMemoryMap *logBufMap)
+{
+ IOVirtualAddress srcBuf = NULL;
+ char *stackBuf = NULL, *logOffset = NULL;
+ int logSize = 0;
+
+ errno_t error = EIO;
+ uint64_t bufSize = 0;
+ swd_hdr *hdr = NULL;
+ OSNumber *failStat = NULL;
+
+ if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock))
+ return;
+
+ if ((logBufMap == 0) || ( (srcBuf = logBufMap->getVirtualAddress()) == 0) )
+ {
+ DLOG("Nothing saved to dump to file\n");
+ goto exit;
+ }
+
+ hdr = (swd_hdr *)srcBuf;
+ bufSize = logBufMap->getLength();
+ if (bufSize <= sizeof(swd_hdr))
+ {
+ IOLog("SleepWake log buffer size is invalid\n");
+ swd_flags |= SWD_BUF_SIZE_ERROR;
+ goto exit;
+ }
+
+ stackBuf = (char*)hdr+hdr->spindump_offset;
+
+ error = sleepWakeDebugSaveFile(getDumpStackFilename(hdr), stackBuf, hdr->spindump_size);
+ if (error) goto exit;
+
+ logOffset = (char*)hdr+offsetof(swd_hdr, UUID);
+ logSize = sizeof(swd_hdr)-offsetof(swd_hdr, UUID);
+
+ error = sleepWakeDebugSaveFile(getDumpLogFilename(hdr), logOffset, logSize);
+ if (error) goto exit;
+
+ hdr->spindump_size = 0;
+ error = 0;
+
+exit:
+ if (error) {
+ // Write just the SleepWakeLog.dump with failure code
+ uint64_t fcode = 0;
+ const char *sname, *lname;
+ swd_hdr hdrCopy;
+
+ /* Try writing an empty stacks file */
+ hdr = &hdrCopy;
+ if (swd_flags & SWD_BOOT_BY_SW_WDOG) {
+ failStat = OSDynamicCast(OSNumber, getProperty(kIOPMSleepWakeFailureCodeKey));
+ fcode = failStat->unsigned64BitValue();
+ lname = kSleepWakeLogFilename;
+ sname = kSleepWakeStackFilename;
+ }
+ else {
+ lname = kAppleOSXWatchdogLogFilename;
+ sname= kAppleOSXWatchdogStackFilename;
+ }
+
+ sleepWakeDebugSaveFile(sname, NULL, 0);
+
+ logOffset = (char*)hdr+offsetof(swd_hdr, UUID);
+ logSize = sizeof(swd_hdr)-offsetof(swd_hdr, UUID);
+ memset(logOffset, 0x20, logSize); // Fill with spaces
+
+
+ snprintf(hdr->spindump_status, sizeof(hdr->spindump_status), "\nstatus: 0x%x", swd_flags);
+ snprintf(hdr->PMStatusCode, sizeof(hdr->PMStatusCode), "\nCode: 0x%llx", fcode);
+ snprintf(hdr->reason, sizeof(hdr->reason), "\nStackshot reason: Watchdog\n\n");
+ sleepWakeDebugSaveFile(lname, logOffset, logSize);
+ }
+
+ gRootDomain->swd_lock = 0;
+}
+
+IOMemoryMap *IOPMrootDomain::sleepWakeDebugRetrieve( )
+{
+ IOVirtualAddress vaddr = NULL;
+ IOMemoryDescriptor * desc = NULL;
+ IOMemoryMap * logBufMap = NULL;
+
+ uint32_t len = INT_MAX;
+ addr64_t data[3];
+ uint64_t bufSize = 0;
+ uint64_t crc = 0;
+ uint64_t newcrc = 0;
+ uint64_t paddr = 0;
+ swd_hdr *hdr = NULL;
+ bool ret = false;
+ char str[20];
+
+
+ if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock))
+ return NULL;
+
+ if (!PEReadNVRAMProperty(kIOSleepWakeDebugKey, 0, &len)) {
+ DLOG("No sleepWakeDebug note to read\n");
+ goto exit;
+ }
+
+ if (len == strlen("sleepimage")) {
+ str[0] = 0;
+ PEReadNVRAMProperty(kIOSleepWakeDebugKey, str, &len);
+
+ if (!strncmp((char*)str, "sleepimage", strlen("sleepimage"))) {
+ DLOG("sleepWakeDebugRetrieve: in file logs\n");
+ swd_flags |= SWD_LOGS_IN_FILE|SWD_VALID_LOGS;
+ goto exit;
+ }
+ }
+ else if (len == sizeof(addr64_t)*3) {
+ PEReadNVRAMProperty(kIOSleepWakeDebugKey, data, &len);
+ }
+ else {
+ DLOG("Invalid sleepWakeDebug note length(%d)\n", len);
+ goto exit;
+ }
+
+
+
+ DLOG("sleepWakeDebugRetrieve: data[0]:0x%llx data[1]:0x%llx data[2]:0x%llx\n",
+ data[0], data[1], data[2]);
+ DLOG("sleepWakeDebugRetrieve: in mem logs\n");
+ bufSize = data[0];
+ crc = data[1];
+ paddr = data[2];
+ if ( (bufSize <= sizeof(swd_hdr)) ||(bufSize > SWD_BUF_SIZE) || (crc == 0) )
+ {
+ IOLog("SleepWake log buffer size is invalid\n");
+ swd_flags |= SWD_BUF_SIZE_ERROR;
+ return NULL;
+ }
+
+ DLOG("size:0x%llx crc:0x%llx paddr:0x%llx\n",
+ bufSize, crc, paddr);
+
+
+ desc = IOMemoryDescriptor::withAddressRange( paddr, bufSize,
+ kIODirectionOutIn | kIOMemoryMapperNone, NULL);
+ if (desc == NULL)
+ {
+ IOLog("Fail to map SleepWake log buffer\n");
+ swd_flags |= SWD_INTERNAL_FAILURE;
+ goto exit;
+ }
+
+ logBufMap = desc->map();
+
+ vaddr = logBufMap->getVirtualAddress();
+
+
+ if ( (logBufMap->getLength() <= sizeof(swd_hdr)) || (vaddr == NULL) ) {
+ IOLog("Fail to map SleepWake log buffer\n");
+ swd_flags |= SWD_INTERNAL_FAILURE;
+ goto exit;
+ }
+
+ hdr = (swd_hdr *)vaddr;
+ if (hdr->spindump_offset+hdr->spindump_size > bufSize)
+ {
+ IOLog("SleepWake log header size is invalid\n");
+ swd_flags |= SWD_HDR_SIZE_ERROR;
+ goto exit;
+ }
+
+ hdr->crc = crc;
+ newcrc = crc32(0, (void *)((char*)vaddr+hdr->spindump_offset),
+ hdr->spindump_size);
+ if (newcrc != crc) {
+ IOLog("SleepWake log buffer contents are invalid\n");
+ swd_flags |= SWD_DATA_CRC_ERROR;
+ goto exit;
+ }
+
+ ret = true;
+ swd_flags |= SWD_LOGS_IN_MEM | SWD_VALID_LOGS;
+
+
+exit:
+ PERemoveNVRAMProperty(kIOSleepWakeDebugKey);
+ if (!ret) {
+ if (logBufMap) logBufMap->release();
+ logBufMap = 0;
+ }
+ if (desc) desc->release();
+ gRootDomain->swd_lock = 0;
+
+ return logBufMap;
+}
+
+#else
+
+void IOPMrootDomain::sleepWakeDebugTrig(bool restart)
+{
+ uint32_t wdog_panic = 1;
+
+ if (restart) {
+ if (PE_parse_boot_argn("swd_panic", &wdog_panic, sizeof(wdog_panic)) &&
+ (wdog_panic == 0)) {
+ return;
+ }
+ panic("Sleep/Wake hang detected");
+ return;
+ }
+}
+
+void IOPMrootDomain::takeStackshot(bool restart, bool isOSXWatchdog, bool isSpinDump)
+{
+#pragma unused(restart)
+#pragma unused(isOSXWatchdog)
+}
+
+void IOPMrootDomain::sleepWakeDebugMemAlloc( )
+{
+}
+void IOPMrootDomain::sleepWakeDebugDumpFromMem(IOMemoryMap *map)
+{
+}
+errno_t IOPMrootDomain::sleepWakeDebugCopyFile(
+ struct vnode *srcVp,
+ vfs_context_t srcCtx,
+ char *tmpBuf, uint64_t tmpBufSize,
+ uint64_t srcOffset,
+ const char *dstFname,
+ uint64_t numBytes,
+ uint32_t crc)
+{
+ return EIO;
+}
+
+void IOPMrootDomain::sleepWakeDebugDumpFromFile()
+{
+}
+
+IOMemoryMap *IOPMrootDomain::sleepWakeDebugRetrieve( )
+{
+ return NULL;
+}
+
+void IOPMrootDomain::sleepWakeDebugEnableWdog()
+{
+}
+
+bool IOPMrootDomain::sleepWakeDebugIsWdogEnabled()
+{
+ return false;
+}
+
+errno_t IOPMrootDomain::sleepWakeDebugSaveFile(const char *name, char *buf, int len)
+{
+ return 0;
+}
+
+#endif
+