IOFWPhysicalAddressSpace.cpp [plain text]
#include <IOKit/firewire/IOFWAddressSpace.h>
#include <IOKit/firewire/IOFireWireController.h>
#include "FWDebugging.h"
#include <IOKit/IOKitKeysPrivate.h>
#include <IOKit/IODMACommand.h>
OSDefineMetaClassAndStructors(IOFWPhysicalAddressSpaceAux, IOFWAddressSpaceAux);
OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 0);
OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 1);
OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 2);
OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 3);
OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 4);
OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 5);
OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 6);
OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 7);
OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 8);
OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 9);
#pragma mark -
OSDefineMetaClassAndStructors(IOFWPhysicalAddressSpace, IOFWAddressSpace)
bool IOFWPhysicalAddressSpace::init( IOFireWireBus * bus )
{
bool success = true;
if( !IOFWAddressSpace::init( bus ) )
success = false;
return success;
}
IOFWAddressSpaceAux * IOFWPhysicalAddressSpace::createAuxiliary( void )
{
IOFWPhysicalAddressSpaceAux * auxiliary;
auxiliary = OSTypeAlloc( IOFWPhysicalAddressSpaceAux );
if( auxiliary != NULL && !auxiliary->init(this) )
{
auxiliary->release();
auxiliary = NULL;
}
return (IOFWAddressSpaceAux*)auxiliary;
}
IOReturn IOFWPhysicalAddressSpace::checkMemoryInRange( IOMemoryDescriptor * memory )
{
IOReturn status = kIOReturnSuccess;
if( memory == NULL )
{
status = kIOReturnBadArgument;
}
bool memory_prepared = false;
if( status == kIOReturnSuccess )
{
status = memory->prepare( kIODirectionInOut );
}
if( status == kIOReturnSuccess )
{
memory_prepared = true;
}
UInt64 length = 0;
if( status == kIOReturnSuccess )
{
length = memory->getLength();
if( length == 0 )
{
status = kIOReturnError;
}
}
IODMACommand * dma_command = NULL;
if( status == kIOReturnSuccess )
{
dma_command = IODMACommand::withSpecification(
kIODMACommandOutputHost64, 64, length, (IODMACommand::MappingOptions)(IODMACommand::kMapped | IODMACommand::kIterateOnly), length, 0, NULL, NULL ); if( dma_command == NULL )
status = kIOReturnError;
}
if( status == kIOReturnSuccess )
{
status = dma_command->setMemoryDescriptor( memory, false );
}
bool dma_command_prepared = false;
if( status == kIOReturnSuccess )
{
status = dma_command->prepare( 0, length, true );
}
if( status == kIOReturnSuccess )
{
dma_command_prepared = true;
}
if( status == kIOReturnSuccess )
{
UInt64 offset = 0;
UInt64 mask = fControl->getFireWirePhysicalAddressMask();
while( (offset < length) && (status == kIOReturnSuccess) )
{
IODMACommand::Segment64 segments[10];
UInt32 num_segments = 10;
status = dma_command->gen64IOVMSegments( &offset, segments, &num_segments );
if( status == kIOReturnSuccess )
{
for( UInt32 i = 0; i < num_segments; i++ )
{
if( (segments[i].fIOVMAddr & (~mask)) )
{
status = kIOReturnNotPermitted;
break;
}
}
}
}
}
if( dma_command_prepared )
{
dma_command->complete();
dma_command_prepared = false;
}
if( dma_command )
{
dma_command->clearMemoryDescriptor();
dma_command->release();
dma_command = NULL;
}
if( memory_prepared )
{
memory->complete();
memory_prepared = false;
}
return status;
}
bool IOFWPhysicalAddressSpace::initWithDesc( IOFireWireBus *control,
IOMemoryDescriptor * mem )
{
if(!IOFWAddressSpace::init(control))
return false;
IOReturn status = kIOReturnSuccess;
if( status == kIOReturnSuccess )
{
if( mem != NULL )
{
status = checkMemoryInRange( mem );
}
}
IODMACommand * dma_command = NULL;
if( status == kIOReturnSuccess )
{
UInt32 address_bits = fControl->getFireWirePhysicalAddressBits();
dma_command = IODMACommand::withSpecification(
kIODMACommandOutputHost64, address_bits, 0, IODMACommand::kMapped, 0, 0, NULL, NULL ); if( dma_command == NULL )
status = kIOReturnError;
}
if( status == kIOReturnSuccess )
{
setDMACommand( dma_command );
dma_command->release();
status = setMemoryDescriptor( mem );
}
return (status == kIOReturnSuccess);
}
bool IOFWPhysicalAddressSpace::initWithDMACommand( IOFireWireBus * control,
IODMACommand * command )
{
if( !IOFWAddressSpace::init(control) )
return false;
setDMACommand( command );
return true;
}
void IOFWPhysicalAddressSpace::free()
{
IOFWAddressSpace::free();
}
UInt32 IOFWPhysicalAddressSpace::doRead(UInt16 nodeID, IOFWSpeed &speed, FWAddress addr, UInt32 len,
IOMemoryDescriptor **buf, IOByteCount * offset, IOFWRequestRefCon refcon)
{
UInt32 res = kFWResponseAddressError;
UInt64 pos;
UInt64 phys;
if( !isTrustedNode( nodeID ) )
return kFWResponseAddressError;
if( !isPrepared() )
return kFWResponseAddressError;
UInt64 address = ((UInt64)addr.addressHi << 32) | (UInt64)addr.addressLo;
UInt64 desc_length = getLength();
pos = 0;
while( pos < desc_length )
{
bool found = false;
UInt64 lengthOfSegment;
phys = getPhysicalSegment( pos, &lengthOfSegment );
if( (address >= phys) && (address < (phys+lengthOfSegment)) )
{
UInt32 union_length = (lengthOfSegment - (address - phys));
if( len <= union_length )
{
found = true;
}
else
{
UInt64 contiguous_address = (phys + lengthOfSegment);
UInt64 contiguous_pos = (pos + lengthOfSegment);
UInt64 contiguous_length = len - union_length;
UInt64 contig_phys;
while( contiguous_pos < desc_length )
{
contig_phys = getPhysicalSegment( contiguous_pos, &lengthOfSegment );
if( contiguous_address != contig_phys )
{
break;
}
if( contiguous_length <= lengthOfSegment )
{
found = true;
break;
}
contiguous_length -= lengthOfSegment;
contiguous_pos += lengthOfSegment;
contiguous_address += lengthOfSegment;
}
}
}
if( found )
{
*offset = (pos + address - phys);
*buf = getMemoryDescriptor();
res = kFWResponseComplete;
break;
}
pos += lengthOfSegment;
}
return res;
}
UInt32 IOFWPhysicalAddressSpace::doWrite(UInt16 nodeID, IOFWSpeed &speed, FWAddress addr, UInt32 len,
const void *buf, IOFWRequestRefCon refcon)
{
UInt32 res = kFWResponseAddressError;
UInt64 pos;
UInt64 phys;
if( !isTrustedNode( nodeID ) )
return kFWResponseAddressError;
if( !isPrepared() )
{
return kFWResponseAddressError;
}
UInt64 address = ((UInt64)addr.addressHi << 32) | (UInt64)addr.addressLo;
UInt64 desc_length = getLength();
pos = 0;
while(pos < desc_length)
{
bool found = false;
UInt64 lengthOfSegment;
phys = getPhysicalSegment(pos, &lengthOfSegment);
if( (address >= phys) && (address < (phys+lengthOfSegment)) )
{
UInt32 union_length = (lengthOfSegment - (address - phys));
if( len <= union_length )
{
found = true;
}
else
{
UInt64 contiguous_address = (phys + lengthOfSegment);
UInt64 contiguous_pos = (pos + lengthOfSegment);
UInt64 contiguous_length = len - union_length;
UInt64 contig_phys;
while( contiguous_pos < desc_length )
{
contig_phys = getPhysicalSegment( contiguous_pos, &lengthOfSegment );
if( contiguous_address != contig_phys )
{
break;
}
if( contiguous_length <= lengthOfSegment )
{
found = true;
break;
}
contiguous_length -= lengthOfSegment;
contiguous_pos += lengthOfSegment;
contiguous_address += lengthOfSegment;
}
}
}
if( found )
{
getMemoryDescriptor()->writeBytes( pos + (address - phys), buf, len);
getDMACommand()->writeBytes( pos + (address - phys), buf, len );
res = kFWResponseComplete;
break;
}
pos += lengthOfSegment;
}
return res;
}
IOMemoryDescriptor * IOFWPhysicalAddressSpace::getMemoryDescriptor( void )
{
IOMemoryDescriptor * desc = NULL;
IODMACommand * dma_command = getDMACommand();
if( dma_command )
{
desc = (IOMemoryDescriptor*)dma_command->getMemoryDescriptor();
}
return desc;
}
IOReturn IOFWPhysicalAddressSpace::setMemoryDescriptor( IOMemoryDescriptor * descriptor )
{
IOReturn status = kIOReturnSuccess;
if( isPrepared() )
{
complete();
}
IODMACommand * dma_command = getDMACommand();
if( dma_command == NULL )
status = kIOReturnError;
if( status == kIOReturnSuccess )
{
if( descriptor == NULL )
{
dma_command->clearMemoryDescriptor();
}
else
{
dma_command->clearMemoryDescriptor();
status = dma_command->setMemoryDescriptor( descriptor, false );
if( status == kIOReturnSuccess )
{
prepare();
}
}
}
return status;
}
UInt64 IOFWPhysicalAddressSpace::getLength( void )
{
UInt64 length = 0;
IOMemoryDescriptor * desc = getMemoryDescriptor();
if( desc )
{
length = desc->getLength();
}
return length;
}
#pragma mark -
bool IOFWPhysicalAddressSpaceAux::init( IOFWAddressSpace * primary )
{
bool success = true;
if( !IOFWAddressSpaceAux::init( primary ) )
success = false;
if( success )
{
fDMACommand = NULL;
}
if( !success )
{
}
return success;
}
void IOFWPhysicalAddressSpaceAux::free()
{
if( isPrepared() )
{
complete();
}
if( fDMACommand )
{
fDMACommand->clearMemoryDescriptor();
fDMACommand->release();
fDMACommand = NULL;
}
IOFWAddressSpaceAux::free();
}
void IOFWPhysicalAddressSpaceAux::setDMACommand( IODMACommand * dma_command )
{
if( fDMACommandPrepared )
{
complete();
}
IODMACommand * old = fDMACommand;
fDMACommand = dma_command;
if( fDMACommand )
{
fDMACommand->retain();
}
if( old )
{
old->release();
}
}
IODMACommand * IOFWPhysicalAddressSpaceAux::getDMACommand( void )
{
return fDMACommand;
}
bool IOFWPhysicalAddressSpaceAux::isPrepared( void )
{
return fDMACommandPrepared;
}
UInt64 IOFWPhysicalAddressSpaceAux::getPhysicalSegment( UInt64 offset, UInt64 * length )
{
IOReturn status = kIOReturnSuccess;
UInt64 phys = 0;
IODMACommand::Segment64 segment;
UInt32 numSegments = 1;
UInt64 pos = offset;
if( status == kIOReturnSuccess )
{
status = fDMACommand->gen64IOVMSegments( &pos, &segment, &numSegments );
}
if( status == kIOReturnSuccess )
{
if( numSegments != 1 )
{
status = kIOReturnNoMemory;
}
}
if( status == kIOReturnSuccess )
{
phys = segment.fIOVMAddr;
*length = segment.fLength;
}
return phys;
}
IOReturn IOFWPhysicalAddressSpaceAux::prepare( void )
{
IOReturn status = kIOReturnSuccess;
if( !fDMACommandPrepared )
{
UInt64 desc_length = ((IOFWPhysicalAddressSpace*)fPrimary)->getLength();
status = fDMACommand->prepare( 0, desc_length );
if( status == kIOReturnSuccess )
{
fDMACommandPrepared = true;
}
}
return status;
}
IOReturn IOFWPhysicalAddressSpaceAux::complete( void )
{
IOReturn status = kIOReturnSuccess;
if( !fDMACommandPrepared )
{
status = kIOReturnNotReady;
}
if( status == kIOReturnSuccess )
{
status = fDMACommand->complete();
if( status == kIOReturnSuccess )
{
fDMACommandPrepared = false;
}
}
return status;
}
IOReturn IOFWPhysicalAddressSpaceAux::synchronize( IOOptionBits options )
{
IOReturn status = kIOReturnSuccess;
if( !fDMACommandPrepared )
{
status = kIOReturnNotReady;
}
if( status == kIOReturnSuccess )
{
status = fDMACommand->synchronize( options );
}
return status;
}
IOReturn IOFWPhysicalAddressSpaceAux::getSegments( UInt64 * offset, FWSegment * fw_segments, UInt32 * num_segments )
{
IOReturn status = kIOReturnSuccess;
IODMACommand::Segment64 * vm_segments = NULL;
UInt32 vm_segments_size = 0;
if( (offset == NULL) || (fw_segments == NULL) || (num_segments == NULL) )
{
status = kIOReturnBadArgument;
}
if( status == kIOReturnSuccess )
{
vm_segments_size = sizeof(IODMACommand::Segment64) * (*num_segments);
vm_segments = (IODMACommand::Segment64*)IOMalloc( vm_segments_size );
if( vm_segments == NULL )
status = kIOReturnNoMemory;
}
if( status == kIOReturnSuccess )
{
IODMACommand * dma_command = getDMACommand();
status = dma_command->gen64IOVMSegments( offset, vm_segments, num_segments );
}
if( status == kIOReturnSuccess )
{
for( UInt32 i = 0; i < *num_segments; i++ )
{
fw_segments[i].length = vm_segments[i].fLength;
fw_segments[i].address.nodeID = 0x0000; fw_segments[i].address.addressHi = (vm_segments[i].fIOVMAddr >> 32) & 0x000000000000ffffULL;
fw_segments[i].address.addressLo = vm_segments[i].fIOVMAddr & 0x00000000ffffffffULL;
}
}
if( fw_segments != NULL )
{
IOFree( vm_segments, vm_segments_size );
vm_segments = NULL;
}
return status;
}