/* * Copyright (c) 2015-2022 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #if defined(__x86_64__) #include // IOSKCopyKextIdentifierWithAddress() #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #define ELOG(fmt, args...) SK_ERR(fmt, ##args) #define DLOG(fmt, args...) SK_DF(SK_VERB_IOSK, fmt, ##args) #define IOSK_SIZE_OK(x) (((x) != 0) && (round_page(x) == (x))) #define IOSK_OFFSET_OK(x) (round_page(x) == (x)) static vm_tag_t getVMTagForMap( vm_map_t map ) { return (map == kernel_map) ? VM_KERN_MEMORY_SKYWALK : VM_MEMORY_SKYWALK; } class IOSKMemoryArray : public IOMultiMemoryDescriptor { OSDeclareFinalStructors( IOSKMemoryArray ); public: bool overwriteMappingInTask( task_t intoTask, mach_vm_address_t * startAddr, IOOptionBits options ); }; class IOSKMemoryBuffer : public IOBufferMemoryDescriptor { OSDeclareFinalStructors( IOSKMemoryBuffer ); public: bool initWithSpec( task_t inTask, mach_vm_size_t capacity, mach_vm_address_t alignment, const IOSKMemoryBufferSpec * spec ); virtual void * getBytesNoCopy( void ) APPLE_KEXT_OVERRIDE; virtual void * getBytesNoCopy(vm_size_t start, vm_size_t withLength) APPLE_KEXT_OVERRIDE; bool isWired( void ) const { return _wireCount != 0; } IOSKMemoryBufferSpec fSpec; void *fKernelAddr; IOMemoryMap *fKernelReadOnlyMapping; protected: virtual void taggedRelease(const void *tag = NULL) const APPLE_KEXT_OVERRIDE; virtual void free( void ) APPLE_KEXT_OVERRIDE; }; // FIXME: rename IOSKMemoryBuffer -> IOSKBuffer typedef IOSKMemoryBuffer IOSKBuffer; // IOSKRegionMapper: // Tracks all memory mappings of a single IOSKRegion, with an array of // IOMemoryMaps to map the region's memory segments. // Created and released by the parent IOSKMapper. class IOSKRegionMapper : public OSObject { OSDeclareFinalStructors( IOSKRegionMapper ); public: bool initWithMapper( IOSKMapper * mapper, IOSKRegion * region, IOSKOffset regionOffset ); IOReturn map( IOSKIndex segIndex, IOSKBuffer * buffer ); void unmap( IOSKIndex segIndex, vm_prot_t prot ); kern_return_t mapOverwrite( vm_map_offset_t addr, vm_map_size_t size, vm_prot_t prot ); private: virtual void free( void ) APPLE_KEXT_OVERRIDE; IOSKMapper * fMapper; IOSKRegion * fRegion; IOMemoryMap ** fMemoryMaps; IOSKCount fMemoryMapCount; IOSKOffset fRegionOffset; }; // IOSKMapper: // Manages all memory mappings of a single task, with an array of // IOSKRegionMappers to map all memory regions of a memory arena. // Retains the IOSKArena. class IOSKMapper : public OSObject { OSDeclareFinalStructors( IOSKMapper ); friend class IOSKRegionMapper; public: bool initWithTask( task_t task, IOSKArena * arena ); IOReturn map( IOSKIndex regIndex, IOSKIndex segIndex, IOSKBuffer * buffer ); void unmap( IOSKIndex regIndex, IOSKIndex segIndex, vm_prot_t prot ); mach_vm_address_t getMapAddress( mach_vm_size_t * size ) const { if (size) { *size = fMapSize; } return fMapAddr; } IOSKArena * getArena( void ) const { return fArena; } bool isRedirected( void ) const { return fRedirected; } void redirectMap( void ) { fRedirected = true; } private: virtual void free( void ) APPLE_KEXT_OVERRIDE; task_t fTask; vm_map_t fTaskMap; IOSKArena * fArena; OSArray * fSubMaps; mach_vm_address_t fMapAddr; mach_vm_size_t fMapSize; bool fRedirected; }; // IOSKArena: // An array of IOSKRegions is used to create an IOSKArena. // One or more IOSKMapper can map the arena memory to tasks. // Retains the IOSKRegions, also circularly retains the IOSKMapper(s) // until the client calls IOSKMapperDestroy(). class IOSKArena : public OSObject { OSDeclareFinalStructors( IOSKArena ); public: bool initWithRegions( IOSKRegion ** regions, IOSKCount regionCount ); IOReturn createMapperForTask( task_t task, LIBKERN_RETURNS_RETAINED IOSKMapper ** mapper ); void redirectMap( IOSKMapper * mapper ); IOSKSize getArenaSize( void ) const { return fArenaSize; } IOSKCount getRegionCount( void ) const { return fRegions->getCount(); } IOSKRegion * getRegion( IOSKIndex regIndex ) const; IOReturn map( const IOSKRegion * region, IOSKOffset regionOffset, IOSKIndex regionIndex, IOSKIndex segmentIndex, IOSKMemoryBuffer * buffer ); void unmap( const IOSKRegion * region, IOSKOffset regionOffset, IOSKIndex regionIndex, IOSKIndex segmentIndex, vm_prot_t prot, bool isRedirected, const void * context ); bool addMapper( const IOSKMapper * mapper ); void removeMapper( const IOSKMapper * mapper ); private: virtual void free( void ) APPLE_KEXT_OVERRIDE; IOLock * fArenaLock; OSSet * fMappers; OSArray * fRegions; IOSKSize fArenaSize; }; // IOSKRegion: // An IOSKRegion manages a dynamic array of IOSKBuffers representing each // memory segment in the region. Each IOSKRegion can be shared by multiple // IOSKArenas, and the IOSKRegion keeps state specific to each arena - the // offset and the index of the region within the arena. A lock is used to // serialize updates to the IOSKBuffer array and the arenas. // Retains the IOSKBuffers. class IOSKRegion : public OSObject { OSDeclareFinalStructors( IOSKRegion ); public: bool initWithSpec( const IOSKRegionSpec * spec, IOSKSize segSize, IOSKCount segCount ); IOReturn setSegmentBuffer( IOSKIndex index, IOSKBuffer * buf ); void clearSegmentBuffer( IOSKIndex index, IOSKMemoryBufferRef * prevBuffer ); bool attachArena( IOSKArena * arena, IOSKOffset regionOffset, IOSKIndex regionIndex ); void detachArena( const IOSKArena * arena ); IOReturn updateMappingsForArena( IOSKArena * arena, bool redirect, const void * context = NULL ); IOSKCount getSegmentCount( void ) const { return fSegmentCount; } IOSKSize getSegmentSize( void ) const { return fSegmentSize; } IOSKSize getRegionSize( void ) const { return fSegmentCount * fSegmentSize; } private: virtual void free( void ) APPLE_KEXT_OVERRIDE; struct Segment { IOSKBuffer * fBuffer; }; struct ArenaEntry { SLIST_ENTRY(ArenaEntry) link; IOSKArena * fArena; IOSKOffset fRegionOffset; IOSKIndex fRegionIndex; }; SLIST_HEAD(ArenaHead, ArenaEntry); IOReturn _setSegmentBuffer( const IOSKIndex index, IOSKMemoryBuffer * buf ); void _clearSegmentBuffer( const IOSKIndex index, IOSKMemoryBufferRef * prevBuffer ); ArenaEntry * findArenaEntry( const IOSKArena * arena ); IOSKRegionSpec fSpec; IOLock * fRegionLock; ArenaHead fArenaHead; Segment * fSegments; IOSKCount fSegmentCount; IOSKSize fSegmentSize; }; #undef super #define super OSObject OSDefineMetaClassAndFinalStructors( IOSKRegionMapper, OSObject ) bool IOSKRegionMapper::initWithMapper( IOSKMapper * mapper, IOSKRegion * region, IOSKOffset regionOffset ) { if ((mapper == NULL) || (region == NULL) || !super::init()) { return false; } // parent mapper retains the arena, which retains the regions assert(IOSK_OFFSET_OK(regionOffset)); fMapper = mapper; fRegion = region; fRegionOffset = regionOffset; fMemoryMapCount = region->getSegmentCount(); assert(fMemoryMapCount != 0); fMemoryMaps = IONew(IOMemoryMap *, fMemoryMapCount); if (!fMemoryMaps) { return false; } bzero(fMemoryMaps, sizeof(IOMemoryMap *) * fMemoryMapCount); DLOG("SKRegionMapper %p mapper %p region %p offset 0x%x", this, mapper, region, regionOffset); return true; } void IOSKRegionMapper::free( void ) { DLOG("SKRegionMapper %p", this); if (fMemoryMaps) { assert(fMemoryMapCount != 0); for (IOSKIndex i = 0; i < fMemoryMapCount; i++) { if (fMemoryMaps[i]) { fMemoryMaps[i]->release(); fMemoryMaps[i] = NULL; } } IODelete(fMemoryMaps, IOMemoryMap *, fMemoryMapCount); fMemoryMaps = NULL; fMemoryMapCount = 0; } fMapper = NULL; fRegion = NULL; super::free(); } IOReturn IOSKRegionMapper::map( IOSKIndex segIndex, IOSKBuffer * buffer ) { mach_vm_address_t addr; mach_vm_offset_t offset; IOMemoryMap * map; IOOptionBits options = kIOMapOverwrite; IOReturn ret = kIOReturnSuccess; assert(segIndex < fMemoryMapCount); assert(buffer != NULL); if ((segIndex >= fMemoryMapCount) || (buffer == NULL)) { return kIOReturnBadArgument; } // redundant map requests are expected when the arena is mapped // by more than one mapper. if ((map = fMemoryMaps[segIndex]) != NULL) { assert(map->getMemoryDescriptor() == buffer); return kIOReturnSuccess; } if (buffer->fSpec.user_writable == FALSE) { options |= kIOMapReadOnly; } offset = fRegionOffset + (segIndex * fRegion->getSegmentSize()); assert((offset + fRegion->getSegmentSize()) <= fMapper->fMapSize); addr = fMapper->fMapAddr + offset; map = buffer->createMappingInTask(fMapper->fTask, addr, options); fMemoryMaps[segIndex] = map; assert((map == NULL) || (map->getLength() == fRegion->getSegmentSize())); if (map == NULL) { ret = kIOReturnVMError; } SK_DF(ret == kIOReturnSuccess ? SK_VERB_IOSK : SK_VERB_ERROR, "%p buffer %p index %u map %p offset 0x%x size 0x%x", this, buffer, segIndex, fMemoryMaps[segIndex], (uint32_t)offset, fRegion->getSegmentSize()); return ret; } void IOSKRegionMapper::unmap( IOSKIndex segIndex, vm_prot_t prot ) { mach_vm_address_t addr; mach_vm_offset_t offset; IOMemoryMap * map; kern_return_t kr; assert(segIndex < fMemoryMapCount); // redundant unmap requests are expected when the arena is mapped // by more than one mapper. if ((segIndex >= fMemoryMapCount) || ((map = fMemoryMaps[segIndex]) == NULL)) { return; } offset = fRegionOffset + (segIndex * fRegion->getSegmentSize()); assert((offset + fRegion->getSegmentSize()) <= fMapper->fMapSize); addr = fMapper->fMapAddr + offset; kr = mapOverwrite(addr, fRegion->getSegmentSize(), prot); assert(KERN_SUCCESS == kr); map->release(); fMemoryMaps[segIndex] = map = NULL; DLOG("SKRegionMapper %p index %u offset 0x%x size 0x%x", this, segIndex, (uint32_t)offset, fRegion->getSegmentSize()); } kern_return_t IOSKRegionMapper::mapOverwrite( vm_map_offset_t addr, vm_map_size_t size, vm_prot_t prot ) { vm_map_kernel_flags_t vmk_flags = VM_MAP_KERNEL_FLAGS_FIXED(); kern_return_t kr; vmk_flags.vmf_overwrite = true; vmk_flags.vm_tag = getVMTagForMap(fMapper->fTaskMap); kr = mach_vm_map_kernel( fMapper->fTaskMap, &addr, size, (vm_map_offset_t)0, vmk_flags, IPC_PORT_NULL, (vm_object_offset_t)0, FALSE, prot, VM_PROT_DEFAULT, VM_INHERIT_NONE); SK_DF(kr == KERN_SUCCESS ? SK_VERB_IOSK : SK_VERB_ERROR, "SKRegionMapper %p addr 0x%llx size 0x%llx prot 0x%x " "kr 0x%x", this, (uint64_t)addr, (uint64_t)size, prot, kr); return kr; } #undef super #define super OSObject OSDefineMetaClassAndFinalStructors( IOSKMapper, OSObject ) bool IOSKMapper::initWithTask( task_t task, IOSKArena * arena ) { IOSKRegionMapper * subMap; IOSKRegion * region; IOSKCount regionCount; IOSKOffset regionOffset = 0; vm_map_offset_t addr; vm_map_size_t size; kern_return_t kr; bool ok = false; if ((task == TASK_NULL) || (arena == NULL) || !super::init()) { return false; } fTask = task; fTaskMap = get_task_map(task); if (fTaskMap == VM_MAP_NULL) { return false; } arena->retain(); fArena = arena; regionCount = fArena->getRegionCount(); assert(regionCount != 0); fSubMaps = OSArray::withCapacity(regionCount); if (!fSubMaps) { return false; } for (IOSKIndex i = 0; i < regionCount; i++) { region = fArena->getRegion(i); assert(region != NULL); subMap = new IOSKRegionMapper; if (subMap && !subMap->initWithMapper(this, region, regionOffset)) { subMap->release(); subMap = NULL; } if (!subMap) { break; } // array retains the regions ok = fSubMaps->setObject(subMap); subMap->release(); subMap = NULL; if (!ok) { break; } // offset of next region regionOffset += region->getRegionSize(); } if (fSubMaps->getCount() != regionCount) { return false; } addr = 0; size = fArena->getArenaSize(); assert(regionOffset == size); assert(IOSK_SIZE_OK(size)); vm_map_kernel_flags_t vmk_flags = VM_MAP_KERNEL_FLAGS_ANYWHERE(); vmk_flags.vm_tag = getVMTagForMap(fTaskMap); // reserve address space on given task with PROT_NONE kr = mach_vm_map_kernel( fTaskMap, &addr, size, (vm_map_offset_t)0, vmk_flags, IPC_PORT_NULL, (vm_object_offset_t)0, FALSE, VM_PROT_NONE, VM_PROT_DEFAULT, VM_INHERIT_NONE); ok = false; if (KERN_SUCCESS == kr) { fMapAddr = (mach_vm_address_t)addr; fMapSize = (mach_vm_size_t)size; ok = true; } SK_DF(kr == KERN_SUCCESS ? SK_VERB_IOSK : SK_VERB_ERROR, "SKMapper %p task 0x%llx map %p addr 0x%llx size 0x%llx subMaps %u " "kr 0x%x", this, (uint64_t)task, fTaskMap, (uint64_t)addr, (uint64_t)size, fSubMaps->getCount(), kr); return ok; } void IOSKMapper::free( void ) { DLOG("SKMapper %p", this); if (fSubMaps != NULL) { fSubMaps->release(); fSubMaps = NULL; } if (fArena != NULL) { fArena->release(); fArena = NULL; } if (fMapSize != 0) { mach_vm_deallocate(fTaskMap, fMapAddr, fMapSize); fTaskMap = NULL; fMapAddr = 0; fMapSize = 0; } fTask = NULL; fTaskMap = NULL; super::free(); } IOReturn IOSKMapper::map( IOSKIndex regionIndex, IOSKIndex segmentIndex, IOSKBuffer * buffer ) { IOSKRegionMapper * subMap; IOReturn ret = kIOReturnBadArgument; // route to the region mapper at regionIndex assert(regionIndex < fSubMaps->getCount()); subMap = (typeof(subMap))fSubMaps->getObject(regionIndex); if (subMap) { ret = subMap->map(segmentIndex, buffer); } return ret; } void IOSKMapper::unmap( IOSKIndex regionIndex, IOSKIndex segmentIndex, vm_prot_t prot ) { IOSKRegionMapper * subMap; // route to the region mapper at regionIndex assert(regionIndex < fSubMaps->getCount()); subMap = (typeof(subMap))fSubMaps->getObject(regionIndex); if (subMap) { subMap->unmap(segmentIndex, prot); } } #undef super #define super OSObject OSDefineMetaClassAndFinalStructors( IOSKArena, OSObject ) bool IOSKArena::initWithRegions( IOSKRegion ** regions, IOSKCount regionCount ) { IOSKRegion * region; IOSKSize regionSize; IOSKOffset regionOffset = 0; bool ok = false; assert(regions != NULL); assert(regionCount != 0); do { if ((regions == NULL) || (regionCount == 0) || !super::init()) { break; } fArenaLock = IOLockAlloc(); if (fArenaLock == NULL) { break; } fRegions = OSArray::withObjects((const OSObject **)regions, regionCount); if (!fRegions) { break; } ok = true; for (uint32_t i = 0; i < regionCount; i++) { region = OSDynamicCast(IOSKRegion, fRegions->getObject(i)); ok = (region != NULL); if (!ok) { break; } regionSize = region->getRegionSize(); assert(IOSK_SIZE_OK(regionSize)); // attach to each region and assign region offset/index ok = region->attachArena(this, regionOffset, i); if (!ok) { break; } // offset of next region regionOffset += regionSize; assert(IOSK_OFFSET_OK(regionOffset)); } fArenaSize = regionOffset; } while (false); DLOG("SKArena %p regions %u size 0x%x ok %d", this, regionCount, fArenaSize, ok); return ok; } void IOSKArena::free( void ) { DLOG("IOSKArena %p", this); if (fRegions) { IOSKRegion * region; OSObject * object; // detach from regions to stop mapping requests for (uint32_t i = 0; (object = fRegions->getObject(i)); i++) { region = OSDynamicCast(IOSKRegion, object); if (region) { region->detachArena(this); } } fRegions->release(); fRegions = NULL; } if (fMappers) { assert(fMappers->getCount() == 0); fMappers->release(); fMappers = NULL; } if (fArenaLock != NULL) { IOLockFree(fArenaLock); fArenaLock = NULL; } super::free(); } IOReturn IOSKArena::createMapperForTask( task_t task, IOSKMapper ** outMapper ) { IOSKRegion * region; OSObject * object; IOSKMapper * mapper; IOReturn result, ret = kIOReturnSuccess; assert(task != TASK_NULL); assert(outMapper != NULL); mapper = new IOSKMapper; if (mapper && !mapper->initWithTask(task, this)) { mapper->release(); mapper = NULL; } if (!mapper || !addMapper(mapper)) { ret = kIOReturnNoMemory; goto done; } // request all regions to refresh the arena's mappings, // which now includes the newly added mapper. for (uint32_t i = 0; (object = fRegions->getObject(i)); i++) { region = OSDynamicCast(IOSKRegion, object); assert(region != NULL); result = region->updateMappingsForArena(this, false); assert(kIOReturnSuccess == result); if (result != kIOReturnSuccess) { ret = result; } } done: if ((ret != kIOReturnSuccess) && mapper) { mapper->release(); mapper = NULL; } *outMapper = mapper; return ret; } IOReturn IOSKArena::map( const IOSKRegion * region __unused, IOSKOffset regionOffset __unused, IOSKIndex regionIndex, IOSKIndex segmentIndex, IOSKBuffer * buffer ) { IOSKMapper * mapper; OSIterator * iter; IOReturn result, ret = kIOReturnSuccess; IOLockLock(fArenaLock); if (fMappers && (iter = OSCollectionIterator::withCollection(fMappers))) { while ((mapper = (typeof(mapper))iter->getNextObject())) { // skip any redirected mapper if (mapper->isRedirected()) { continue; } result = mapper->map(regionIndex, segmentIndex, buffer); assert(kIOReturnSuccess == result); if (result != kIOReturnSuccess) { ret = result; } } iter->release(); } IOLockUnlock(fArenaLock); return ret; } void IOSKArena::unmap( const IOSKRegion * region __unused, IOSKOffset regionOffset __unused, IOSKIndex regionIndex, IOSKIndex segmentIndex, vm_prot_t prot, bool redirecting, const void * context ) { IOSKMapper * mapper; const IOSKMapper * redirectMapper = (typeof(redirectMapper))context; OSIterator * iter; IOLockLock(fArenaLock); if (fMappers && (iter = OSCollectionIterator::withCollection(fMappers))) { while ((mapper = (typeof(mapper))iter->getNextObject())) { if (redirecting) { if ((redirectMapper == NULL) || (redirectMapper == mapper)) { // redirecting can be specific to one mapper mapper->unmap(regionIndex, segmentIndex, prot); mapper->redirectMap(); } } else if (!mapper->isRedirected()) { mapper->unmap(regionIndex, segmentIndex, prot); } } iter->release(); } IOLockUnlock(fArenaLock); } void IOSKArena::redirectMap( IOSKMapper * mapper ) { OSObject * object; IOSKRegion * region; IOReturn ret; // request all (redirectable) regions to redirect the arena's mapper, // mapper=0 will redirect all mappers. for (uint32_t i = 0; (object = fRegions->getObject(i)); i++) { region = OSDynamicCast(IOSKRegion, object); assert(region != NULL); ret = region->updateMappingsForArena(this, true, (const void *)mapper); assert(kIOReturnSuccess == ret); } } IOSKRegion * IOSKArena::getRegion( IOSKIndex regionIndex ) const { assert(regionIndex < getRegionCount()); return OSDynamicCast(IOSKRegion, fRegions->getObject(regionIndex)); } bool IOSKArena::addMapper( const IOSKMapper * mapper ) { bool ok = false; assert(mapper != NULL); if (!mapper) { return false; } IOLockLock(fArenaLock); if (!fMappers) { fMappers = OSSet::withCapacity(2); } if (fMappers) { ok = fMappers->setObject(mapper); } IOLockUnlock(fArenaLock); DLOG("arena %p mapper %p ok %d", this, mapper, ok); return ok; } void IOSKArena::removeMapper( const IOSKMapper * mapper ) { assert(mapper != NULL); if (!mapper) { return; } IOLockLock(fArenaLock); if (fMappers) { fMappers->removeObject(mapper); } IOLockUnlock(fArenaLock); DLOG("arena %p mapper %p", this, mapper); } #undef super #define super OSObject OSDefineMetaClassAndFinalStructors( IOSKRegion, OSObject ) bool IOSKRegion::initWithSpec( const IOSKRegionSpec * spec, IOSKSize segmentSize, IOSKCount segmentCount ) { bool ok = false; do { if (!IOSK_SIZE_OK(segmentSize) || (segmentCount == 0) || !super::init()) { break; } if (spec) { fSpec = *spec; } fSegmentCount = segmentCount; fSegmentSize = segmentSize; fRegionLock = IOLockAlloc(); if (fRegionLock == NULL) { break; } SLIST_INIT(&fArenaHead); fSegments = IONew(Segment, fSegmentCount); if (fSegments == NULL) { break; } bzero(fSegments, sizeof(IOSKRegion::Segment) * fSegmentCount); ok = true; } while (false); SK_DF(ok ? SK_VERB_IOSK : SK_VERB_ERROR, "SKRegion %p segment size 0x%x count %u ok %d", this, segmentSize, segmentCount, ok); return ok; } void IOSKRegion::free( void ) { DLOG("SKRegion %p", this); ArenaEntry *entry, *tentry; SLIST_FOREACH_SAFE(entry, &fArenaHead, link, tentry) { SLIST_REMOVE(&fArenaHead, entry, ArenaEntry, link); // Arena didn't detach from the region before release() assert(entry->fArena == NULL); IOFreeType(entry, ArenaEntry); } assert(SLIST_EMPTY(&fArenaHead)); if (fSegments != NULL) { assert(fSegmentCount != 0); for (uint32_t i = 0; i < fSegmentCount; i++) { _clearSegmentBuffer(i, NULL); } IODelete(fSegments, Segment, fSegmentCount); fSegments = NULL; } if (fRegionLock != NULL) { IOLockFree(fRegionLock); fRegionLock = NULL; } super::free(); } IOReturn IOSKRegion::_setSegmentBuffer( const IOSKIndex segmentIndex, IOSKBuffer * buffer ) { Segment * seg; IOReturn ret = kIOReturnSuccess; assert(buffer != NULL); assert(segmentIndex < fSegmentCount); if (!buffer || (buffer->getCapacity() != fSegmentSize) || (segmentIndex >= fSegmentCount)) { ret = kIOReturnBadArgument; goto done; } seg = &fSegments[segmentIndex]; assert(seg->fBuffer == NULL); if (seg->fBuffer == NULL) { buffer->retain(); seg->fBuffer = buffer; // update mappings for all arenas containing this region, // or none if no arena is attached. ArenaEntry * entry; SLIST_FOREACH(entry, &fArenaHead, link) { if (entry->fArena != NULL) { ret = entry->fArena->map(this, entry->fRegionOffset, entry->fRegionIndex, segmentIndex, buffer); assert(kIOReturnSuccess == ret); if (ret != kIOReturnSuccess) { break; } } } } if (ret != kIOReturnSuccess) { _clearSegmentBuffer(segmentIndex, NULL); } done: SK_DF(ret == kIOReturnSuccess ? SK_VERB_IOSK : SK_VERB_ERROR, "SKRegion %p set segment[%u] buffer %p ret 0x%x", this, segmentIndex, buffer, ret); return ret; } void IOSKRegion::_clearSegmentBuffer( const IOSKIndex segmentIndex, IOSKMemoryBufferRef * prevBuffer ) { Segment * seg; bool cleared = false; IOSKBuffer * foundBuffer = NULL; assert(segmentIndex < fSegmentCount); if (segmentIndex >= fSegmentCount) { goto done; } seg = &fSegments[segmentIndex]; if (seg->fBuffer != NULL) { foundBuffer = seg->fBuffer; // update mappings for all arenas containing this region, // or none if no arena is attached. vm_prot_t prot = VM_PROT_NONE; ArenaEntry * entry; SLIST_FOREACH(entry, &fArenaHead, link) { if (entry->fArena != NULL) { entry->fArena->unmap(this, entry->fRegionOffset, entry->fRegionIndex, segmentIndex, prot, false, NULL); } } seg->fBuffer->release(); seg->fBuffer = NULL; cleared = true; } if (prevBuffer) { *prevBuffer = foundBuffer; } done: DLOG("SKRegion %p clear segment[%u] ok %d", this, segmentIndex, cleared); } IOReturn IOSKRegion::setSegmentBuffer( IOSKIndex index, IOSKMemoryBuffer * buffer ) { IOReturn ret; IOLockLock(fRegionLock); ret = _setSegmentBuffer(index, buffer); IOLockUnlock(fRegionLock); return ret; } void IOSKRegion::clearSegmentBuffer( IOSKIndex index, IOSKMemoryBufferRef * prevBuffer ) { IOLockLock(fRegionLock); _clearSegmentBuffer(index, prevBuffer); IOLockUnlock(fRegionLock); } IOSKRegion::ArenaEntry * IOSKRegion::findArenaEntry( const IOSKArena * arena ) { ArenaEntry * found = NULL; assert(arena != NULL); ArenaEntry * entry; SLIST_FOREACH(entry, &fArenaHead, link) { if (entry->fArena == arena) { found = entry; break; } } return found; } bool IOSKRegion::attachArena( IOSKArena * arena, IOSKOffset regionOffset, IOSKIndex regionIndex ) { bool ok = false; assert(arena != NULL); if (!arena) { return false; } IOLockLock(fRegionLock); ArenaEntry * entry = NULL; ArenaEntry * empty = NULL; ArenaEntry * dup = NULL; SLIST_FOREACH(entry, &fArenaHead, link) { // duplicates not allowed assert(entry->fArena != arena); if (entry->fArena == arena) { dup = entry; break; } if ((empty == NULL) && (entry->fArena == NULL)) { empty = entry; } } if (dup != NULL) { // do nothing } else if (empty != NULL) { // update the empty/available entry empty->fArena = arena; empty->fRegionOffset = regionOffset; empty->fRegionIndex = regionIndex; ok = true; } else { // append a new entry ArenaEntry * newEntry = IOMallocType(ArenaEntry); newEntry->fArena = arena; newEntry->fRegionOffset = regionOffset; newEntry->fRegionIndex = regionIndex; SLIST_INSERT_HEAD(&fArenaHead, newEntry, link); ok = true; } IOLockUnlock(fRegionLock); SK_DF(ok ? SK_VERB_IOSK : SK_VERB_ERROR, "SKRegion %p attach arena %p offset 0x%x index %u ok %d", this, arena, regionOffset, regionIndex, ok); return ok; } void IOSKRegion::detachArena( const IOSKArena * arena ) { ArenaEntry * entry; bool detached = false; assert(arena != NULL); if (!arena) { return; } IOLockLock(fRegionLock); entry = findArenaEntry(arena); if (entry != NULL) { entry->fArena = NULL; entry->fRegionOffset = 0; entry->fRegionIndex = 0; detached = true; } IOLockUnlock(fRegionLock); DLOG("SKRegion %p detach arena %p ok %d", this, arena, detached); } IOReturn IOSKRegion::updateMappingsForArena( IOSKArena * arena, bool redirect, const void * context ) { ArenaEntry * entry; Segment * seg; vm_prot_t prot; IOReturn result = kIOReturnSuccess; assert(arena != NULL); if (redirect && fSpec.noRedirect) { DLOG("SKRegion %p no redirect", this); return kIOReturnSuccess; } IOLockLock(fRegionLock); entry = findArenaEntry(arena); if (entry != NULL) { assert(entry->fArena == arena); for (uint32_t index = 0; index < fSegmentCount; index++) { seg = &fSegments[index]; if ((seg->fBuffer == NULL) || redirect) { prot = VM_PROT_NONE; if (redirect && (seg->fBuffer != NULL)) { prot = VM_PROT_READ; if (seg->fBuffer->fSpec.user_writable) { prot |= VM_PROT_WRITE; } } arena->unmap(this, entry->fRegionOffset, entry->fRegionIndex, index, prot, redirect, context); } else { result = arena->map(this, entry->fRegionOffset, entry->fRegionIndex, index, seg->fBuffer); } } } IOLockUnlock(fRegionLock); SK_DF(result == kIOReturnSuccess ? SK_VERB_IOSK : SK_VERB_ERROR, "%p update arena %p redirect %d ret 0x%x", this, arena, redirect, result); return result; } OSDefineMetaClassAndFinalStructors( IOSKMemoryArray, IOMultiMemoryDescriptor ) bool IOSKMemoryArray::overwriteMappingInTask( task_t intoTask, mach_vm_address_t * startAddr, IOOptionBits options ) { bool ok = true; for (uint32_t i = 0; i < _descriptorsCount; i++) { IOMemoryDescriptor * iomd = _descriptors[i]; IOSKMemoryBuffer * mb = OSDynamicCast(IOSKMemoryBuffer, iomd); IOSKMemoryArray * ma = OSDynamicCast(IOSKMemoryArray, iomd); if (mb) { IOMemoryMap * rwMap; if (mb->fSpec.user_writable) { // overwrite read-only mapping to read-write rwMap = mb->createMappingInTask(intoTask, *startAddr, options | kIOMapOverwrite); if (rwMap) { DLOG("map_rw %d: addr 0x%llx, size 0x%x", i, *startAddr, (uint32_t)iomd->getLength()); rwMap->release(); } else { ELOG("overwrite map failed"); ok = false; break; } } else { DLOG("map_ro %d: addr 0x%llx, size 0x%x", i, *startAddr, (uint32_t)iomd->getLength()); } //DLOG("map increment 0x%x", (uint32_t)iomd->getLength()); *startAddr += iomd->getLength(); } else if (ma) { ok = ma->overwriteMappingInTask(intoTask, startAddr, options); if (!ok) { break; } } } return ok; } #undef super #define super IOBufferMemoryDescriptor OSDefineMetaClassAndFinalStructorsWithZone( IOSKMemoryBuffer, IOBufferMemoryDescriptor, ZC_NONE ) bool IOSKMemoryBuffer::initWithSpec( task_t inTask, mach_vm_size_t capacity, mach_vm_address_t alignment, const IOSKMemoryBufferSpec * spec ) { bool ok = true; IOOptionBits options = kIOMemoryKernelUserShared; if (spec) { fSpec = *spec; } if (fSpec.iodir_in) { options |= kIODirectionIn; } if (fSpec.iodir_out) { options |= kIODirectionOut; } if (fSpec.purgeable) { options |= (kIOMemoryPageable | kIOMemoryPurgeable); } if (fSpec.inhibitCache) { options |= kIOMapInhibitCache; } if (fSpec.physcontig) { options |= kIOMemoryPhysicallyContiguous; } if (fSpec.threadSafe) { options |= kIOMemoryThreadSafe; } setVMTags(VM_KERN_MEMORY_SKYWALK, VM_MEMORY_SKYWALK); if (fSpec.kernel_writable) { if (fSpec.puredata) { /* purely data; use data buffers heap */ ok = initWithPhysicalMask( inTask, options, capacity, alignment, 0); } else { /* may have pointers; use default heap */ ok = initControlWithPhysicalMask( inTask, options, capacity, alignment, 0); } if (!ok) { return false; } fKernelAddr = super::getBytesNoCopy(); return true; } else { /* * To create kernel read-only BMD: * 1. init with TASK_NULL (which isn’t mapped anywhere); * 2. then map read-only into kernel_task * Note that kernel virtual address has to be obtained from * the secondary kernel read-only mapping. */ options |= kIOMapReadOnly; if (fSpec.puredata) { /* purely data; use data buffers heap */ ok = initWithPhysicalMask( TASK_NULL, options, capacity, alignment, 0); } else { /* may have pointers; use default heap */ ok = initControlWithPhysicalMask( TASK_NULL, options, capacity, alignment, 0); } if (!ok) { return false; } /* RO mapping will retain this, see ::taggedRelease() */ fKernelReadOnlyMapping = super::createMappingInTask(kernel_task, 0, options); if (fKernelReadOnlyMapping == NULL) { return false; } fKernelAddr = (void *)fKernelReadOnlyMapping->getVirtualAddress(); assert(fKernelAddr != NULL); return true; } } void IOSKMemoryBuffer::taggedRelease(const void *tag) const { /* * RO buffer has extra retain from fKernelReadOnlyMapping, needs to * explicitly release when refcnt == 2 to free ourselves. */ if (!fSpec.kernel_writable && fKernelReadOnlyMapping != NULL) { super::taggedRelease(tag, 2); } else { super::taggedRelease(tag); } } void IOSKMemoryBuffer::free( void ) { if (!fSpec.kernel_writable && fKernelReadOnlyMapping != NULL) { OSSafeReleaseNULL(fKernelReadOnlyMapping); fKernelAddr = NULL; } super::free(); } void * IOSKMemoryBuffer::getBytesNoCopy( void ) { return fKernelAddr; } void * IOSKMemoryBuffer::getBytesNoCopy( vm_size_t start, vm_size_t withLength ) { IOVirtualAddress address; if ((start + withLength) < start) { return NULL; } address = (IOVirtualAddress) fKernelAddr; if (start < _length && (start + withLength) <= _length) { return (void *)(address + start); } return NULL; } static IOSKMemoryBuffer * RefToMemoryBuffer( IOSKMemoryRef inRef ) { IOSKMemoryBuffer * mb = OSDynamicCast(IOSKMemoryBuffer, inRef); return mb; } static IOSKMemoryArray * RefToMemoryArray( IOSKMemoryRef inRef ) { IOSKMemoryArray * ma = OSDynamicCast(IOSKMemoryArray, inRef); return ma; } __BEGIN_DECLS void IOSKMemoryDestroy( IOSKMemoryRef reference ) { assert(reference); if (reference) { reference->release(); } } void IOSKMemoryMapDestroy( IOSKMemoryMapRef reference ) { assert(reference); if (reference) { reference->release(); } } IOSKMemoryBufferRef IOSKMemoryBufferCreate( mach_vm_size_t capacity, const IOSKMemoryBufferSpec * spec, mach_vm_address_t * kvaddr ) { IOSKMemoryBuffer * mb; void * addr = NULL; mach_vm_size_t rounded_capacity = round_page(capacity); if (capacity != rounded_capacity) { return NULL; } mb = new IOSKMemoryBuffer; if (mb && !mb->initWithSpec(kernel_task, capacity, PAGE_SIZE, spec)) { mb->release(); mb = NULL; } if (!mb) { ELOG("create capacity=0x%llx failed", capacity); goto fail; } addr = mb->fKernelAddr; if (kvaddr) { *kvaddr = (mach_vm_address_t)(uintptr_t)addr; } DLOG("buffer %p, vaddr %p, capacity 0x%llx", mb, addr, capacity); fail: return mb; } IOSKMemoryArrayRef IOSKMemoryArrayCreate( const IOSKMemoryRef refs[], uint32_t count ) { IOSKMemoryArray * ma; IOSKMemoryRef ref; bool ok = true; if (!refs || (count < 1)) { return NULL; } // Validate the references for (uint32_t i = 0; i < count; i++) { ref = refs[i]; assert(RefToMemoryBuffer(ref) || RefToMemoryArray(ref)); if (!RefToMemoryBuffer(ref) && !RefToMemoryArray(ref)) { ok = false; break; } } if (!ok) { return NULL; } ma = new IOSKMemoryArray; if (ma && !ma->initWithDescriptors((IOMemoryDescriptor **)refs, count, kIODirectionInOut, false)) { ma->release(); ma = NULL; } if (!ma) { ELOG("create count=%u failed", count); } else { DLOG("array %p count=%u", ma, count); } return ma; } IOSKMemoryMapRef IOSKMemoryMapToTask( IOSKMemoryRef reference, task_t intoTask, mach_vm_address_t * mapAddr, mach_vm_size_t * mapSize ) { IOOptionBits options = kIOMapAnywhere | kIOMapReadOnly; mach_vm_address_t startAddr; IOMemoryMap * map = NULL; IOSKMemoryArray * ma = RefToMemoryArray(reference); assert(ma); if (!ma) { return NULL; } assert(intoTask != kernel_task); map = ma->createMappingInTask(intoTask, 0, options); if (map) { bool ok; startAddr = map->getAddress(); *mapAddr = startAddr; *mapSize = map->getSize(); DLOG("map vaddr 0x%llx, size 0x%llx", *mapAddr, *mapSize); options &= ~(kIOMapReadOnly | kIOMapAnywhere); ok = ma->overwriteMappingInTask(intoTask, &startAddr, options); if (!ok) { map->release(); map = NULL; } } return map; } IOSKMemoryMapRef IOSKMemoryMapToKernelTask( IOSKMemoryRef reference, mach_vm_address_t * mapAddr, mach_vm_size_t * mapSize ) { IOOptionBits options = kIOMapAnywhere; mach_vm_address_t startAddr; IOMemoryMap * map = NULL; IOSKMemoryArray * ma = RefToMemoryArray(reference); assert(ma); if (!ma) { return NULL; } map = ma->createMappingInTask(kernel_task, 0, options); if (map) { startAddr = map->getAddress(); *mapAddr = startAddr; *mapSize = map->getSize(); DLOG("map vaddr 0x%llx, size 0x%llx", *mapAddr, *mapSize); } return map; } IOReturn IOSKMemoryDiscard( IOSKMemoryRef reference ) { IOSKMemoryBuffer * mb = RefToMemoryBuffer(reference); assert(mb); assert(mb->fSpec.purgeable); if (!mb || !mb->fSpec.purgeable) { return kIOReturnBadArgument; } return mb->setPurgeable(kIOMemoryPurgeableEmpty | kIOMemoryPurgeableFaultOnAccess, NULL); } IOReturn IOSKMemoryReclaim( IOSKMemoryRef reference ) { IOSKMemoryBuffer * mb = RefToMemoryBuffer(reference); assert(mb); assert(mb->fSpec.purgeable); if (!mb || !mb->fSpec.purgeable) { return kIOReturnBadArgument; } return mb->setPurgeable(kIOMemoryPurgeableNonVolatile, NULL); } IOReturn IOSKMemoryWire( IOSKMemoryRef reference ) { IOSKMemoryBuffer * mb = RefToMemoryBuffer(reference); assert(mb); assert(mb->fSpec.purgeable); if (!mb || !mb->fSpec.purgeable) { return kIOReturnBadArgument; } return mb->prepare(); } IOReturn IOSKMemoryUnwire( IOSKMemoryRef reference ) { IOSKMemoryBuffer * mb = RefToMemoryBuffer(reference); assert(mb); assert(mb->fSpec.purgeable); if (!mb || !mb->fSpec.purgeable) { return kIOReturnBadArgument; } return mb->complete(); } static void IOSKObjectDestroy( const OSObject * object ) { assert(object != NULL); if (object) { object->release(); } } IOSKArenaRef IOSKArenaCreate( IOSKRegionRef * regionList, IOSKCount regionCount ) { IOSKArenaRef arena; arena = new IOSKArena; if ((arena != NULL) && !arena->initWithRegions(regionList, regionCount)) { arena->release(); arena = NULL; } return arena; } void IOSKArenaDestroy( IOSKArenaRef arena ) { IOSKObjectDestroy(arena); } void IOSKArenaRedirect( IOSKArenaRef arena ) { assert(arena != NULL); if (arena != NULL) { arena->redirectMap(NULL); } } IOSKRegionRef IOSKRegionCreate( const IOSKRegionSpec * regionSpec, IOSKSize segSize, IOSKCount segCount ) { IOSKRegionRef region; region = new IOSKRegion; if ((region != NULL) && !region->initWithSpec(regionSpec, segSize, segCount)) { region->release(); region = NULL; } return region; } void IOSKRegionDestroy( IOSKRegionRef region ) { IOSKObjectDestroy(region); } IOReturn IOSKRegionSetBuffer( IOSKRegionRef region, IOSKIndex segmentIndex, IOSKMemoryBufferRef buffer ) { IOReturn ret = kIOReturnBadArgument; assert(region != NULL); if (region != NULL) { ret = region->setSegmentBuffer(segmentIndex, (IOSKBuffer *)buffer); } return ret; } void IOSKRegionClearBuffer( IOSKRegionRef region, IOSKIndex segmentIndex ) { assert(region != NULL); if (region != NULL) { region->clearSegmentBuffer(segmentIndex, NULL); } } void IOSKRegionClearBufferDebug( IOSKRegionRef region, IOSKIndex segmentIndex, IOSKMemoryBufferRef * prevBufferRef ) { assert(region != NULL); if (region != NULL) { region->clearSegmentBuffer(segmentIndex, prevBufferRef); } } IOSKMapperRef IOSKMapperCreate( IOSKArenaRef arena, task_t task ) { IOSKMapperRef mapper = NULL; assert(arena != NULL); if (arena != NULL) { arena->createMapperForTask(task, &mapper); } return mapper; } void IOSKMapperDestroy( IOSKMapperRef mapper ) { assert(mapper != NULL); if (mapper != NULL) { IOSKArena * arena = mapper->getArena(); assert(arena != NULL); arena->removeMapper(mapper); IOSKObjectDestroy(mapper); } } void IOSKMapperRedirect( IOSKMapperRef mapper ) { assert(mapper != NULL); if (mapper != NULL) { IOSKArena * arena = mapper->getArena(); assert(arena != NULL); arena->redirectMap(mapper); } } IOReturn IOSKMapperGetAddress( IOSKMapperRef mapper, mach_vm_address_t * address, mach_vm_size_t * size ) { assert(mapper != NULL); if ((mapper == NULL) || (address == NULL)) { return kIOReturnBadArgument; } *address = mapper->getMapAddress(size); return kIOReturnSuccess; } boolean_t IOSKBufferIsWired( IOSKMemoryBufferRef buffer ) { assert(buffer != NULL); return ((IOSKBuffer *)buffer)->isWired(); } __END_DECLS #if DEVELOPMENT || DEBUG extern int IOSkywalkSupportTest(int x); int IOSkywalkSupportTest( int newValue ) { static const int kNumRegions = 3; static const int kNumBuffers = 6; static const int kNumMappers = 3; static const int kNumArenas = 2; IOSKMemoryBufferSpec bspec; IOSKRegionSpec rspec; IOSKMemoryBufferRef buffers[kNumBuffers]; mach_vm_address_t bufkvas[kNumBuffers]; IOSKRegionRef regions[kNumRegions]; IOSKRegionRef reverse[kNumRegions]; IOSKArenaRef arenas[kNumArenas]; IOSKMapperRef mappers[kNumMappers]; mach_vm_address_t addrs[kNumMappers]; mach_vm_size_t size; uint32_t value; uint32_t * ptr; IOReturn ret; kprintf("IOSKArena count : %u\n", IOSKArena::gMetaClass.getInstanceCount()); kprintf("IOSKRegion count : %u\n", IOSKRegion::gMetaClass.getInstanceCount()); kprintf("IOSKMapper count : %u, %u (sub maps)\n", IOSKMapper::gMetaClass.getInstanceCount(), IOSKRegionMapper::gMetaClass.getInstanceCount()); kprintf("IOSKBuffer count : %u\n", IOSKBuffer::gMetaClass.getInstanceCount()); rspec.noRedirect = true; regions[0] = IOSKRegionCreate(&rspec, (IOSKSize) ptoa(1), 2); assert(regions[0]); rspec.noRedirect = false; regions[1] = IOSKRegionCreate(&rspec, (IOSKSize) ptoa(2), 3); assert(regions[1]); regions[2] = IOSKRegionCreate(&rspec, (IOSKSize) ptoa(3), 4); assert(regions[2]); reverse[0] = regions[2]; reverse[1] = regions[1]; reverse[2] = regions[0]; arenas[0] = IOSKArenaCreate(regions, 3); assert(arenas[0]); arenas[1] = IOSKArenaCreate(reverse, 3); assert(arenas[1]); bzero(&bspec, sizeof(bspec)); bspec.purgeable = true; bspec.user_writable = false; buffers[0] = IOSKMemoryBufferCreate(ptoa(1), &bspec, &bufkvas[0]); assert(buffers[0]); assert(IOSKBufferIsWired(buffers[0]) == false); bspec.user_writable = true; buffers[1] = IOSKMemoryBufferCreate(ptoa(1), &bspec, &bufkvas[1]); assert(buffers[1]); buffers[2] = IOSKMemoryBufferCreate(ptoa(2), &bspec, &bufkvas[2]); assert(buffers[2]); buffers[3] = IOSKMemoryBufferCreate(ptoa(2), &bspec, &bufkvas[3]); assert(buffers[3]); buffers[4] = IOSKMemoryBufferCreate(ptoa(3), &bspec, &bufkvas[4]); assert(buffers[4]); buffers[5] = IOSKMemoryBufferCreate(ptoa(3), &bspec, &bufkvas[5]); assert(buffers[5]); for (int i = 0; i < kNumBuffers; i++) { value = 0x534B0000 | i; ptr = (uint32_t *)(uintptr_t)bufkvas[i]; *ptr = value; assert(value == *ptr); } ret = IOSKRegionSetBuffer(regions[0], 0, buffers[0]); assert(ret == kIOReturnSuccess); ret = IOSKRegionSetBuffer(regions[0], 1, buffers[1]); assert(ret == kIOReturnSuccess); ret = IOSKRegionSetBuffer(regions[1], 0, buffers[2]); assert(ret == kIOReturnSuccess); ret = IOSKRegionSetBuffer(regions[1], 1, buffers[3]); assert(ret == kIOReturnSuccess); ret = IOSKRegionSetBuffer(regions[2], 0, buffers[4]); assert(ret == kIOReturnSuccess); ret = IOSKRegionSetBuffer(regions[2], 3, buffers[5]); assert(ret == kIOReturnSuccess); mappers[0] = IOSKMapperCreate(arenas[0], current_task()); assert(mappers[0]); mappers[1] = IOSKMapperCreate(arenas[0], current_task()); assert(mappers[1]); mappers[2] = IOSKMapperCreate(arenas[1], current_task()); assert(mappers[2]); ret = IOSKMapperGetAddress(mappers[0], &addrs[0], &size); assert(ret == kIOReturnSuccess); assert(size == ptoa(20)); ret = IOSKMapperGetAddress(mappers[1], &addrs[1], &size); assert(ret == kIOReturnSuccess); assert(size == ptoa(20)); ret = IOSKMapperGetAddress(mappers[2], &addrs[2], &size); assert(ret == kIOReturnSuccess); assert(size == ptoa(20)); for (int i = 0; i < kNumMappers; i++) { kprintf("mapper[%d] %p map address 0x%llx size 0x%x\n", i, mappers[i], (uint64_t)addrs[i], (uint32_t)size); } ptr = (uint32_t *)(uintptr_t)addrs[0]; assert(*ptr == 0x534B0000); ptr = (uint32_t *)(uintptr_t)(addrs[0] + ptoa(1)); assert(*ptr == 0x534B0001); ptr = (uint32_t *)(uintptr_t)(addrs[0] + ptoa(2)); assert(*ptr == 0x534B0002); ptr = (uint32_t *)(uintptr_t)(addrs[0] + ptoa(4)); assert(*ptr == 0x534B0003); ptr = (uint32_t *)(uintptr_t)(addrs[0] + ptoa(8)); assert(*ptr == 0x534B0004); ptr = (uint32_t *)(uintptr_t)(addrs[0] + ptoa(17)); assert(*ptr == 0x534B0005); *ptr = 0x4B530005; assert(0x4B530005 == *ptr); *ptr = 0x534B0005; IOSKMapperRedirect(mappers[0]); *ptr = 0x33333333; assert(0x33333333 == *ptr); ptr = (uint32_t *)(uintptr_t)addrs[0]; assert(*ptr == 0x534B0000); ptr = (uint32_t *)(uintptr_t)addrs[2]; assert(*ptr == 0x534B0004); ptr = (uint32_t *)(uintptr_t)(addrs[2] + ptoa(9)); assert(*ptr == 0x534B0005); ptr = (uint32_t *)(uintptr_t)(addrs[2] + ptoa(12)); assert(*ptr == 0x534B0002); ptr = (uint32_t *)(uintptr_t)(addrs[2] + ptoa(14)); assert(*ptr == 0x534B0003); ptr = (uint32_t *)(uintptr_t)(addrs[2] + ptoa(18)); assert(*ptr == 0x534B0000); ptr = (uint32_t *)(uintptr_t)(addrs[2] + ptoa(19)); assert(*ptr == 0x534B0001); IOSKRegionClearBufferDebug(regions[0], 1, NULL); ret = IOSKRegionSetBuffer(regions[0], 1, buffers[1]); assert(ret == kIOReturnSuccess); assert(*ptr == 0x534B0001); IOSKArenaRedirect(arenas[0]); IOSKArenaRedirect(arenas[1]); for (int i = 0; i < kNumBuffers; i++) { IOSKMemoryDestroy(buffers[i]); } for (int i = 0; i < kNumRegions; i++) { IOSKRegionDestroy(regions[i]); } for (int i = 0; i < kNumArenas; i++) { IOSKArenaDestroy(arenas[i]); } for (int i = 0; i < kNumMappers; i++) { IOSKMapperDestroy(mappers[i]); } kprintf("IOSKArena count : %u\n", IOSKArena::gMetaClass.getInstanceCount()); kprintf("IOSKRegion count : %u\n", IOSKRegion::gMetaClass.getInstanceCount()); kprintf("IOSKMapper count : %u, %u (sub maps)\n", IOSKMapper::gMetaClass.getInstanceCount(), IOSKRegionMapper::gMetaClass.getInstanceCount()); kprintf("IOSKBuffer count : %u\n", IOSKBuffer::gMetaClass.getInstanceCount()); return 0; } #endif /* DEVELOPMENT || DEBUG */ #if defined(__x86_64__) const OSSymbol * IOSKCopyKextIdentifierWithAddress( vm_address_t address ) { const OSSymbol * id = NULL; OSKext * kext = OSKext::lookupKextWithAddress(address); if (kext) { id = kext->getIdentifier(); if (id) { id->retain(); } kext->release(); } return id; } #endif /* __x86_64__ */