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