/*
- * Copyright (c) 2014 Apple Inc. All rights reserved.
+ * Copyright (c) 2014-2016 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <IOKit/IOMapper.h>
#include <IOKit/IODMACommand.h>
#include <IOKit/IOKitKeysPrivate.h>
+#include "Tests.h"
#ifndef __LP64__
#include <IOKit/IOSubMemoryDescriptor.h>
#if DEVELOPMENT || DEBUG
+extern SInt32 gIOMemoryReferenceCount;
+
static int IOMultMemoryDescriptorTest(int newValue)
{
IOMemoryDescriptor * mds[3];
}
+
+// <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/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);
+}
+
+static IOReturn
+IOMemoryPrefaultTest(uint32_t options)
+{
+ IOBufferMemoryDescriptor * bmd;
+ IOMemoryMap * map;
+ IOReturn kr;
+ uint32_t data;
+ uint32_t * p;
+ IOSimpleLock * lock;
+
+ lock = IOSimpleLockAlloc();
+ assert(lock);
+
+ bmd = IOBufferMemoryDescriptor::inTaskWithOptions(current_task(),
+ kIODirectionOutIn | kIOMemoryPageable, ptoa(8));
+ assert(bmd);
+ kr = bmd->prepare();
+ assert(KERN_SUCCESS == kr);
+
+ map = bmd->map(kIOMapPrefault);
+ assert(map);
+
+ p = (typeof(p)) map->getVirtualAddress();
+ IOSimpleLockLock(lock);
+ data = p[0];
+ IOSimpleLockUnlock(lock);
+
+ IOLog("IOMemoryPrefaultTest %d\n", data);
+
+ map->release();
+ bmd->release();
+ IOSimpleLockFree(lock);
+
+ return (kIOReturnSuccess);
+}
+
+
+// <rdar://problem/26375234>
+static IOReturn
+ZeroLengthTest(int newValue)
+{
+ IOMemoryDescriptor * md;
+
+ md = IOMemoryDescriptor::withAddressRange(
+ 0, 0, kIODirectionNone, current_task());
+ assert(md);
+ md->prepare();
+ md->complete();
+ md->release();
+ return (0);
+}
+
+// <rdar://problem/27002624>
+static IOReturn
+BadFixedAllocTest(int newValue)
+{
+ IOBufferMemoryDescriptor * bmd;
+ IOMemoryMap * map;
+
+ bmd = IOBufferMemoryDescriptor::inTaskWithOptions(NULL,
+ kIODirectionIn | kIOMemoryPageable, ptoa(1));
+ assert(bmd);
+ map = bmd->createMappingInTask(kernel_task, 0x2000, 0);
+ assert(!map);
+
+ bmd->release();
+ return (0);
+}
+
+// <rdar://problem/26466423>
+static IOReturn
+IODirectionPrepareNoZeroFillTest(int newValue)
+{
+ IOBufferMemoryDescriptor * bmd;
+
+ bmd = IOBufferMemoryDescriptor::inTaskWithOptions(NULL,
+ kIODirectionIn | kIOMemoryPageable, ptoa(24));
+ assert(bmd);
+ bmd->prepare((IODirection)(kIODirectionIn | kIODirectionPrepareNoZeroFill));
+ bmd->prepare(kIODirectionIn);
+ bmd->complete((IODirection)(kIODirectionIn | kIODirectionCompleteWithDataValid));
+ bmd->complete(kIODirectionIn);
+ bmd->release();
+ return (0);
+}
+
+// <rdar://problem/28190483>
+static IOReturn
+IOMemoryMapTest(uint32_t options)
+{
+ IOBufferMemoryDescriptor * bmd;
+ IOMemoryDescriptor * md;
+ IOMemoryMap * map;
+ uint32_t data;
+ user_addr_t p;
+ uint8_t * p2;
+ int r;
+ uint64_t time, nano;
+
+ bmd = IOBufferMemoryDescriptor::inTaskWithOptions(current_task(),
+ kIODirectionOutIn | kIOMemoryPageable, 0x4018+0x800);
+ assert(bmd);
+ p = (typeof(p)) bmd->getBytesNoCopy();
+ p += 0x800;
+ data = 0x11111111;
+ r = copyout(&data, p, sizeof(data));
+ assert(r == 0);
+ data = 0x22222222;
+ r = copyout(&data, p + 0x1000, sizeof(data));
+ assert(r == 0);
+ data = 0x33333333;
+ r = copyout(&data, p + 0x2000, sizeof(data));
+ assert(r == 0);
+ data = 0x44444444;
+ r = copyout(&data, p + 0x3000, sizeof(data));
+ assert(r == 0);
+
+ md = IOMemoryDescriptor::withAddressRange(p, 0x4018,
+ kIODirectionOut | options,
+ current_task());
+ assert(md);
+ time = mach_absolute_time();
+ map = md->map(kIOMapReadOnly);
+ time = mach_absolute_time() - time;
+ assert(map);
+ absolutetime_to_nanoseconds(time, &nano);
+
+ p2 = (typeof(p2)) map->getVirtualAddress();
+ assert(0x11 == p2[0]);
+ assert(0x22 == p2[0x1000]);
+ assert(0x33 == p2[0x2000]);
+ assert(0x44 == p2[0x3000]);
+
+ data = 0x99999999;
+ r = copyout(&data, p + 0x2000, sizeof(data));
+ assert(r == 0);
+
+ assert(0x11 == p2[0]);
+ assert(0x22 == p2[0x1000]);
+ assert(0x44 == p2[0x3000]);
+ if (kIOMemoryMapCopyOnWrite & options) assert(0x33 == p2[0x2000]);
+ else assert(0x99 == p2[0x2000]);
+
+ IOLog("IOMemoryMapCopyOnWriteTest map(%s) %lld ns\n",
+ kIOMemoryMapCopyOnWrite & options ? "kIOMemoryMapCopyOnWrite" : "",
+ nano);
+
+ map->release();
+ md->release();
+ bmd->release();
+
+ return (kIOReturnSuccess);
+}
+
+static int
+IOMemoryMapCopyOnWriteTest(int newValue)
+{
+ IOMemoryMapTest(0);
+ IOMemoryMapTest(kIOMemoryMapCopyOnWrite);
+ return (0);
+}
+
+static int
+AllocationNameTest(int newValue)
+{
+ IOMemoryDescriptor * bmd;
+ kern_allocation_name_t name, prior;
+
+ name = kern_allocation_name_allocate("com.apple.iokit.test", 0);
+ assert(name);
+
+ prior = thread_set_allocation_name(name);
+
+ bmd = IOBufferMemoryDescriptor::inTaskWithOptions(TASK_NULL,
+ kIODirectionOutIn | kIOMemoryPageable | kIOMemoryKernelUserShared,
+ ptoa(13));
+ assert(bmd);
+ bmd->prepare();
+
+ thread_set_allocation_name(prior);
+ kern_allocation_name_release(name);
+
+ if (newValue != 7) bmd->release();
+
+ return (0);
+}
+
int IOMemoryDescriptorTest(int newValue)
{
int result;
+ IOLog("/IOMemoryDescriptorTest %d\n", (int) gIOMemoryReferenceCount);
+
#if 0
- if (5 == newValue)
+ if (6 == newValue)
+ {
+ IOMemoryDescriptor * sbmds[3];
+ IOMultiMemoryDescriptor * smmd;
+ IOMemoryDescriptor * mds[2];
+ IOMultiMemoryDescriptor * mmd;
+ IOMemoryMap * map;
+
+ sbmds[0] = IOBufferMemoryDescriptor::inTaskWithOptions(kernel_task, kIODirectionOutIn | kIOMemoryKernelUserShared, ptoa(1));
+ sbmds[1] = IOBufferMemoryDescriptor::inTaskWithOptions(kernel_task, kIODirectionOutIn | kIOMemoryKernelUserShared, ptoa(2));
+ sbmds[2] = IOBufferMemoryDescriptor::inTaskWithOptions(kernel_task, kIODirectionOutIn | kIOMemoryKernelUserShared, ptoa(3));
+ smmd = IOMultiMemoryDescriptor::withDescriptors(&sbmds[0], sizeof(sbmds)/sizeof(sbmds[0]), kIODirectionOutIn, false);
+
+ mds[0] = IOBufferMemoryDescriptor::inTaskWithOptions(kernel_task, kIODirectionOutIn | kIOMemoryKernelUserShared, ptoa(1));
+ mds[1] = smmd;
+ mmd = IOMultiMemoryDescriptor::withDescriptors(&mds[0], sizeof(mds)/sizeof(mds[0]), kIODirectionOutIn, false);
+ map = mmd->createMappingInTask(kernel_task, 0, kIOMapAnywhere);
+ assert(map);
+ map->release();
+ mmd->release();
+ mds[0]->release();
+ mds[1]->release();
+ sbmds[0]->release();
+ sbmds[1]->release();
+ sbmds[2]->release();
+
+ return (0);
+ }
+ else if (5 == newValue)
{
IOReturn ret;
IOMemoryDescriptor * md;
}
#endif
+ result = IODMACommandForceDoubleBufferTest(newValue);
+ if (result) return (result);
+
+ result = AllocationNameTest(newValue);
+ if (result) return (result);
+
+ result = IOMemoryMapCopyOnWriteTest(newValue);
+ if (result) return (result);
+
result = IOMultMemoryDescriptorTest(newValue);
if (result) return (result);
+ result = ZeroLengthTest(newValue);
+ if (result) return (result);
+
+ result = IODirectionPrepareNoZeroFillTest(newValue);
+ if (result) return (result);
+
+ result = BadFixedAllocTest(newValue);
+ if (result) return (result);
+
+ result = IOMemoryRemoteTest(newValue);
+ if (result) return (result);
+
+ result = IOMemoryPrefaultTest(newValue);
+ if (result) return (result);
+
IOGeneralMemoryDescriptor * md;
vm_offset_t data[2];
vm_size_t bsize = 16*1024*1024;
vm_size_t srcsize, srcoffset, mapoffset, size;
kern_return_t kr;
- kr = vm_allocate(kernel_map, &data[0], bsize, VM_FLAGS_ANYWHERE);
+ data[0] = data[1] = 0;
+ kr = vm_allocate_kernel(kernel_map, &data[0], bsize, VM_FLAGS_ANYWHERE, VM_KERN_MEMORY_IOKIT);
+ assert(KERN_SUCCESS == kr);
+
vm_inherit(kernel_map, data[0] + ptoa(1), ptoa(1), VM_INHERIT_NONE);
vm_inherit(kernel_map, data[0] + ptoa(16), ptoa(4), VM_INHERIT_NONE);
bzero(&ranges[0], sizeof(ranges));
ranges[0].address = data[0] + srcoffset;
ranges[0].length = srcsize;
+ ranges[1].address = ranges[2].address = data[0];
if (srcsize > ptoa(5))
{
assert(md);
IOLog("IOMemoryDescriptor::withAddressRanges [0x%lx @ 0x%lx]\n[0x%llx, 0x%llx],\n[0x%llx, 0x%llx],\n[0x%llx, 0x%llx]\n",
- (long) srcsize, (long) srcoffset,
+ (long) srcsize, (long) srcoffset,
(long long) ranges[0].address - data[0], (long long) ranges[0].length,
(long long) ranges[1].address - data[0], (long long) ranges[1].length,
(long long) ranges[2].address - data[0], (long long) ranges[2].length);
vm_deallocate(kernel_map, data[0], bsize);
// vm_deallocate(kernel_map, data[1], size);
+ IOLog("IOMemoryDescriptorTest/ %d\n", (int) gIOMemoryReferenceCount);
+
return (0);
}