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