+ IOMemoryDescriptor * mds[3];
+ IOMultiMemoryDescriptor * mmd;
+ IOMemoryMap * map;
+ void * addr;
+ uint8_t * data;
+ uint32_t i;
+ IOAddressRange ranges[2];
+
+ data = (typeof(data))IOMallocAligned(ptoa(8), page_size);
+ for (i = 0; i < ptoa(8); i++) {
+ data[i] = atop(i) | 0xD0;
+ }
+
+ ranges[0].address = (IOVirtualAddress)(data + ptoa(4));
+ ranges[0].length = ptoa(4);
+ ranges[1].address = (IOVirtualAddress)(data + ptoa(0));
+ ranges[1].length = ptoa(4);
+
+ mds[0] = IOMemoryDescriptor::withAddressRanges(&ranges[0], 2, kIODirectionOutIn, kernel_task);
+
+ mds[1] = IOSubMemoryDescriptor::withSubRange(mds[0], ptoa(3), ptoa(2), kIODirectionOutIn);
+ mds[2] = IOSubMemoryDescriptor::withSubRange(mds[0], ptoa(7), ptoa(1), kIODirectionOutIn);
+
+ mmd = IOMultiMemoryDescriptor::withDescriptors(&mds[0], sizeof(mds) / sizeof(mds[0]), kIODirectionOutIn, false);
+ mds[2]->release();
+ mds[1]->release();
+ mds[0]->release();
+ map = mmd->createMappingInTask(kernel_task, 0, kIOMapAnywhere, ptoa(7), mmd->getLength() - ptoa(7));
+ mmd->release();
+ assert(map);
+
+ addr = (void *) map->getVirtualAddress();
+ assert(ptoa(4) == map->getLength());
+ assert(0xd3d3d3d3 == ((uint32_t *)addr)[ptoa(0) / sizeof(uint32_t)]);
+ assert(0xd7d7d7d7 == ((uint32_t *)addr)[ptoa(1) / sizeof(uint32_t)]);
+ assert(0xd0d0d0d0 == ((uint32_t *)addr)[ptoa(2) / sizeof(uint32_t)]);
+ assert(0xd3d3d3d3 == ((uint32_t *)addr)[ptoa(3) / sizeof(uint32_t)]);
+ map->release();
+ IOFreeAligned(data, ptoa(8));
+
+ return 0;
+}
+
+
+
+// <rdar://problem/30102458>
+static int
+IODMACommandForceDoubleBufferTest(int newValue)
+{
+ IOReturn ret;
+ IOBufferMemoryDescriptor * bmd;
+ IODMACommand * dma;
+ uint32_t dir, data;
+ IODMACommand::SegmentOptions segOptions =
+ {
+ .fStructSize = sizeof(segOptions),
+ .fNumAddressBits = 64,
+ .fMaxSegmentSize = 0x2000,
+ .fMaxTransferSize = 128 * 1024,
+ .fAlignment = 1,
+ .fAlignmentLength = 1,
+ .fAlignmentInternalSegments = 1
+ };
+ IODMACommand::Segment64 segments[1];
+ UInt32 numSegments;
+ UInt64 dmaOffset;
+
+
+ for (dir = kIODirectionIn;; dir++) {
+ bmd = IOBufferMemoryDescriptor::inTaskWithOptions(kernel_task,
+ dir | kIOMemoryPageable, ptoa(8));
+ assert(bmd);
+
+ ((uint32_t*) bmd->getBytesNoCopy())[0] = 0x53535300 | dir;
+
+ ret = bmd->prepare((IODirection) dir);
+ assert(kIOReturnSuccess == ret);
+
+ dma = IODMACommand::withSpecification(kIODMACommandOutputHost64, &segOptions,
+ kIODMAMapOptionMapped,
+ NULL, NULL);
+ assert(dma);
+ ret = dma->setMemoryDescriptor(bmd, true);
+ assert(kIOReturnSuccess == ret);
+
+ ret = dma->synchronize(IODMACommand::kForceDoubleBuffer | kIODirectionOut);
+ assert(kIOReturnSuccess == ret);
+
+ dmaOffset = 0;
+ numSegments = 1;
+ ret = dma->gen64IOVMSegments(&dmaOffset, &segments[0], &numSegments);
+ assert(kIOReturnSuccess == ret);
+ assert(1 == numSegments);
+
+ if (kIODirectionOut & dir) {
+ data = ((uint32_t*) bmd->getBytesNoCopy())[0];
+ assertf((0x53535300 | dir) == data, "mismatch 0x%x", data);
+ }
+ if (kIODirectionIn & dir) {
+ IOMappedWrite32(segments[0].fIOVMAddr, 0x11223300 | dir);
+ }
+
+ ret = dma->clearMemoryDescriptor(true);
+ assert(kIOReturnSuccess == ret);
+ dma->release();
+
+ bmd->complete((IODirection) dir);
+
+ if (kIODirectionIn & dir) {
+ data = ((uint32_t*) bmd->getBytesNoCopy())[0];
+ assertf((0x11223300 | dir) == data, "mismatch 0x%x", data);
+ }
+
+ bmd->release();
+
+ if (dir == kIODirectionInOut) {
+ break;
+ }
+ }
+
+ return 0;
+}
+
+// <rdar://problem/34322778>
+static int __unused
+IODMACommandLocalMappedNonContig(int newValue)
+{
+ IOReturn kr;
+ IOMemoryDescriptor * md;
+ IODMACommand * dma;
+ OSDictionary * matching;
+ IOService * device;
+ IOMapper * mapper;
+ IODMACommand::SegmentOptions segOptions =
+ {
+ .fStructSize = sizeof(segOptions),
+ .fNumAddressBits = 64,
+ .fMaxSegmentSize = 128 * 1024,
+ .fMaxTransferSize = 128 * 1024,
+ .fAlignment = 1,
+ .fAlignmentLength = 1,
+ .fAlignmentInternalSegments = 1
+ };
+ IODMACommand::Segment64 segments[1];
+ UInt32 numSegments;
+ UInt64 dmaOffset;
+ UInt64 segPhys;
+ vm_address_t buffer;
+ vm_size_t bufSize = ptoa(4);
+
+ if (!IOMapper::gSystem) {
+ return 0;
+ }
+
+ buffer = 0;
+ kr = vm_allocate_kernel(kernel_map, &buffer, bufSize, VM_FLAGS_ANYWHERE, VM_KERN_MEMORY_IOKIT);
+ assert(KERN_SUCCESS == kr);
+
+ // fragment the vmentries
+ kr = vm_inherit(kernel_map, buffer + ptoa(1), ptoa(1), VM_INHERIT_NONE);
+ assert(KERN_SUCCESS == kr);
+
+ md = IOMemoryDescriptor::withAddressRange(
+ buffer + 0xa00, 0x2000, kIODirectionOutIn, kernel_task);
+ assert(md);
+ kr = md->prepare(kIODirectionOutIn);
+ assert(kIOReturnSuccess == kr);
+
+ segPhys = md->getPhysicalSegment(0, NULL, 0);
+
+ matching = IOService::nameMatching("XHC1");
+ assert(matching);
+ device = IOService::copyMatchingService(matching);
+ matching->release();
+ mapper = device ? IOMapper::copyMapperForDeviceWithIndex(device, 0) : NULL;
+
+ dma = IODMACommand::withSpecification(kIODMACommandOutputHost64, &segOptions,
+ kIODMAMapOptionMapped,
+ mapper, NULL);
+ assert(dma);
+ kr = dma->setMemoryDescriptor(md, true);
+ assert(kIOReturnSuccess == kr);
+
+ dmaOffset = 0;
+ numSegments = 1;
+ kr = dma->gen64IOVMSegments(&dmaOffset, &segments[0], &numSegments);
+ assert(kIOReturnSuccess == kr);
+ assert(1 == numSegments);
+
+ if (mapper) {
+ assertf(segments[0].fIOVMAddr != segPhys, "phys !local 0x%qx, 0x%qx, %p", segments[0].fIOVMAddr, segPhys, dma);
+ }
+
+ kr = dma->clearMemoryDescriptor(true);
+ assert(kIOReturnSuccess == kr);
+ dma->release();
+
+ kr = md->complete(kIODirectionOutIn);
+ assert(kIOReturnSuccess == kr);
+ md->release();
+
+ kr = vm_deallocate(kernel_map, buffer, bufSize);
+ assert(KERN_SUCCESS == kr);
+ OSSafeReleaseNULL(mapper);
+
+ return 0;
+}
+
+// <rdar://problem/30102458>
+static int
+IOMemoryRemoteTest(int newValue)
+{
+ IOReturn ret;
+ IOMemoryDescriptor * md;
+ IOByteCount offset, length;
+ addr64_t addr;
+ uint32_t idx;
+
+ IODMACommand * dma;
+ IODMACommand::SegmentOptions segOptions =
+ {
+ .fStructSize = sizeof(segOptions),
+ .fNumAddressBits = 64,
+ .fMaxSegmentSize = 0x2000,
+ .fMaxTransferSize = 128 * 1024,
+ .fAlignment = 1,
+ .fAlignmentLength = 1,
+ .fAlignmentInternalSegments = 1
+ };
+ IODMACommand::Segment64 segments[1];
+ UInt32 numSegments;
+ UInt64 dmaOffset;
+
+ IOAddressRange ranges[2] = {
+ { 0x1234567890123456ULL, 0x1000 }, { 0x5432109876543210, 0x2000 },
+ };
+
+ md = IOMemoryDescriptor::withAddressRanges(&ranges[0], 2, kIODirectionOutIn | kIOMemoryRemote, TASK_NULL);
+ assert(md);
+
+// md->map();
+// md->readBytes(0, &idx, sizeof(idx));
+
+ ret = md->prepare(kIODirectionOutIn);
+ assert(kIOReturnSuccess == ret);
+
+ printf("remote md flags 0x%qx, r %d\n",
+ md->getFlags(), (0 != (kIOMemoryRemote & md->getFlags())));
+
+ for (offset = 0, idx = 0; true; offset += length, idx++) {
+ addr = md->getPhysicalSegment(offset, &length, 0);
+ if (!length) {
+ break;
+ }
+ assert(idx < 2);
+ assert(addr == ranges[idx].address);
+ assert(length == ranges[idx].length);
+ }
+ assert(offset == md->getLength());
+
+ dma = IODMACommand::withSpecification(kIODMACommandOutputHost64, &segOptions,
+ kIODMAMapOptionUnmapped | kIODMAMapOptionIterateOnly,
+ NULL, NULL);
+ assert(dma);
+ ret = dma->setMemoryDescriptor(md, true);
+ assert(kIOReturnSuccess == ret);
+
+ for (dmaOffset = 0, idx = 0; dmaOffset < md->getLength(); idx++) {
+ numSegments = 1;
+ ret = dma->gen64IOVMSegments(&dmaOffset, &segments[0], &numSegments);
+ assert(kIOReturnSuccess == ret);
+ assert(1 == numSegments);
+ assert(idx < 2);
+ assert(segments[0].fIOVMAddr == ranges[idx].address);
+ assert(segments[0].fLength == ranges[idx].length);
+ }
+ assert(dmaOffset == md->getLength());
+
+ ret = dma->clearMemoryDescriptor(true);
+ assert(kIOReturnSuccess == ret);
+ dma->release();
+ md->complete(kIODirectionOutIn);
+ md->release();
+
+ return 0;