xref: /haiku/src/tests/system/kernel/device_manager/dma_resource_test.cpp (revision 425ac1b60a56f4df7a0e88bd784545c0ec4fa01f)
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 
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 
DataBase() const33 			addr_t			DataBase() const { return fDataBase; }
PhysicalDataBase() const34 			addr_t			PhysicalDataBase() const
35 								{ return fPhysicalDataBase; }
SecondPhysicalDataBase() const36 			addr_t			SecondPhysicalDataBase() const
37 								{ return fSecondPhysicalDataBase; }
38 
IsContiguous() const39 			bool			IsContiguous() const
40 								{ return fSecondPhysicalDataBase == 0; }
41 
CompareBase() const42 			addr_t			CompareBase() const { return fCompareBase; }
43 
Size() const44 			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 			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 
105 typedef DoublyLinkedList<Test> TestList;
106 
107 
108 class TestSuite {
109 public:
TestSuite(TestSuiteContext & context,const char * name,const dma_restrictions & restrictions,size_t blockSize)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 
~TestSuite()130 	~TestSuite()
131 	{
132 		while (Test* test = fTests.RemoveHead()) {
133 			delete test;
134 		}
135 	}
136 
AddTest(off_t offset,size_t length,bool isWrite,uint32 flags)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 
Run()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 
DataBase() const156 	addr_t DataBase() const { return fContext.DataBase(); }
PhysicalDataBase() const157 	addr_t PhysicalDataBase() const { return fContext.PhysicalDataBase(); }
SecondPhysicalDataBase() const158 	addr_t SecondPhysicalDataBase() const
159 		{ return fContext.SecondPhysicalDataBase(); }
IsContiguous() const160 	bool IsContiguous() const { return fContext.IsContiguous(); }
CompareBase() const161 	addr_t CompareBase() const { return fContext.CompareBase(); }
Size() const162 	size_t Size() const { return fContext.Size(); }
163 
164 private:
165 	TestSuiteContext& fContext;
166 	DMAResource		fDMAResource;
167 	uint8*			fBase;
168 	uint8*			fPhysicalBase;
169 	size_t			fSize;
170 	TestList		fTests;
171 };
172 
173 
174 struct device_manager_info* sDeviceManager;
175 
176 static area_id sArea;
177 static size_t sAreaSize;
178 static void* sAreaAddress;
179 static DMAResource* sDMAResource;
180 static IOScheduler* sIOScheduler;
181 
182 
183 status_t
do_io(void * data,IOOperation * operation)184 do_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 
TestSuiteContext()209 TestSuiteContext::TestSuiteContext()
210 	:
211 	fDataArea(-1),
212 	fCompareArea(-1),
213 	fSize(0)
214 {
215 }
216 
217 
~TestSuiteContext()218 TestSuiteContext::~TestSuiteContext()
219 {
220 	_Uninit();
221 }
222 
223 
224 void
_Uninit()225 TestSuiteContext::_Uninit()
226 {
227 	delete_area(fDataArea);
228 	delete_area(fCompareArea);
229 }
230 
231 
232 status_t
Init(size_t size,bool contiguous)233 TestSuiteContext::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 
Test(TestSuite & suite,off_t offset,size_t length,bool isWrite,uint32 flags)297 Test::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 
311 Test&
AddSource(addr_t address,size_t length)312 Test::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 
330 Test&
NextResult(off_t offset,bool partialBegin,bool partialEnd)331 Test::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 
343 Test&
AddTarget(addr_t base,size_t length,bool usesBounceBuffer)344 Test::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 
357 addr_t
_SourceToVirtual(addr_t source)358 Test::_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 
371 addr_t
_SourceToCompare(addr_t source)372 Test::_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 
387 void
_Prepare()388 Test::_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 
455 void
_CheckCompare()456 Test::_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 
476 void
_CheckWrite()477 Test::_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 
527 void
_CheckResults()528 Test::_CheckResults()
529 {
530 	if (fIsWrite)
531 		_CheckWrite();
532 	else
533 		_CheckCompare();
534 }
535 
536 
537 status_t
_DoIO(IOOperation & operation)538 Test::_DoIO(IOOperation& operation)
539 {
540 	return do_io(NULL, &operation);
541 }
542 
543 
544 void
Run(DMAResource & resource)545 Test::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 
658 void
_Panic(const char * message,...)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("    [%#" 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 
698 static void
run_tests_no_restrictions(TestSuiteContext & context)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
run_tests_address_restrictions(TestSuiteContext & context)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
run_tests_alignment_restrictions(TestSuiteContext & context)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
run_tests_boundary_restrictions(TestSuiteContext & context)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
run_tests_segment_restrictions(TestSuiteContext & context)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
run_tests_transfer_restrictions(TestSuiteContext & context)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
run_tests_interesting_restrictions(TestSuiteContext & context)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
run_tests_mean_restrictions(TestSuiteContext & context)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
run_tests_non_contiguous_no_restrictions(TestSuiteContext & context)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
run_test()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
dma_test_supports_device(device_node * parent)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
dma_test_register_device(device_node * parent)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
dma_test_init_driver(device_node * node,void ** _driverCookie)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
dma_test_uninit_driver(void * driverCookie)1134 dma_test_uninit_driver(void *driverCookie)
1135 {
1136 	delete_area(sArea);
1137 }
1138 
1139 
1140 static status_t
dma_test_register_child_devices(void * driverCookie)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
dma_test_init_device(void * driverCookie,void ** _deviceCookie)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) 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 
1196 static void
dma_test_uninit_device(void * deviceCookie)1197 dma_test_uninit_device(void *deviceCookie)
1198 {
1199 }
1200 
1201 
1202 static status_t
dma_test_open(void * deviceCookie,const char * path,int openMode,void ** _cookie)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
dma_test_close(void * cookie)1211 dma_test_close(void *cookie)
1212 {
1213 	return B_OK;
1214 }
1215 
1216 
1217 static status_t
dma_test_free(void * cookie)1218 dma_test_free(void *cookie)
1219 {
1220 	return B_OK;
1221 }
1222 
1223 
1224 static status_t
dma_test_read(void * cookie,off_t pos,void * buffer,size_t * _length)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, (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 
1256 static status_t
dma_test_write(void * cookie,off_t pos,const void * buffer,size_t * _length)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, (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 
1290 static status_t
dma_test_io(void * cookie,io_request * request)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
dma_test_control(void * cookie,uint32 op,void * buffer,size_t length)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