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