1/*
2 * Copyright (c) 1998-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <IOKit/firewire/IOFWAddressSpace.h>
24#include <IOKit/firewire/IOFireWireController.h>
25
26#include "FWDebugging.h"
27
28#include <IOKit/IOKitKeysPrivate.h>
29#include <IOKit/IODMACommand.h>
30
31/*
32 * Direct physical memory <-> FireWire address.
33 * Accesses to these addresses will be handled automatically by the
34 * hardware without notification.
35 *
36 * The following is currently true, though the code no longer makes such assumptions :
37 * The 64 bit FireWire address of (32 bit) physical addr xxxx:xxxx is hostNode:0000:xxxx:xxxx
38 */
39
40OSDefineMetaClassAndStructors(IOFWPhysicalAddressSpaceAux, IOFWAddressSpaceAux);
41
42OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 0);
43OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 1);
44OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 2);
45OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 3);
46OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 4);
47OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 5);
48OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 6);
49OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 7);
50OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 8);
51OSMetaClassDefineReservedUnused(IOFWPhysicalAddressSpaceAux, 9);
52
53#pragma mark -
54
55OSDefineMetaClassAndStructors(IOFWPhysicalAddressSpace, IOFWAddressSpace)
56
57// init
58//
59//
60
61bool IOFWPhysicalAddressSpace::init( IOFireWireBus * bus )
62{
63	bool success = true;		// assume success
64
65	// init super
66
67    if( !IOFWAddressSpace::init( bus ) )
68        success = false;
69
70	return success;
71}
72
73// createAuxiliary
74//
75// virtual method for creating auxiliary object.  subclasses needing to subclass
76// the auxiliary object can override this.
77
78IOFWAddressSpaceAux * IOFWPhysicalAddressSpace::createAuxiliary( void )
79{
80	IOFWPhysicalAddressSpaceAux * auxiliary;
81
82	auxiliary = OSTypeAlloc( IOFWPhysicalAddressSpaceAux );
83
84    if( auxiliary != NULL && !auxiliary->init(this) )
85	{
86        auxiliary->release();
87        auxiliary = NULL;
88    }
89
90    return (IOFWAddressSpaceAux*)auxiliary;
91}
92
93// checkMemoryInRange
94//
95//
96
97IOReturn IOFWPhysicalAddressSpace::checkMemoryInRange( IOMemoryDescriptor * memory )
98{
99	IOReturn status = kIOReturnSuccess;
100
101	if( memory == NULL )
102	{
103		status = kIOReturnBadArgument;
104	}
105
106	//
107	// setup
108	//
109
110	bool memory_prepared = false;
111	if( status == kIOReturnSuccess )
112	{
113		status = memory->prepare( kIODirectionInOut );
114	}
115
116	if( status == kIOReturnSuccess )
117	{
118		memory_prepared = true;
119	}
120
121	UInt64 length = 0;
122	if( status == kIOReturnSuccess )
123	{
124		length = memory->getLength();
125		if( length == 0 )
126		{
127			status = kIOReturnError;
128		}
129	}
130
131	//
132	// create IODMACommand
133	//
134
135	IODMACommand * dma_command = NULL;
136	if( status == kIOReturnSuccess )
137	{
138		dma_command = IODMACommand::withSpecification(
139												kIODMACommandOutputHost64,		// segment function
140												64,								// max address bits
141												length,							// max segment size
142												(IODMACommand::MappingOptions)(IODMACommand::kMapped | IODMACommand::kIterateOnly),		// IO mapped & don't bounce buffer
143												length,							// max transfer size
144												0,								// page alignment
145												NULL,							// mapper
146												NULL );							// refcon
147		if( dma_command == NULL )
148			status = kIOReturnError;
149
150	}
151
152	if( status == kIOReturnSuccess )
153	{
154		// set memory descriptor and don't prepare it
155		status = dma_command->setMemoryDescriptor( memory, false );
156	}
157
158	bool dma_command_prepared = false;
159	if( status == kIOReturnSuccess )
160	{
161		status = dma_command->prepare( 0, length, true );
162	}
163
164	if( status == kIOReturnSuccess )
165	{
166		dma_command_prepared = true;
167	}
168
169	//
170	// check ranges
171	//
172
173	if( status == kIOReturnSuccess )
174	{
175		UInt64 offset = 0;
176		UInt64 mask = fControl->getFireWirePhysicalAddressMask();
177		while( (offset < length) && (status == kIOReturnSuccess) )
178		{
179			IODMACommand::Segment64 segments[10];
180			UInt32 num_segments = 10;
181			status = dma_command->gen64IOVMSegments( &offset, segments, &num_segments );
182			if( status == kIOReturnSuccess )
183			{
184				for( UInt32 i = 0; i < num_segments; i++ )
185				{
186				//	IOLog( "checkSegments - segments[%d].fIOVMAddr = 0x%016llx, fLength = %d\n", i, segments[i].fIOVMAddr, segments[i].fLength  );
187
188					if( (segments[i].fIOVMAddr & (~mask)) )
189					{
190				//		IOLog( "checkSegmentsFailed - 0x%016llx & 0x%016llx\n", segments[i].fIOVMAddr, mask );
191						status = kIOReturnNotPermitted;
192						break;
193					}
194				}
195			}
196		}
197	}
198
199	//
200	// clean up
201	//
202
203	if( dma_command_prepared )
204	{
205		dma_command->complete();
206		dma_command_prepared = false;
207	}
208
209	if( dma_command )
210	{
211		dma_command->clearMemoryDescriptor();
212		dma_command->release();
213		dma_command = NULL;
214	}
215
216	if( memory_prepared )
217	{
218		memory->complete();
219		memory_prepared = false;
220	}
221
222	return status;
223
224}
225
226// initWithDesc
227//
228//
229
230bool IOFWPhysicalAddressSpace::initWithDesc( IOFireWireBus *control,
231                                             IOMemoryDescriptor * mem )
232{
233    if(!IOFWAddressSpace::init(control))
234		return false;
235
236//	IOLog( "IOFWPhysicalAddressSpace::initWithDesc\n" );
237
238	IOReturn status = kIOReturnSuccess;
239
240	if( status == kIOReturnSuccess )
241	{
242		if( mem != NULL )
243		{
244			status = checkMemoryInRange( mem );
245		}
246	}
247
248//	IOLog( "IOFWPhysicalAddressSpace::initWithDesc (1) - status = 0x%08lx\n", status );
249
250	IODMACommand * dma_command = NULL;
251	if( status == kIOReturnSuccess )
252	{
253		UInt32 address_bits = fControl->getFireWirePhysicalAddressBits();
254		dma_command = IODMACommand::withSpecification(
255												kIODMACommandOutputHost64,		// segment function
256												address_bits,					// max address bits
257												0,								// max segment size
258												IODMACommand::kMapped,			// I/O mapped
259												0,								// max transfer size
260												0,								// no alignment
261												NULL,							// mapper
262												NULL );							// refcon
263		if( dma_command == NULL )
264			status = kIOReturnError;
265
266	}
267
268	if( status == kIOReturnSuccess )
269	{
270		setDMACommand( dma_command );
271		dma_command->release();
272		status = setMemoryDescriptor( mem );
273	}
274
275//	IOLog( "IOFWPhysicalAddressSpace::initWithDesc (2) - status = 0x%08lx\n", status );
276
277    return (status == kIOReturnSuccess);
278}
279
280// initWithDesc
281//
282//
283
284bool IOFWPhysicalAddressSpace::initWithDMACommand(	IOFireWireBus * control,
285													IODMACommand * command )
286{
287    if( !IOFWAddressSpace::init(control) )
288        return false;
289
290	setDMACommand( command );
291
292    return true;
293}
294
295// free
296//
297//
298
299void IOFWPhysicalAddressSpace::free()
300{
301//	IOLog( "IOFWPhysicalAddressSpace::free\n" );
302
303    IOFWAddressSpace::free();
304}
305
306// doRead
307//
308//
309
310UInt32 IOFWPhysicalAddressSpace::doRead(UInt16 nodeID, IOFWSpeed &speed, FWAddress addr, UInt32 len,
311					IOMemoryDescriptor **buf, IOByteCount * offset, IOFWRequestRefCon refcon)
312{
313    UInt32 res = kFWResponseAddressError;
314    UInt64 pos;
315    UInt64 phys;
316
317	if( !isTrustedNode( nodeID ) )
318		return kFWResponseAddressError;
319
320	if( !isPrepared() )
321		return kFWResponseAddressError;
322
323	UInt64 address = ((UInt64)addr.addressHi << 32) | (UInt64)addr.addressLo;
324	UInt64 desc_length = getLength();
325
326    pos = 0;
327    while( pos < desc_length )
328	{
329		bool found = false;
330		UInt64 lengthOfSegment;
331        phys = getPhysicalSegment( pos, &lengthOfSegment );
332
333		if( (address >= phys) && (address < (phys+lengthOfSegment)) )
334		{
335			UInt32 union_length = (lengthOfSegment - (address - phys));
336
337			// check if the request extends beyond this physical segment
338			if( len <= union_length )
339			{
340				found = true;
341			}
342			else
343			{
344				// look ahead for contiguous ranges
345
346				UInt64 contiguous_address = (phys + lengthOfSegment);
347				UInt64 contiguous_pos = (pos + lengthOfSegment);
348				UInt64 contiguous_length = len - union_length;
349				UInt64 contig_phys;
350
351				while( contiguous_pos < desc_length )
352				{
353					contig_phys = getPhysicalSegment( contiguous_pos, &lengthOfSegment );
354					if( contiguous_address != contig_phys )
355					{
356						// not contiguous, bail
357						break;
358					}
359
360					if( contiguous_length <= lengthOfSegment )
361					{
362						// fits in this segment - success
363						found = true;
364						break;
365					}
366
367					contiguous_length -= lengthOfSegment;
368					contiguous_pos += lengthOfSegment;
369					contiguous_address += lengthOfSegment;
370				}
371
372			}
373		}
374
375		if( found )
376		{
377            // OK, block is in space
378			// Set position to exact start
379			*offset = (pos + address - phys);
380            *buf = getMemoryDescriptor();
381            res = kFWResponseComplete;
382            break;
383        }
384
385        pos += lengthOfSegment;
386    }
387
388    return res;
389}
390
391// doWrite
392//
393//
394
395UInt32 IOFWPhysicalAddressSpace::doWrite(UInt16 nodeID, IOFWSpeed &speed, FWAddress addr, UInt32 len,
396                                         const void *buf, IOFWRequestRefCon refcon)
397{
398    UInt32 res = kFWResponseAddressError;
399    UInt64 pos;
400    UInt64 phys;
401
402//	IOLog( "IOFWPhysicalAddressSpace::doWrite\n" );
403
404	if( !isTrustedNode( nodeID ) )
405		return kFWResponseAddressError;
406
407	if( !isPrepared() )
408	{
409		return kFWResponseAddressError;
410	}
411
412	UInt64 address = ((UInt64)addr.addressHi << 32) | (UInt64)addr.addressLo;
413
414	UInt64 desc_length = getLength();
415
416    pos = 0;
417    while(pos < desc_length)
418	{
419		bool found = false;
420		UInt64 lengthOfSegment;
421        phys = getPhysicalSegment(pos, &lengthOfSegment);
422
423//		IOLog( "IOFWPhysicalAddressSpace::doWrite - address = 0x%016llx phys = 0x%016llx\n", address, phys );
424
425		if( (address >= phys) && (address < (phys+lengthOfSegment)) )
426		{
427			UInt32 union_length = (lengthOfSegment - (address - phys));
428
429			// check if the request extends beyond this physical segment
430			if( len <= union_length )
431			{
432				found = true;
433			}
434			else
435			{
436				// look ahead for contiguous ranges
437
438				UInt64 contiguous_address = (phys + lengthOfSegment);
439				UInt64 contiguous_pos = (pos + lengthOfSegment);
440				UInt64 contiguous_length = len - union_length;
441				UInt64 contig_phys;
442
443				while( contiguous_pos < desc_length )
444				{
445					contig_phys = getPhysicalSegment( contiguous_pos, &lengthOfSegment );
446					if( contiguous_address != contig_phys )
447					{
448						// not contiguous, bail
449						break;
450					}
451
452					if( contiguous_length <= lengthOfSegment )
453					{
454						// fits in this segment - success
455						found = true;
456						break;
457					}
458
459					contiguous_length -= lengthOfSegment;
460					contiguous_pos += lengthOfSegment;
461					contiguous_address += lengthOfSegment;
462				}
463
464			}
465		}
466
467		if( found )
468		{
469            // OK, block is in space
470
471			getMemoryDescriptor()->writeBytes( pos + (address - phys), buf, len);
472			getDMACommand()->writeBytes( pos + (address - phys), buf, len );
473
474			// make sure any bounce buffers have the new data
475		//	synchronize( kIODirectionOut );
476
477			res = kFWResponseComplete;
478            break;
479        }
480
481        pos += lengthOfSegment;
482    }
483
484    return res;
485}
486
487// getMemoryDescriptor
488//
489//
490
491IOMemoryDescriptor * IOFWPhysicalAddressSpace::getMemoryDescriptor( void )
492{
493	IOMemoryDescriptor * desc = NULL;
494
495	IODMACommand * dma_command = getDMACommand();
496	if( dma_command )
497	{
498		desc = (IOMemoryDescriptor*)dma_command->getMemoryDescriptor();
499	}
500
501	return desc;
502}
503
504// setMemoryDescriptor
505//
506//
507
508IOReturn IOFWPhysicalAddressSpace::setMemoryDescriptor( IOMemoryDescriptor * descriptor )
509{
510	IOReturn status = kIOReturnSuccess;
511
512	if( isPrepared() )
513	{
514		complete();
515	}
516
517	IODMACommand * dma_command = getDMACommand();
518	if( dma_command == NULL )
519		status = kIOReturnError;
520
521	if( status == kIOReturnSuccess )
522	{
523		if( descriptor == NULL )
524		{
525			dma_command->clearMemoryDescriptor();
526		}
527		else
528		{
529			dma_command->clearMemoryDescriptor();
530			status = dma_command->setMemoryDescriptor( descriptor, false );
531			if( status == kIOReturnSuccess )
532			{
533				prepare();
534			}
535		}
536	}
537
538	return status;
539}
540
541// getLength
542//
543//
544
545UInt64 IOFWPhysicalAddressSpace::getLength( void )
546{
547	UInt64 length = 0;
548
549	IOMemoryDescriptor * desc = getMemoryDescriptor();
550	if( desc )
551	{
552		length = desc->getLength();
553	}
554
555	return length;
556}
557
558/////////////////////////////////////////////////////////////////////////////////////
559#pragma mark -
560
561// init
562//
563//
564
565bool IOFWPhysicalAddressSpaceAux::init( IOFWAddressSpace * primary )
566{
567	bool success = true;		// assume success
568
569	// init super
570
571    if( !IOFWAddressSpaceAux::init( primary ) )
572        success = false;
573
574//	IOLog( "IOFWPhysicalAddressSpaceAux::init\n" );
575
576	if( success )
577	{
578		fDMACommand = NULL;
579	}
580
581	if( !success )
582	{
583	}
584
585	return success;
586}
587
588// free
589//
590//
591
592void IOFWPhysicalAddressSpaceAux::free()
593{
594//	IOLog( "IOFWPhysicalAddressSpaceAux::free\n" );
595
596	if( isPrepared() )
597	{
598		complete();
599	}
600
601	if( fDMACommand )
602	{
603		fDMACommand->clearMemoryDescriptor();
604		fDMACommand->release();
605		fDMACommand = NULL;
606	}
607
608	IOFWAddressSpaceAux::free();
609}
610
611// setDMACommand
612//
613//
614
615void IOFWPhysicalAddressSpaceAux::setDMACommand( IODMACommand * dma_command )
616{
617	if( fDMACommandPrepared )
618	{
619		complete();
620	}
621
622	IODMACommand * old = fDMACommand;
623	fDMACommand = dma_command;
624
625	if( fDMACommand )
626	{
627		fDMACommand->retain();
628	}
629
630	if( old )
631	{
632		old->release();
633	}
634}
635
636// setDMACommand
637//
638//
639
640IODMACommand * IOFWPhysicalAddressSpaceAux::getDMACommand( void )
641{
642	return fDMACommand;
643}
644
645// isPrepared
646//
647//
648
649bool IOFWPhysicalAddressSpaceAux::isPrepared( void )
650{
651	return fDMACommandPrepared;
652}
653
654// getPhysicalSegment
655//
656//
657
658UInt64 IOFWPhysicalAddressSpaceAux::getPhysicalSegment( UInt64 offset, UInt64 * length )
659{
660	IOReturn status = kIOReturnSuccess;
661
662	UInt64 	phys = 0;
663
664	IODMACommand::Segment64 segment;
665	UInt32 numSegments = 1;
666	UInt64 pos = offset;
667	if( status == kIOReturnSuccess )
668	{
669		status = fDMACommand->gen64IOVMSegments( &pos, &segment, &numSegments );
670	}
671
672	if( status == kIOReturnSuccess )
673	{
674		if( numSegments != 1 )
675		{
676			status = kIOReturnNoMemory;
677		}
678	}
679
680	if( status == kIOReturnSuccess )
681	{
682		phys = segment.fIOVMAddr;
683		*length = segment.fLength;
684	}
685
686	return phys;
687}
688
689// prepare
690//
691//
692
693IOReturn IOFWPhysicalAddressSpaceAux::prepare( void )
694{
695	IOReturn status = kIOReturnSuccess;
696
697//	IOLog( "IOFWPhysicalAddressSpaceAux::prepare\n" );
698
699	if( !fDMACommandPrepared )
700	{
701		UInt64 desc_length = ((IOFWPhysicalAddressSpace*)fPrimary)->getLength();
702		status = fDMACommand->prepare( 0, desc_length );
703		if( status == kIOReturnSuccess )
704		{
705			fDMACommandPrepared = true;
706		}
707	}
708
709	return status;
710}
711
712// complete
713//
714//
715
716IOReturn IOFWPhysicalAddressSpaceAux::complete( void )
717{
718	IOReturn status = kIOReturnSuccess;
719
720//	IOLog( "IOFWPhysicalAddressSpaceAux::complete\n" );
721
722	if( !fDMACommandPrepared )
723	{
724		status = kIOReturnNotReady;
725	}
726
727	if( status == kIOReturnSuccess )
728	{
729		status = fDMACommand->complete();
730		if( status == kIOReturnSuccess )
731		{
732			fDMACommandPrepared = false;
733		}
734	}
735
736	return status;
737}
738
739// synchronize
740//
741//
742
743IOReturn IOFWPhysicalAddressSpaceAux::synchronize( IOOptionBits options )
744{
745	IOReturn status = kIOReturnSuccess;
746
747//	IOLog( "IOFWPhysicalAddressSpaceAux::synchronize - direction = %d\n", direction );
748
749	if( !fDMACommandPrepared )
750	{
751		status = kIOReturnNotReady;
752	}
753
754	if( status == kIOReturnSuccess )
755	{
756		status = fDMACommand->synchronize( options );
757	}
758
759	return status;
760}
761
762// getSegments
763//
764//
765
766IOReturn IOFWPhysicalAddressSpaceAux::getSegments( UInt64 * offset, FWSegment * fw_segments, UInt32 * num_segments )
767{
768	IOReturn status = kIOReturnSuccess;
769
770	IODMACommand::Segment64	* vm_segments = NULL;
771	UInt32 vm_segments_size = 0;
772
773	if( (offset == NULL) || (fw_segments == NULL) || (num_segments == NULL) )
774	{
775		status = kIOReturnBadArgument;
776	}
777
778	if( status == kIOReturnSuccess )
779	{
780		vm_segments_size = sizeof(IODMACommand::Segment64) * (*num_segments);
781		vm_segments = (IODMACommand::Segment64*)IOMalloc( vm_segments_size );
782		if( vm_segments == NULL )
783			status = kIOReturnNoMemory;
784	}
785
786	if( status == kIOReturnSuccess )
787	{
788		IODMACommand * dma_command = getDMACommand();
789		status = dma_command->gen64IOVMSegments( offset, vm_segments, num_segments );
790	}
791
792	if( status == kIOReturnSuccess )
793	{
794		for( UInt32 i = 0; i < *num_segments; i++ )
795		{
796			fw_segments[i].length = vm_segments[i].fLength;
797			fw_segments[i].address.nodeID = 0x0000;		// invalid node id
798			fw_segments[i].address.addressHi = (vm_segments[i].fIOVMAddr >> 32) & 0x000000000000ffffULL;
799			fw_segments[i].address.addressLo = vm_segments[i].fIOVMAddr & 0x00000000ffffffffULL;
800		}
801	}
802
803	if( fw_segments != NULL )
804	{
805		IOFree( vm_segments, vm_segments_size );
806		vm_segments = NULL;
807	}
808
809	return status;
810}
811