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