1/*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2008, Axel D��rfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7#include <stdio.h>
8#include <string.h>
9
10#include <device_manager.h>
11
12#include <vm/vm.h>
13
14#include "dma_resources.h"
15#include "io_requests.h"
16#include "IOSchedulerSimple.h"
17
18
19#define DMA_TEST_BLOCK_SIZE				512
20#define DMA_TEST_BUFFER_COUNT			10
21#define DMA_TEST_BOUNCE_BUFFER_COUNT	128
22
23
24class TestSuite;
25
26class TestSuiteContext {
27public:
28							TestSuiteContext();
29							~TestSuiteContext();
30
31			status_t		Init(size_t size, bool contiguous = true);
32
33			addr_t			DataBase() const { return fDataBase; }
34			addr_t			PhysicalDataBase() const
35								{ return fPhysicalDataBase; }
36			addr_t			SecondPhysicalDataBase() const
37								{ return fSecondPhysicalDataBase; }
38
39			bool			IsContiguous() const
40								{ return fSecondPhysicalDataBase == 0; }
41
42			addr_t			CompareBase() const { return fCompareBase; }
43
44			size_t			Size() const { return fSize; }
45
46private:
47			void			_Uninit();
48
49			area_id			fDataArea;
50			addr_t			fDataBase;
51			addr_t			fPhysicalDataBase;
52			addr_t			fSecondPhysicalDataBase;
53			area_id			fCompareArea;
54			addr_t			fCompareBase;
55			size_t			fSize;
56};
57
58class Test : public DoublyLinkedListLinkImpl<Test> {
59public:
60							Test(TestSuite& suite, off_t offset, size_t length,
61								bool isWrite, uint32 flags);
62
63			Test&			AddSource(addr_t base, size_t length);
64			Test&			NextResult(off_t offset, bool partialBegin,
65								bool partialEnd);
66			Test&			AddTarget(addr_t base, size_t length,
67								bool usesBounceBuffer);
68
69			void			Run(DMAResource& resource);
70
71private:
72			addr_t			_SourceToVirtual(addr_t source);
73			addr_t			_SourceToCompare(addr_t source);
74			void			_Prepare();
75			void			_CheckCompare();
76			void			_CheckWrite();
77			void			_CheckResults();
78			status_t		_DoIO(IOOperation& operation);
79			void			_Panic(const char* message,...);
80
81			TestSuite&		fSuite;
82			off_t			fOffset;
83			size_t			fLength;
84			bool			fIsWrite;
85			uint32			fFlags;
86			generic_io_vec	fSourceVecs[32];
87			uint32			fSourceCount;
88
89			struct target_t {
90				addr_t		address;
91				size_t		length;
92				bool		uses_bounce_buffer;
93			};
94			struct result_t {
95				off_t		offset;
96				target_t	targets[32];
97				uint32		count;
98				bool		partial_begin;
99				bool		partial_end;
100			};
101			result_t		fResults[32];
102			uint32			fResultCount;
103};
104
105typedef DoublyLinkedList<Test> TestList;
106
107
108class TestSuite {
109public:
110	TestSuite(TestSuiteContext& context, const char* name,
111			const dma_restrictions& restrictions, size_t blockSize)
112		:
113		fContext(context)
114	{
115		dprintf("----- Run \"%s\" tests ---------------------------\n", name);
116		dprintf("  DMA restrictions: address %#" B_PRIxGENADDR " - %#"
117			B_PRIxGENADDR ", align %" B_PRIuGENADDR ", boundary %" B_PRIuGENADDR
118			",\n    max transfer %" B_PRIuGENADDR ", max segs %" B_PRIx32
119			", max seg size %" B_PRIxGENADDR ", flags %" B_PRIx32 "\n\n",
120			restrictions.low_address, restrictions.high_address,
121			restrictions.alignment, restrictions.boundary,
122			restrictions.max_transfer_size, restrictions.max_segment_count,
123			restrictions.max_segment_size, restrictions.flags);
124
125		status_t status = fDMAResource.Init(restrictions, blockSize, 10, 10);
126		if (status != B_OK)
127			panic("initializing DMA resource failed: %s\n", strerror(status));
128	}
129
130	~TestSuite()
131	{
132		while (Test* test = fTests.RemoveHead()) {
133			delete test;
134		}
135	}
136
137	Test& AddTest(off_t offset, size_t length, bool isWrite, uint32 flags)
138	{
139		Test* test = new(std::nothrow) Test(*this, offset, length, isWrite,
140			flags);
141		fTests.Add(test);
142
143		return *test;
144	}
145
146	void Run()
147	{
148		TestList::Iterator iterator = fTests.GetIterator();
149		uint32 count = 1;
150		while (Test* test = iterator.Next()) {
151			dprintf("test %lu...\n", count++);
152			test->Run(fDMAResource);
153		}
154	}
155
156	addr_t DataBase() const { return fContext.DataBase(); }
157	addr_t PhysicalDataBase() const { return fContext.PhysicalDataBase(); }
158	addr_t SecondPhysicalDataBase() const
159		{ return fContext.SecondPhysicalDataBase(); }
160	bool IsContiguous() const { return fContext.IsContiguous(); }
161	addr_t CompareBase() const { return fContext.CompareBase(); }
162	size_t Size() const { return fContext.Size(); }
163
164private:
165	TestSuiteContext& fContext;
166	DMAResource		fDMAResource;
167	uint8*			fBase;
168	uint8*			fPhysicalBase;
169	size_t			fSize;
170	TestList		fTests;
171};
172
173
174struct device_manager_info* sDeviceManager;
175
176static area_id sArea;
177static size_t sAreaSize;
178static void* sAreaAddress;
179static DMAResource* sDMAResource;
180static IOScheduler* sIOScheduler;
181
182
183status_t
184do_io(void* data, IOOperation* operation)
185{
186	uint8* disk = (uint8*)sAreaAddress;
187	off_t offset = operation->Offset();
188
189	for (uint32 i = 0; i < operation->VecCount(); i++) {
190		const generic_io_vec& vec = operation->Vecs()[i];
191		generic_addr_t base = vec.base;
192		generic_size_t length = vec.length;
193
194		if (operation->IsWrite())
195			vm_memcpy_from_physical(disk + offset, base, length, false);
196		else
197			vm_memcpy_to_physical(base, disk + offset, length, false);
198	}
199
200	if (sIOScheduler != NULL)
201		sIOScheduler->OperationCompleted(operation, B_OK, operation->Length());
202	return B_OK;
203}
204
205
206//	#pragma mark -
207
208
209TestSuiteContext::TestSuiteContext()
210	:
211	fDataArea(-1),
212	fCompareArea(-1),
213	fSize(0)
214{
215}
216
217
218TestSuiteContext::~TestSuiteContext()
219{
220	_Uninit();
221}
222
223
224void
225TestSuiteContext::_Uninit()
226{
227	delete_area(fDataArea);
228	delete_area(fCompareArea);
229}
230
231
232status_t
233TestSuiteContext::Init(size_t size, bool contiguous)
234{
235	if (!contiguous) {
236		// we can't force this, so we have to try and see
237
238		if (size != B_PAGE_SIZE * 2)
239			return B_NOT_SUPPORTED;
240
241		while (true) {
242			fDataArea = create_area("data buffer", (void**)&fDataBase,
243				B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK,
244				B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
245			if (fDataArea < B_OK)
246				return fDataArea;
247
248			// get memory map to see if we succeeded
249
250			physical_entry entry[2];
251			get_memory_map((void*)fDataBase, 2 * B_PAGE_SIZE, entry, 2);
252
253			if (entry[0].size == B_PAGE_SIZE) {
254				fPhysicalDataBase = (addr_t)entry[0].address;
255				fSecondPhysicalDataBase = (addr_t)entry[1].address;
256
257				dprintf("DMA Test area %p, physical %#" B_PRIxPHYSADDR
258					", second physical %#" B_PRIxPHYSADDR "\n",
259					(void*)fDataBase, entry[0].address, entry[1].address);
260				break;
261			}
262
263			// try again
264			delete_area(fDataArea);
265		}
266	} else {
267		fDataArea = create_area("data buffer", (void**)&fDataBase,
268			B_ANY_KERNEL_ADDRESS, size, B_CONTIGUOUS,
269			B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
270		if (fDataArea < B_OK)
271			return fDataArea;
272
273		physical_entry entry;
274		get_memory_map((void*)fDataBase, B_PAGE_SIZE, &entry, 1);
275
276		fPhysicalDataBase = (addr_t)entry.address;
277		fSecondPhysicalDataBase = 0;
278
279		dprintf("DMA Test area %p, physical %#" B_PRIxPHYSADDR "\n",
280			(void*)fDataBase, entry.address);
281	}
282
283	fCompareArea = create_area("compare buffer", (void**)&fCompareBase,
284		B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK,
285		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
286	if (fCompareArea < B_OK)
287		return fCompareArea;
288
289	fSize = size;
290	return B_OK;
291}
292
293
294//	#pragma mark -
295
296
297Test::Test(TestSuite& suite, off_t offset, size_t length, bool isWrite,
298		uint32 flags)
299	:
300	fSuite(suite),
301	fOffset(offset),
302	fLength(length),
303	fIsWrite(isWrite),
304	fFlags(flags),
305	fSourceCount(0),
306	fResultCount(0)
307{
308}
309
310
311Test&
312Test::AddSource(addr_t address, size_t length)
313{
314	if (fSuite.IsContiguous() || (fFlags & B_PHYSICAL_IO_REQUEST) != 0
315		|| address < B_PAGE_SIZE) {
316		fSourceVecs[fSourceCount].base
317			= ((fFlags & B_PHYSICAL_IO_REQUEST) == 0
318				? fSuite.DataBase() : fSuite.PhysicalDataBase()) + address;
319	} else {
320		fSourceVecs[fSourceCount].base
321			= fSuite.SecondPhysicalDataBase() + address;
322	}
323	fSourceVecs[fSourceCount].length = length;
324	fSourceCount++;
325
326	return *this;
327}
328
329
330Test&
331Test::NextResult(off_t offset, bool partialBegin, bool partialEnd)
332{
333	fResults[fResultCount].offset = offset;
334	fResults[fResultCount].count = 0;
335	fResults[fResultCount].partial_begin = partialBegin;
336	fResults[fResultCount].partial_end = partialEnd;
337	fResultCount++;
338
339	return *this;
340}
341
342
343Test&
344Test::AddTarget(addr_t base, size_t length, bool usesBounceBuffer)
345{
346	struct result_t& result = fResults[fResultCount - 1];
347	struct target_t& target = result.targets[result.count++];
348
349	target.address = base;
350	target.length = length;
351	target.uses_bounce_buffer = usesBounceBuffer;
352
353	return *this;
354}
355
356
357addr_t
358Test::_SourceToVirtual(addr_t source)
359{
360	if ((fFlags & B_PHYSICAL_IO_REQUEST) != 0) {
361		if (!fSuite.IsContiguous() && source >= B_PAGE_SIZE)
362			return source - fSuite.SecondPhysicalDataBase() + fSuite.DataBase();
363
364		return source - fSuite.PhysicalDataBase() + fSuite.DataBase();
365	}
366
367	return source;
368}
369
370
371addr_t
372Test::_SourceToCompare(addr_t source)
373{
374	if ((fFlags & B_PHYSICAL_IO_REQUEST) != 0) {
375		if (!fSuite.IsContiguous() && source >= B_PAGE_SIZE) {
376			return source - fSuite.SecondPhysicalDataBase()
377				+ fSuite.CompareBase();
378		}
379
380		return source - fSuite.PhysicalDataBase() + fSuite.CompareBase();
381	}
382
383	return source - fSuite.DataBase() + fSuite.CompareBase();
384}
385
386
387void
388Test::_Prepare()
389{
390	// prepare disk
391
392	uint8* disk = (uint8*)sAreaAddress;
393	for (size_t i = 0; i < sAreaSize; i++) {
394		disk[i] = i % 26 + 'a';
395	}
396
397	// prepare data
398
399	memset((void*)fSuite.DataBase(), 0xcc, fSuite.Size());
400
401	if (fIsWrite) {
402		off_t offset = fOffset;
403		size_t length = fLength;
404
405		for (uint32 i = 0; i < fSourceCount; i++) {
406			uint8* data = (uint8*)_SourceToVirtual(fSourceVecs[i].base);
407			size_t vecLength = min_c(fSourceVecs[i].length, length);
408
409			for (uint32 j = 0; j < vecLength; j++) {
410				data[j] = (offset + j) % 10 + '0';
411			}
412			offset += vecLength;
413			length -= vecLength;
414		}
415	}
416
417	// prepare compare data
418
419	memset((void*)fSuite.CompareBase(), 0xcc, fSuite.Size());
420
421	if (fIsWrite) {
422		// copy data from source
423		off_t offset = fOffset;
424		size_t length = fLength;
425
426		for (uint32 i = 0; i < fSourceCount; i++) {
427			uint8* compare = (uint8*)_SourceToCompare(fSourceVecs[i].base);
428			size_t vecLength = min_c(fSourceVecs[i].length, length);
429
430			memcpy(compare, (void*)_SourceToVirtual(fSourceVecs[i].base),
431				vecLength);
432			offset += vecLength;
433			length -= vecLength;
434		}
435	} else {
436		// copy data from drive
437		off_t offset = fOffset;
438		size_t length = fLength;
439
440		for (uint32 i = 0; i < fSourceCount; i++) {
441			uint8* compare = (uint8*)_SourceToCompare(fSourceVecs[i].base);
442			size_t vecLength = min_c(fSourceVecs[i].length, length);
443
444			memcpy(compare, disk + offset, vecLength);
445			offset += vecLength;
446			length -= vecLength;
447		}
448	}
449
450	if (fIsWrite)
451		_CheckCompare();
452}
453
454
455void
456Test::_CheckCompare()
457{
458	uint8* data = (uint8*)fSuite.DataBase();
459	uint8* compare = (uint8*)fSuite.CompareBase();
460
461	for (size_t i = 0; i < fSuite.Size(); i++) {
462		if (data[i] != compare[i]) {
463			dprintf("offset %lu differs, %s:\n", i,
464				fIsWrite ? "write" : "read");
465			i &= ~63;
466			dump_block((char*)&data[i], min_c(64, fSuite.Size() - i), "  ");
467			dprintf("should be:\n");
468			dump_block((char*)&compare[i], min_c(64, fSuite.Size() - i), "  ");
469
470			_Panic("Data %s differs", fIsWrite ? "write" : "read");
471		}
472	}
473}
474
475
476void
477Test::_CheckWrite()
478{
479	_CheckCompare();
480
481	// check if we overwrote parts we shouldn't have
482
483	uint8* disk = (uint8*)sAreaAddress;
484	for (size_t i = 0; i < sAreaSize; i++) {
485		if (i >= fOffset && i < fOffset + fLength)
486			continue;
487
488		if (disk[i] != i % 26 + 'a') {
489			dprintf("disk[i] %c, expected %c, i %lu, fLength + fOffset %lld\n",
490				disk[i], (int)(i % 26 + 'a'), i, fLength + fOffset);
491			dprintf("offset %lu differs, touched innocent data:\n", i);
492			i &= ~63;
493			dump_block((char*)&disk[i], min_c(64, fSuite.Size() - i), "  ");
494
495			_Panic("Data %s differs", fIsWrite ? "write" : "read");
496		}
497	}
498
499	// check if the data we wanted to have on disk ended up there
500
501	off_t offset = fOffset;
502	size_t length = fLength;
503
504	for (uint32 i = 0; i < fSourceCount; i++) {
505		uint8* data = (uint8*)_SourceToVirtual(fSourceVecs[i].base);
506		size_t vecLength = min_c(fSourceVecs[i].length, length);
507
508		for (uint32 j = 0; j < vecLength; j++) {
509			if (disk[offset + j] != data[j]) {
510				dprintf("offset %lu differs, found on disk:\n", j);
511				j &= ~63;
512				dump_block((char*)&disk[offset + j],
513					min_c(64, fSuite.Size() - i), "  ");
514				dprintf("should be:\n");
515				dump_block((char*)&data[j], min_c(64, fSuite.Size() - j), "  ");
516
517				_Panic("Data write differs");
518			}
519		}
520
521		offset += vecLength;
522		length -= vecLength;
523	}
524}
525
526
527void
528Test::_CheckResults()
529{
530	if (fIsWrite)
531		_CheckWrite();
532	else
533		_CheckCompare();
534}
535
536
537status_t
538Test::_DoIO(IOOperation& operation)
539{
540	return do_io(NULL, &operation);
541}
542
543
544void
545Test::Run(DMAResource& resource)
546{
547	_Prepare();
548
549	IORequest request;
550	status_t status = request.Init(fOffset, fSourceVecs, fSourceCount,
551		fLength, fIsWrite, fFlags);
552	if (status != B_OK)
553		_Panic("request init failed: %s\n", strerror(status));
554
555	uint32 resultIndex = 0;
556
557	IOOperation operation;
558	while (request.RemainingBytes() > 0) {
559		if (resultIndex >= fResultCount)
560			_Panic("no results left");
561
562		status_t status = resource.TranslateNext(&request, &operation, 0);
563		if (status != B_OK) {
564			_Panic("DMAResource::TranslateNext() failed: %s\n",
565				strerror(status));
566			break;
567		}
568
569		DMABuffer* buffer = operation.Buffer();
570
571		dprintf("IOOperation: offset %" B_PRIdOFF ", length %" B_PRIuGENADDR
572			" (%" B_PRIdOFF "/%" B_PRIuGENADDR ")\n", operation.Offset(),
573			operation.Length(), operation.OriginalOffset(),
574			operation.OriginalLength());
575		dprintf("  DMABuffer %p, %lu vecs, bounce buffer: %p (%p) %s\n", buffer,
576			buffer->VecCount(), buffer->BounceBufferAddress(),
577			(void*)buffer->PhysicalBounceBufferAddress(),
578			operation.UsesBounceBuffer() ? "used" : "unused");
579		for (uint32 i = 0; i < buffer->VecCount(); i++) {
580			dprintf("    [%" B_PRIu32 "] base %#" B_PRIxGENADDR ", length %"
581				B_PRIuGENADDR "%s\n", i, buffer->VecAt(i).base,
582				buffer->VecAt(i).length,
583				buffer->UsesBounceBufferAt(i) ? ", bounce" : "");
584		}
585
586		dprintf("  remaining bytes: %" B_PRIuGENADDR "\n",
587			request.RemainingBytes());
588
589		// check results
590
591		const result_t& result = fResults[resultIndex];
592		if (result.count != buffer->VecCount())
593			panic("result count differs (expected %lu)\n", result.count);
594
595		for (uint32 i = 0; i < result.count; i++) {
596			const target_t& target = result.targets[i];
597			const generic_io_vec& vec = buffer->VecAt(i);
598
599			if (target.length != vec.length)
600				_Panic("[%lu] length differs", i);
601
602			generic_addr_t address;
603			if (target.uses_bounce_buffer) {
604				address = target.address
605					+ (addr_t)buffer->PhysicalBounceBufferAddress();
606			} else if (fSuite.IsContiguous() || target.address < B_PAGE_SIZE) {
607				address = target.address + fSuite.PhysicalDataBase();
608			} else {
609				address = target.address - B_PAGE_SIZE
610					+ fSuite.SecondPhysicalDataBase();
611			}
612
613			if (address != vec.base) {
614				_Panic("[%" B_PRIu32 "] address differs: %#" B_PRIxGENADDR
615					", should be %#" B_PRIuGENADDR "", i, vec.base, address);
616			}
617		}
618
619		_DoIO(operation);
620		operation.SetStatus(B_OK);
621		bool finished = operation.Finish();
622		bool isPartial = result.partial_begin || result.partial_end;
623		if (finished == (isPartial && fIsWrite))
624			_Panic("partial finished %s", finished ? "early" : "late");
625
626		if (!finished) {
627			dprintf("  operation not done yet!\n");
628			_DoIO(operation);
629			operation.SetStatus(B_OK);
630
631			isPartial = result.partial_begin && result.partial_end;
632			finished = operation.Finish();
633			if (finished == result.partial_begin && result.partial_end)
634				_Panic("partial finished %s", finished ? "early" : "late");
635
636			if (!finished) {
637				dprintf("  operation not done yet!\n");
638				_DoIO(operation);
639				operation.SetStatus(B_OK);
640
641				if (!operation.Finish())
642					_Panic("operation doesn't finish");
643			}
644		}
645
646		request.OperationFinished(&operation, operation.Status(),
647			false,
648			operation.OriginalOffset() - operation.Parent()->Offset()
649				+ operation.OriginalLength());
650
651		resultIndex++;
652	}
653
654	_CheckResults();
655}
656
657
658void
659Test::_Panic(const char* message,...)
660{
661	char buffer[1024];
662
663	va_list args;
664	va_start(args, message);
665	vsnprintf(buffer, sizeof(buffer), message, args);
666	va_end(args);
667
668	dprintf("test failed\n");
669	dprintf("  offset:  %lld\n", fOffset);
670	dprintf("  base:    %p (physical: %p)\n", (void*)fSuite.DataBase(),
671		(void*)fSuite.PhysicalDataBase());
672	dprintf("  length:  %lu\n", fLength);
673	dprintf("  write:   %d\n", fIsWrite);
674	dprintf("  flags:   %#lx\n", fFlags);
675	dprintf("  sources:\n");
676	for (uint32 i = 0; i < fSourceCount; i++) {
677		dprintf("    [%#" B_PRIxGENADDR ", %" B_PRIuGENADDR "]\n",
678			fSourceVecs[i].base, fSourceVecs[i].length);
679	}
680	for (uint32 i = 0; i < fResultCount; i++) {
681		const result_t& result = fResults[i];
682		dprintf("  result %lu:\n", i);
683		dprintf("    offset:  %lld\n", result.offset);
684		dprintf("    partial: %d/%d\n", result.partial_begin,
685			result.partial_end);
686
687		for (uint32 k = 0; k < result.count; k++) {
688			const target_t& target = result.targets[k];
689			dprintf("    [%p, %lu, %d]\n", (void*)target.address, target.length,
690				target.uses_bounce_buffer);
691		}
692	}
693
694	panic("%s", buffer);
695}
696
697
698static void
699run_tests_no_restrictions(TestSuiteContext& context)
700{
701	const dma_restrictions restrictions = {
702		0x0,	// low
703		0x0,	// high
704		0,		// alignment
705		0,		// boundary
706		0,		// max transfer
707		0,		// max segment count
708		0,		// max segment size
709		0		// flags
710	};
711
712	TestSuite suite(context, "no restrictions", restrictions, 512);
713
714	suite.AddTest(0, 1024, false, 0)
715		.AddSource(0, 1024)
716		.NextResult(0, false, false)
717			.AddTarget(0, 1024, false);
718
719	// read partial begin/end
720	suite.AddTest(23, 1024, false, 0)
721		.AddSource(0, 1024)
722		.NextResult(0, true, true)
723			.AddTarget(0, 23, true)
724			.AddTarget(0, 1024, false)
725			.AddTarget(23, 512 - 23, true);
726
727	// read less than a block
728	suite.AddTest(23, 30, false, 0)
729		.AddSource(0, 1024)
730		.NextResult(0, true, true)
731			.AddTarget(0, 23, true)
732			.AddTarget(0, 30, false)
733			.AddTarget(23, 512 - 53, true);
734
735	// write begin/end
736	suite.AddTest(23, 1024, true, 0)
737		.AddSource(0, 1024)
738		.NextResult(0, true, true)
739			.AddTarget(0, 512, true)
740			.AddTarget(489, 512, false)
741			.AddTarget(512, 512, true);
742
743	// read partial end, length < iovec length
744	suite.AddTest(0, 1028, false, 0)
745		.AddSource(0, 512)
746		.AddSource(1024, 1024)
747		.NextResult(0, false, true)
748			.AddTarget(0, 512, false)
749			.AddTarget(1024, 516, false)
750			.AddTarget(0, 508, true);
751
752	// write partial end, length < iovec length
753	suite.AddTest(0, 1028, true, 0)
754		.AddSource(0, 512)
755		.AddSource(1024, 1024)
756		.NextResult(0, false, true)
757			.AddTarget(0, 512, false)
758			.AddTarget(1024, 512, false)
759			.AddTarget(0, 512, true);
760
761	suite.Run();
762}
763
764
765static void
766run_tests_address_restrictions(TestSuiteContext& context)
767{
768	const dma_restrictions restrictions = {
769		context.PhysicalDataBase() + 512,	// low
770		0,		// high
771		0,		// alignment
772		0,		// boundary
773		0,		// max transfer
774		0,		// max segment count
775		0,		// max segment size
776		0		// flags
777	};
778
779	TestSuite suite(context, "address", restrictions, 512);
780
781	suite.AddTest(0, 1024, false, 0)
782		.AddSource(0, 1024)
783		.NextResult(0, false, false)
784			.AddTarget(0, 512, true)
785			.AddTarget(512, 512, false);
786
787	suite.Run();
788}
789
790
791static void
792run_tests_alignment_restrictions(TestSuiteContext& context)
793{
794	const dma_restrictions restrictions = {
795		0x0,	// low
796		0x0,	// high
797		32,		// alignment
798		0,		// boundary
799		0,		// max transfer
800		0,		// max segment count
801		0,		// max segment size
802		0		// flags
803	};
804
805	TestSuite suite(context, "alignment", restrictions, 512);
806
807	suite.AddTest(0, 1024, false, B_PHYSICAL_IO_REQUEST)
808		.AddSource(16, 1024)
809		.NextResult(0, false, false)
810			.AddTarget(0, 1024, true);
811
812	suite.AddTest(0, 2559, true, B_PHYSICAL_IO_REQUEST)
813		.AddSource(0, 2559)
814		.NextResult(0, false, true)
815			.AddTarget(0, 2048, false)
816			.AddTarget(0, 512, true);
817
818	suite.AddTest(0, 51, true, B_PHYSICAL_IO_REQUEST)
819		.AddSource(0, 4096)
820		.NextResult(0, false, true)
821			.AddTarget(0, 512, true);
822
823	suite.AddTest(32, 51, true, B_PHYSICAL_IO_REQUEST)
824		.AddSource(0, 4096)
825		.NextResult(0, true, false)
826			.AddTarget(0, 512, true);
827			// Note: The operation has actually both partial begin and end, but
828			// our Test is not clever enough to realize that it is only one
829			// block and thus only one read phase is needed.
830
831	suite.Run();
832}
833
834
835static void
836run_tests_boundary_restrictions(TestSuiteContext& context)
837{
838	const dma_restrictions restrictions = {
839		0x0,	// low
840		0x0,	// high
841		0,		// alignment
842		1024,	// boundary
843		0,		// max transfer
844		0,		// max segment count
845		0,		// max segment size
846		0		// flags
847	};
848
849	TestSuite suite(context, "boundary", restrictions, 512);
850
851	suite.AddTest(0, 2000, false, 0)
852		.AddSource(0, 2048)
853		.NextResult(0, false, false)
854			.AddTarget(0, 1024, false)
855			.AddTarget(1024, 976, false)
856			.AddTarget(0, 48, true);
857
858	suite.Run();
859}
860
861
862static void
863run_tests_segment_restrictions(TestSuiteContext& context)
864{
865	const dma_restrictions restrictions = {
866		0x0,	// low
867		0x0,	// high
868		0,		// alignment
869		0,		// boundary
870		0,		// max transfer
871		4,		// max segment count
872		1024,	// max segment size
873		0		// flags
874	};
875
876	TestSuite suite(context, "segment", restrictions, 512);
877
878	suite.AddTest(0, 4096, false, 0)
879		.AddSource(0, 4096)
880		.NextResult(0, false, false)
881			.AddTarget(0, 1024, false)
882			.AddTarget(1024, 1024, false)
883			.AddTarget(2048, 1024, false)
884			.AddTarget(3072, 1024, false);
885
886	suite.AddTest(0, 2560, false, B_PHYSICAL_IO_REQUEST)
887		.AddSource(0, 512)
888		.AddSource(1024, 512)
889		.AddSource(2048, 512)
890		.AddSource(3072, 512)
891		.AddSource(4096, 512)
892		.NextResult(0, false, false)
893			.AddTarget(0, 512, false)
894			.AddTarget(1024, 512, false)
895			.AddTarget(2048, 512, false)
896			.AddTarget(3072, 512, false)
897		.NextResult(2048, false, false)
898			.AddTarget(4096, 512, false);
899
900	suite.Run();
901}
902
903
904static void
905run_tests_transfer_restrictions(TestSuiteContext& context)
906{
907	const dma_restrictions restrictions = {
908		0x0,	// low
909		0x0,	// high
910		0,		// alignment
911		0,		// boundary
912		1024,	// max transfer
913		0,		// max segment count
914		0,		// max segment size
915		0		// flags
916	};
917
918	TestSuite suite(context, "transfer", restrictions, 512);
919
920	suite.AddTest(0, 4000, false, 0)
921		.AddSource(0, 4096)
922		.NextResult(0, false, false)
923			.AddTarget(0, 1024, false)
924		.NextResult(0, false, false)
925			.AddTarget(1024, 1024, false)
926		.NextResult(0, false, false)
927			.AddTarget(2048, 1024, false)
928		.NextResult(0, false, false)
929			.AddTarget(3072, 1024 - 96, false)
930			.AddTarget(0, 96, true);
931
932	suite.Run();
933}
934
935
936static void
937run_tests_interesting_restrictions(TestSuiteContext& context)
938{
939	dma_restrictions restrictions = {
940		0x0,	// low
941		0x0,	// high
942		32,		// alignment
943		512,	// boundary
944		0,		// max transfer
945		0,		// max segment count
946		0,		// max segment size
947		0		// flags
948	};
949
950	TestSuite suite(context, "interesting", restrictions, 512);
951
952	// read with partial begin/end
953	suite.AddTest(32, 1000, false, 0)
954		.AddSource(0, 1024)
955		.NextResult(0, true, true)
956			.AddTarget(0, 32, true)
957			.AddTarget(0, 512, false)
958			.AddTarget(512, 480, false)
959			.AddTarget(32, 480, true)
960			.AddTarget(512, 32, true);
961
962	// write with partial begin/end
963	suite.AddTest(32, 1000, true, 0)
964		.AddSource(0, 1024)
965		.NextResult(0, true, true)
966			.AddTarget(0, 512, true)
967			.AddTarget(480, 32, false)
968			.AddTarget(512, 480, false)
969			.AddTarget(512, 512, true);
970
971	suite.Run();
972
973	restrictions = (dma_restrictions){
974		0x0,	// low
975		0x0,	// high
976		32,		// alignment
977		512,	// boundary
978		0,		// max transfer
979		4,		// max segment count
980		0,		// max segment size
981		0		// flags
982	};
983
984	TestSuite suite2(context, "interesting2", restrictions, 512);
985
986	suite2.AddTest(32, 1000, false, 0)
987		.AddSource(0, 1024)
988		.NextResult(0, true, false)
989			.AddTarget(0, 32, true)
990			.AddTarget(0, 512, false)
991			.AddTarget(512, 480, false)
992		.NextResult(0, false, true)
993			.AddTarget(0, 512, true);
994
995	suite2.Run();
996}
997
998
999static void
1000run_tests_mean_restrictions(TestSuiteContext& context)
1001{
1002	const dma_restrictions restrictions = {
1003		context.PhysicalDataBase() + 1024,	// low
1004		0x0,	// high
1005		32,		// alignment
1006		1024,	// boundary
1007		0,		// max transfer
1008		2,		// max segment count
1009		512,	// max segment size
1010		0		// flags
1011	};
1012
1013	TestSuite suite(context, "mean", restrictions, 512);
1014
1015	suite.AddTest(0, 1024, false, 0)
1016		.AddSource(0, 1024)
1017		.NextResult(0, false, false)
1018			.AddTarget(0, 512, true)
1019			.AddTarget(512, 512, true);
1020
1021	suite.AddTest(0, 1024, false, 0)
1022		.AddSource(1024 + 32, 1024)
1023		.NextResult(0, false, false)
1024			.AddTarget(1024 + 32, 512, false)
1025		.NextResult(0, false, false)
1026			.AddTarget(1568, 480, false)
1027			.AddTarget(1568 + 480, 32, false);
1028
1029	suite.Run();
1030}
1031
1032
1033static void
1034run_tests_non_contiguous_no_restrictions(TestSuiteContext& context)
1035{
1036	const dma_restrictions restrictions = {
1037		0x0,	// low
1038		0x0,	// high
1039		0,		// alignment
1040		0,		// boundary
1041		0,		// max transfer
1042		0,		// max segment count
1043		0,		// max segment size
1044		0		// flags
1045	};
1046
1047	TestSuite suite(context, "non contiguous, no restrictions", restrictions,
1048		512);
1049
1050	suite.AddTest(0, 8192, false, 0)
1051		.AddSource(0, 8192)
1052		.NextResult(0, false, false)
1053			.AddTarget(0, 4096, false)
1054			.AddTarget(4096, 4096, false);
1055
1056	suite.Run();
1057}
1058
1059
1060static void
1061run_test()
1062{
1063	TestSuiteContext context;
1064	status_t status = context.Init(4 * B_PAGE_SIZE);
1065	if (status != B_OK)
1066		return;
1067
1068	run_tests_no_restrictions(context);
1069	run_tests_address_restrictions(context);
1070	run_tests_alignment_restrictions(context);
1071	run_tests_boundary_restrictions(context);
1072	run_tests_segment_restrictions(context);
1073	run_tests_transfer_restrictions(context);
1074	run_tests_interesting_restrictions(context);
1075	run_tests_mean_restrictions(context);
1076
1077	// physical non-contiguous
1078
1079	status = context.Init(2 * B_PAGE_SIZE, false);
1080	if (status != B_OK)
1081		return;
1082
1083	run_tests_non_contiguous_no_restrictions(context);
1084
1085	dprintf("All tests passed!\n");
1086}
1087
1088
1089//	#pragma mark - driver
1090
1091
1092static float
1093dma_test_supports_device(device_node *parent)
1094{
1095	const char* bus = NULL;
1096	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)
1097			== B_OK && !strcmp(bus, "generic"))
1098		return 0.8;
1099
1100	return -1;
1101}
1102
1103
1104static status_t
1105dma_test_register_device(device_node *parent)
1106{
1107	device_attr attrs[] = {
1108		{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "DMA Test"}},
1109		{NULL}
1110	};
1111
1112	return sDeviceManager->register_node(parent,
1113		"drivers/disk/dma_resource_test/driver_v1", attrs, NULL, NULL);
1114}
1115
1116
1117static status_t
1118dma_test_init_driver(device_node *node, void **_driverCookie)
1119{
1120	sAreaSize = 10 * 1024 * 1024;
1121	sArea = create_area("dma test", &sAreaAddress, B_ANY_KERNEL_ADDRESS,
1122		sAreaSize, B_LAZY_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
1123	if (sArea < B_OK)
1124		return sArea;
1125
1126	*_driverCookie = node;
1127
1128	run_test();
1129	return B_OK;
1130}
1131
1132
1133static void
1134dma_test_uninit_driver(void *driverCookie)
1135{
1136	delete_area(sArea);
1137}
1138
1139
1140static status_t
1141dma_test_register_child_devices(void *driverCookie)
1142{
1143	return sDeviceManager->publish_device((device_node*)driverCookie,
1144		"disk/virtual/dma_test/raw",
1145		"drivers/disk/dma_resource_test/device_v1");
1146}
1147
1148
1149//	#pragma mark - device
1150
1151
1152static status_t
1153dma_test_init_device(void *driverCookie, void **_deviceCookie)
1154{
1155	const dma_restrictions restrictions = {
1156		0x0,	// low
1157		0x0,	// high
1158		4,		// alignment
1159		0,		// boundary
1160		0,		// max transfer
1161		0,		// max segment count
1162		B_PAGE_SIZE, // max segment size
1163		0		// flags
1164	};
1165
1166	*_deviceCookie = driverCookie;
1167	sDMAResource = new(std::nothrow) DMAResource;
1168	if (sDMAResource == NULL)
1169		return B_NO_MEMORY;
1170
1171	status_t status = sDMAResource->Init(restrictions, DMA_TEST_BLOCK_SIZE,
1172		DMA_TEST_BUFFER_COUNT, DMA_TEST_BOUNCE_BUFFER_COUNT);
1173	if (status != B_OK) {
1174		delete sDMAResource;
1175		return status;
1176	}
1177
1178	sIOScheduler = new(std::nothrow) IOSchedulerSimple(sDMAResource);
1179	if (sIOScheduler == NULL) {
1180		delete sDMAResource;
1181		return B_NO_MEMORY;
1182	}
1183
1184	status = sIOScheduler->Init("dma test scheduler");
1185	if (status != B_OK) {
1186		delete sIOScheduler;
1187		delete sDMAResource;
1188		return status;
1189	}
1190
1191	sIOScheduler->SetCallback(&do_io, NULL);
1192	return B_OK;
1193}
1194
1195
1196static void
1197dma_test_uninit_device(void *deviceCookie)
1198{
1199}
1200
1201
1202static status_t
1203dma_test_open(void *deviceCookie, const char *path, int openMode,
1204	void **_cookie)
1205{
1206	return B_OK;
1207}
1208
1209
1210static status_t
1211dma_test_close(void *cookie)
1212{
1213	return B_OK;
1214}
1215
1216
1217static status_t
1218dma_test_free(void *cookie)
1219{
1220	return B_OK;
1221}
1222
1223
1224static status_t
1225dma_test_read(void *cookie, off_t pos, void *buffer, size_t *_length)
1226{
1227	size_t length = *_length;
1228
1229	if (pos >= sAreaSize)
1230		return B_BAD_VALUE;
1231	if (pos + length > sAreaSize)
1232		length = sAreaSize - pos;
1233
1234#if 1
1235	IORequest request;
1236	status_t status = request.Init(pos, (addr_t)buffer, length, false, 0);
1237	if (status != B_OK)
1238		return status;
1239
1240	status = sIOScheduler->ScheduleRequest(&request);
1241	if (status != B_OK)
1242		return status;
1243
1244	status = request.Wait(0, 0);
1245	dprintf("dma_test_read(): request.Wait() returned: %s\n", strerror(status));
1246#else
1247	status_t status = user_memcpy(buffer, (uint8*)sAreaAddress + pos, length);
1248#endif
1249
1250	if (status == B_OK)
1251		*_length = length;
1252	return status;
1253}
1254
1255
1256static status_t
1257dma_test_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
1258{
1259	size_t length = *_length;
1260
1261	if (pos >= sAreaSize)
1262		return B_BAD_VALUE;
1263	if (pos + length > sAreaSize)
1264		length = sAreaSize - pos;
1265
1266#if 1
1267	IORequest request;
1268	status_t status = request.Init(pos, (addr_t)buffer, length, true, 0);
1269	if (status != B_OK)
1270		return status;
1271
1272	status = sIOScheduler->ScheduleRequest(&request);
1273	if (status != B_OK)
1274		return status;
1275
1276	status = request.Wait(0, 0);
1277	dprintf("dma_test_write(): request.Wait() returned: %s\n",
1278		strerror(status));
1279#else
1280	status_t status = user_memcpy((uint8*)sAreaAddress + pos, buffer, length);
1281#endif
1282
1283	if (status == B_OK)
1284		*_length = length;
1285
1286	return status;
1287}
1288
1289
1290static status_t
1291dma_test_io(void *cookie, io_request *request)
1292{
1293	dprintf("dma_test_io(%p)\n", request);
1294
1295	return sIOScheduler->ScheduleRequest(request);
1296}
1297
1298
1299static status_t
1300dma_test_control(void *cookie, uint32 op, void *buffer, size_t length)
1301{
1302	switch (op) {
1303		case B_GET_DEVICE_SIZE:
1304			return user_memcpy(buffer, &sAreaSize, sizeof(size_t));
1305
1306		case B_SET_NONBLOCKING_IO:
1307		case B_SET_BLOCKING_IO:
1308			return B_OK;
1309
1310		case B_GET_READ_STATUS:
1311		case B_GET_WRITE_STATUS:
1312		{
1313			bool value = true;
1314			return user_memcpy(buffer, &value, sizeof(bool));
1315		}
1316
1317		case B_GET_GEOMETRY:
1318		case B_GET_BIOS_GEOMETRY:
1319		{
1320			device_geometry geometry;
1321			geometry.bytes_per_sector = DMA_TEST_BLOCK_SIZE;
1322			geometry.sectors_per_track = 1;
1323			geometry.cylinder_count = sAreaSize / DMA_TEST_BLOCK_SIZE;
1324			geometry.head_count = 1;
1325			geometry.device_type = B_DISK;
1326			geometry.removable = true;
1327			geometry.read_only = false;
1328			geometry.write_once = false;
1329
1330			return user_memcpy(buffer, &geometry, sizeof(device_geometry));
1331		}
1332
1333		case B_GET_MEDIA_STATUS:
1334		{
1335			status_t status = B_OK;
1336			return user_memcpy(buffer, &status, sizeof(status_t));
1337		}
1338
1339		case B_SET_UNINTERRUPTABLE_IO:
1340		case B_SET_INTERRUPTABLE_IO:
1341		case B_FLUSH_DRIVE_CACHE:
1342			return B_OK;
1343	}
1344	return B_BAD_VALUE;
1345}
1346
1347
1348module_dependency module_dependencies[] = {
1349	{B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager},
1350	{}
1351};
1352
1353
1354static const struct driver_module_info sDMATestDriverModule = {
1355	{
1356		"drivers/disk/dma_resource_test/driver_v1",
1357		0,
1358		NULL
1359	},
1360
1361	dma_test_supports_device,
1362	dma_test_register_device,
1363	dma_test_init_driver,
1364	dma_test_uninit_driver,
1365	dma_test_register_child_devices
1366};
1367
1368static const struct device_module_info sDMATestDeviceModule = {
1369	{
1370		"drivers/disk/dma_resource_test/device_v1",
1371		0,
1372		NULL
1373	},
1374
1375	dma_test_init_device,
1376	dma_test_uninit_device,
1377	NULL,
1378
1379	dma_test_open,
1380	dma_test_close,
1381	dma_test_free,
1382
1383	dma_test_read,
1384	dma_test_write,
1385	dma_test_io,
1386
1387	dma_test_control,
1388
1389	NULL,	// select
1390	NULL	// deselect
1391};
1392
1393const module_info* modules[] = {
1394	(module_info*)&sDMATestDriverModule,
1395	(module_info*)&sDMATestDeviceModule,
1396	NULL
1397};
1398