xref: /haiku/src/tests/system/kernel/device_manager/dma_resource_test.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
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.h>
13 
14 #include "dma_resources.h"
15 #include "io_requests.h"
16 #include "IOScheduler.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 
24 class TestSuite;
25 
26 class TestSuiteContext {
27 public:
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 
46 private:
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 
58 class Test : public DoublyLinkedListLinkImpl<Test> {
59 public:
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 
71 private:
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 			iovec			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 
105 typedef DoublyLinkedList<Test> TestList;
106 
107 
108 class TestSuite {
109 public:
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 %#lx - %#lx, align %lu, boundary "
117 			"%lu,\n    max transfer %lu, max segs %lu, max seg size %lu, "
118 			"flags %lx\n\n", restrictions.low_address,
119 			restrictions.high_address, restrictions.alignment,
120 			restrictions.boundary, restrictions.max_transfer_size,
121 			restrictions.max_segment_count, restrictions.max_segment_size,
122 			restrictions.flags);
123 
124 		status_t status = fDMAResource.Init(restrictions, blockSize, 10, 10);
125 		if (status != B_OK)
126 			panic("initializing DMA resource failed: %s\n", strerror(status));
127 	}
128 
129 	~TestSuite()
130 	{
131 		while (Test* test = fTests.RemoveHead()) {
132 			delete test;
133 		}
134 	}
135 
136 	Test& AddTest(off_t offset, size_t length, bool isWrite, uint32 flags)
137 	{
138 		Test* test = new(std::nothrow) Test(*this, offset, length, isWrite,
139 			flags);
140 		fTests.Add(test);
141 
142 		return *test;
143 	}
144 
145 	void Run()
146 	{
147 		TestList::Iterator iterator = fTests.GetIterator();
148 		uint32 count = 1;
149 		while (Test* test = iterator.Next()) {
150 			dprintf("test %lu...\n", count++);
151 			test->Run(fDMAResource);
152 		}
153 	}
154 
155 	addr_t DataBase() const { return fContext.DataBase(); }
156 	addr_t PhysicalDataBase() const { return fContext.PhysicalDataBase(); }
157 	addr_t SecondPhysicalDataBase() const
158 		{ return fContext.SecondPhysicalDataBase(); }
159 	bool IsContiguous() const { return fContext.IsContiguous(); }
160 	addr_t CompareBase() const { return fContext.CompareBase(); }
161 	size_t Size() const { return fContext.Size(); }
162 
163 private:
164 	TestSuiteContext& fContext;
165 	DMAResource		fDMAResource;
166 	uint8*			fBase;
167 	uint8*			fPhysicalBase;
168 	size_t			fSize;
169 	TestList		fTests;
170 };
171 
172 
173 struct device_manager_info* sDeviceManager;
174 
175 static area_id sArea;
176 static size_t sAreaSize;
177 static void* sAreaAddress;
178 static DMAResource* sDMAResource;
179 static IOScheduler* sIOScheduler;
180 
181 
182 status_t
183 do_io(void* data, IOOperation* operation)
184 {
185 	uint8* disk = (uint8*)sAreaAddress;
186 	off_t offset = operation->Offset();
187 
188 	for (uint32 i = 0; i < operation->VecCount(); i++) {
189 		const iovec& vec = operation->Vecs()[i];
190 		addr_t base = (addr_t)vec.iov_base;
191 		size_t length = vec.iov_len;
192 
193 		if (operation->IsWrite())
194 			vm_memcpy_from_physical(disk + offset, base, length, false);
195 		else
196 			vm_memcpy_to_physical(base, disk + offset, length, false);
197 	}
198 
199 	if (sIOScheduler != NULL)
200 		sIOScheduler->OperationCompleted(operation, B_OK, operation->Length());
201 	return B_OK;
202 }
203 
204 
205 //	#pragma mark -
206 
207 
208 TestSuiteContext::TestSuiteContext()
209 	:
210 	fDataArea(-1),
211 	fCompareArea(-1),
212 	fSize(0)
213 {
214 }
215 
216 
217 TestSuiteContext::~TestSuiteContext()
218 {
219 	_Uninit();
220 }
221 
222 
223 void
224 TestSuiteContext::_Uninit()
225 {
226 	delete_area(fDataArea);
227 	delete_area(fCompareArea);
228 }
229 
230 
231 status_t
232 TestSuiteContext::Init(size_t size, bool contiguous)
233 {
234 	if (!contiguous) {
235 		// we can't force this, so we have to try and see
236 
237 		if (size != B_PAGE_SIZE * 2)
238 			return B_NOT_SUPPORTED;
239 
240 		while (true) {
241 			fDataArea = create_area("data buffer", (void**)&fDataBase,
242 				B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK,
243 				B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
244 			if (fDataArea < B_OK)
245 				return fDataArea;
246 
247 			// get memory map to see if we succeeded
248 
249 			physical_entry entry[2];
250 			get_memory_map((void*)fDataBase, 2 * B_PAGE_SIZE, entry, 2);
251 
252 			if (entry[0].size == B_PAGE_SIZE) {
253 				fPhysicalDataBase = (addr_t)entry[0].address;
254 				fSecondPhysicalDataBase = (addr_t)entry[1].address;
255 
256 				dprintf("DMA Test area %p, physical %p, second physical %p\n",
257 					(void*)fDataBase, entry[0].address, entry[1].address);
258 				break;
259 			}
260 
261 			// try again
262 			delete_area(fDataArea);
263 		}
264 	} else {
265 		fDataArea = create_area("data buffer", (void**)&fDataBase,
266 			B_ANY_KERNEL_ADDRESS, size, B_CONTIGUOUS,
267 			B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
268 		if (fDataArea < B_OK)
269 			return fDataArea;
270 
271 		physical_entry entry;
272 		get_memory_map((void*)fDataBase, B_PAGE_SIZE, &entry, 1);
273 
274 		fPhysicalDataBase = (addr_t)entry.address;
275 		fSecondPhysicalDataBase = 0;
276 
277 		dprintf("DMA Test area %p, physical %p\n", (void*)fDataBase,
278 			entry.address);
279 	}
280 
281 	fCompareArea = create_area("compare buffer", (void**)&fCompareBase,
282 		B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK,
283 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
284 	if (fCompareArea < B_OK)
285 		return fCompareArea;
286 
287 	fSize = size;
288 	return B_OK;
289 }
290 
291 
292 //	#pragma mark -
293 
294 
295 Test::Test(TestSuite& suite, off_t offset, size_t length, bool isWrite,
296 		uint32 flags)
297 	:
298 	fSuite(suite),
299 	fOffset(offset),
300 	fLength(length),
301 	fIsWrite(isWrite),
302 	fFlags(flags),
303 	fSourceCount(0),
304 	fResultCount(0)
305 {
306 }
307 
308 
309 Test&
310 Test::AddSource(addr_t address, size_t length)
311 {
312 	if (fSuite.IsContiguous() || (fFlags & B_PHYSICAL_IO_REQUEST) != 0
313 		|| address < B_PAGE_SIZE) {
314 		fSourceVecs[fSourceCount].iov_base
315 			= (void*)(((fFlags & B_PHYSICAL_IO_REQUEST) == 0
316 				? fSuite.DataBase() : fSuite.PhysicalDataBase()) + address);
317 	} else {
318 		fSourceVecs[fSourceCount].iov_base
319 			= (void*)(fSuite.SecondPhysicalDataBase() + address);
320 	}
321 	fSourceVecs[fSourceCount].iov_len = length;
322 	fSourceCount++;
323 
324 	return *this;
325 }
326 
327 
328 Test&
329 Test::NextResult(off_t offset, bool partialBegin, bool partialEnd)
330 {
331 	fResults[fResultCount].offset = offset;
332 	fResults[fResultCount].count = 0;
333 	fResults[fResultCount].partial_begin = partialBegin;
334 	fResults[fResultCount].partial_end = partialEnd;
335 	fResultCount++;
336 
337 	return *this;
338 }
339 
340 
341 Test&
342 Test::AddTarget(addr_t base, size_t length, bool usesBounceBuffer)
343 {
344 	struct result_t& result = fResults[fResultCount - 1];
345 	struct target_t& target = result.targets[result.count++];
346 
347 	target.address = base;
348 	target.length = length;
349 	target.uses_bounce_buffer = usesBounceBuffer;
350 
351 	return *this;
352 }
353 
354 
355 addr_t
356 Test::_SourceToVirtual(addr_t source)
357 {
358 	if ((fFlags & B_PHYSICAL_IO_REQUEST) != 0) {
359 		if (!fSuite.IsContiguous() && source >= B_PAGE_SIZE)
360 			return source - fSuite.SecondPhysicalDataBase() + fSuite.DataBase();
361 
362 		return source - fSuite.PhysicalDataBase() + fSuite.DataBase();
363 	}
364 
365 	return source;
366 }
367 
368 
369 addr_t
370 Test::_SourceToCompare(addr_t source)
371 {
372 	if ((fFlags & B_PHYSICAL_IO_REQUEST) != 0) {
373 		if (!fSuite.IsContiguous() && source >= B_PAGE_SIZE) {
374 			return source - fSuite.SecondPhysicalDataBase()
375 				+ fSuite.CompareBase();
376 		}
377 
378 		return source - fSuite.PhysicalDataBase() + fSuite.CompareBase();
379 	}
380 
381 	return source - fSuite.DataBase() + fSuite.CompareBase();
382 }
383 
384 
385 void
386 Test::_Prepare()
387 {
388 	// prepare disk
389 
390 	uint8* disk = (uint8*)sAreaAddress;
391 	for (size_t i = 0; i < sAreaSize; i++) {
392 		disk[i] = i % 26 + 'a';
393 	}
394 
395 	// prepare data
396 
397 	memset((void*)fSuite.DataBase(), 0xcc, fSuite.Size());
398 
399 	if (fIsWrite) {
400 		off_t offset = fOffset;
401 		size_t length = fLength;
402 
403 		for (uint32 i = 0; i < fSourceCount; i++) {
404 			uint8* data = (uint8*)_SourceToVirtual(
405 				(addr_t)fSourceVecs[i].iov_base);
406 			size_t vecLength = min_c(fSourceVecs[i].iov_len, length);
407 
408 			for (uint32 j = 0; j < vecLength; j++) {
409 				data[j] = (offset + j) % 10 + '0';
410 			}
411 			offset += vecLength;
412 			length -= vecLength;
413 		}
414 	}
415 
416 	// prepare compare data
417 
418 	memset((void*)fSuite.CompareBase(), 0xcc, fSuite.Size());
419 
420 	if (fIsWrite) {
421 		// copy data from source
422 		off_t offset = fOffset;
423 		size_t length = fLength;
424 
425 		for (uint32 i = 0; i < fSourceCount; i++) {
426 			uint8* compare = (uint8*)_SourceToCompare(
427 				(addr_t)fSourceVecs[i].iov_base);
428 			size_t vecLength = min_c(fSourceVecs[i].iov_len, length);
429 
430 			memcpy(compare,
431 				(void*)_SourceToVirtual((addr_t)fSourceVecs[i].iov_base),
432 				vecLength);
433 			offset += vecLength;
434 			length -= vecLength;
435 		}
436 	} else {
437 		// copy data from drive
438 		off_t offset = fOffset;
439 		size_t length = fLength;
440 
441 		for (uint32 i = 0; i < fSourceCount; i++) {
442 			uint8* compare = (uint8*)_SourceToCompare(
443 				(addr_t)fSourceVecs[i].iov_base);
444 			size_t vecLength = min_c(fSourceVecs[i].iov_len, length);
445 
446 			memcpy(compare, disk + offset, vecLength);
447 			offset += vecLength;
448 			length -= vecLength;
449 		}
450 	}
451 
452 	if (fIsWrite)
453 		_CheckCompare();
454 }
455 
456 
457 void
458 Test::_CheckCompare()
459 {
460 	uint8* data = (uint8*)fSuite.DataBase();
461 	uint8* compare = (uint8*)fSuite.CompareBase();
462 
463 	for (size_t i = 0; i < fSuite.Size(); i++) {
464 		if (data[i] != compare[i]) {
465 			dprintf("offset %lu differs, %s:\n", i,
466 				fIsWrite ? "write" : "read");
467 			i &= ~63;
468 			dump_block((char*)&data[i], min_c(64, fSuite.Size() - i), "  ");
469 			dprintf("should be:\n");
470 			dump_block((char*)&compare[i], min_c(64, fSuite.Size() - i), "  ");
471 
472 			_Panic("Data %s differs", fIsWrite ? "write" : "read");
473 		}
474 	}
475 }
476 
477 
478 void
479 Test::_CheckWrite()
480 {
481 	_CheckCompare();
482 
483 	// check if we overwrote parts we shouldn't have
484 
485 	uint8* disk = (uint8*)sAreaAddress;
486 	for (size_t i = 0; i < sAreaSize; i++) {
487 		if (i >= fOffset && i < fOffset + fLength)
488 			continue;
489 
490 		if (disk[i] != i % 26 + 'a') {
491 			dprintf("disk[i] %c, expected %c, i %lu, fLength + fOffset %Ld\n",
492 				disk[i], (int)(i % 26 + 'a'), i, fLength + fOffset);
493 			dprintf("offset %lu differs, touched innocent data:\n", i);
494 			i &= ~63;
495 			dump_block((char*)&disk[i], min_c(64, fSuite.Size() - i), "  ");
496 
497 			_Panic("Data %s differs", fIsWrite ? "write" : "read");
498 		}
499 	}
500 
501 	// check if the data we wanted to have on disk ended up there
502 
503 	off_t offset = fOffset;
504 	size_t length = fLength;
505 
506 	for (uint32 i = 0; i < fSourceCount; i++) {
507 		uint8* data = (uint8*)_SourceToVirtual(
508 			(addr_t)fSourceVecs[i].iov_base);
509 		size_t vecLength = min_c(fSourceVecs[i].iov_len, length);
510 
511 		for (uint32 j = 0; j < vecLength; j++) {
512 			if (disk[offset + j] != data[j]) {
513 				dprintf("offset %lu differs, found on disk:\n", j);
514 				j &= ~63;
515 				dump_block((char*)&disk[offset + j],
516 					min_c(64, fSuite.Size() - i), "  ");
517 				dprintf("should be:\n");
518 				dump_block((char*)&data[j], min_c(64, fSuite.Size() - j), "  ");
519 
520 				_Panic("Data write differs");
521 			}
522 		}
523 
524 		offset += vecLength;
525 		length -= vecLength;
526 	}
527 }
528 
529 
530 void
531 Test::_CheckResults()
532 {
533 	if (fIsWrite)
534 		_CheckWrite();
535 	else
536 		_CheckCompare();
537 }
538 
539 
540 status_t
541 Test::_DoIO(IOOperation& operation)
542 {
543 	return do_io(NULL, &operation);
544 }
545 
546 
547 void
548 Test::Run(DMAResource& resource)
549 {
550 	_Prepare();
551 
552 	IORequest request;
553 	status_t status = request.Init(fOffset, fSourceVecs, fSourceCount,
554 		fLength, fIsWrite, fFlags);
555 	if (status != B_OK)
556 		_Panic("request init failed: %s\n", strerror(status));
557 
558 	uint32 resultIndex = 0;
559 
560 	IOOperation operation;
561 	while (request.RemainingBytes() > 0) {
562 		if (resultIndex >= fResultCount)
563 			_Panic("no results left");
564 
565 		status_t status = resource.TranslateNext(&request, &operation, 0);
566 		if (status != B_OK) {
567 			_Panic("DMAResource::TranslateNext() failed: %s\n",
568 				strerror(status));
569 			break;
570 		}
571 
572 		DMABuffer* buffer = operation.Buffer();
573 
574 		dprintf("IOOperation: offset %Ld, length %lu (%Ld/%lu)\n",
575 			operation.Offset(), operation.Length(), operation.OriginalOffset(),
576 			operation.OriginalLength());
577 		dprintf("  DMABuffer %p, %lu vecs, bounce buffer: %p (%p) %s\n", buffer,
578 			buffer->VecCount(), buffer->BounceBufferAddress(),
579 			(void*)buffer->PhysicalBounceBufferAddress(),
580 			operation.UsesBounceBuffer() ? "used" : "unused");
581 		for (uint32 i = 0; i < buffer->VecCount(); i++) {
582 			dprintf("    [%lu] base %p, length %lu%s\n", i,
583 				buffer->VecAt(i).iov_base, buffer->VecAt(i).iov_len,
584 				buffer->UsesBounceBufferAt(i) ? ", bounce" : "");
585 		}
586 
587 		dprintf("  remaining bytes: %lu\n", 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 iovec& vec = buffer->VecAt(i);
598 
599 			if (target.length != vec.iov_len)
600 				_Panic("[%lu] length differs", i);
601 
602 			void* address;
603 			if (target.uses_bounce_buffer) {
604 				address = (void*)(target.address
605 					+ (addr_t)buffer->PhysicalBounceBufferAddress());
606 			} else if (fSuite.IsContiguous() || target.address < B_PAGE_SIZE) {
607 				address = (void*)(target.address + fSuite.PhysicalDataBase());
608 			} else {
609 				address = (void*)(target.address - B_PAGE_SIZE
610 					+ fSuite.SecondPhysicalDataBase());
611 			}
612 
613 			if (address != vec.iov_base) {
614 				_Panic("[%lu] address differs: %p, should be %p", i,
615 					vec.iov_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 
658 void
659 Test::_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("    [%p, %lu]\n", fSourceVecs[i].iov_base,
678 			fSourceVecs[i].iov_len);
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 
698 static void
699 run_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 
765 static void
766 run_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 
791 static void
792 run_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 
835 static void
836 run_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 
862 static void
863 run_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 
904 static void
905 run_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 
936 static void
937 run_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 
999 static void
1000 run_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 
1033 static void
1034 run_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 
1060 static void
1061 run_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 
1092 static float
1093 dma_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 
1104 static status_t
1105 dma_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 
1117 static status_t
1118 dma_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 
1133 static void
1134 dma_test_uninit_driver(void *driverCookie)
1135 {
1136 	delete_area(sArea);
1137 }
1138 
1139 
1140 static status_t
1141 dma_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 
1152 static status_t
1153 dma_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) IOScheduler(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 
1196 static void
1197 dma_test_uninit_device(void *deviceCookie)
1198 {
1199 }
1200 
1201 
1202 static status_t
1203 dma_test_open(void *deviceCookie, const char *path, int openMode,
1204 	void **_cookie)
1205 {
1206 	return B_OK;
1207 }
1208 
1209 
1210 static status_t
1211 dma_test_close(void *cookie)
1212 {
1213 	return B_OK;
1214 }
1215 
1216 
1217 static status_t
1218 dma_test_free(void *cookie)
1219 {
1220 	return B_OK;
1221 }
1222 
1223 
1224 static status_t
1225 dma_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, 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 
1256 static status_t
1257 dma_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, (void*)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 
1290 static status_t
1291 dma_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 
1299 static status_t
1300 dma_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 
1348 module_dependency module_dependencies[] = {
1349 	{B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager},
1350 	{}
1351 };
1352 
1353 
1354 static 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 
1368 static 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 
1393 const module_info* modules[] = {
1394 	(module_info*)&sDMATestDriverModule,
1395 	(module_info*)&sDMATestDeviceModule,
1396 	NULL
1397 };
1398