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 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 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: 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 130 ~TestSuite() 131 { 132 while (Test* test = fTests.RemoveHead()) { 133 delete test; 134 } 135 } 136 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 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 156 addr_t DataBase() const { return fContext.DataBase(); } 157 addr_t PhysicalDataBase() const { return fContext.PhysicalDataBase(); } 158 addr_t SecondPhysicalDataBase() const 159 { return fContext.SecondPhysicalDataBase(); } 160 bool IsContiguous() const { return fContext.IsContiguous(); } 161 addr_t CompareBase() const { return fContext.CompareBase(); } 162 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 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 209 TestSuiteContext::TestSuiteContext() 210 : 211 fDataArea(-1), 212 fCompareArea(-1), 213 fSize(0) 214 { 215 } 216 217 218 TestSuiteContext::~TestSuiteContext() 219 { 220 _Uninit(); 221 } 222 223 224 void 225 TestSuiteContext::_Uninit() 226 { 227 delete_area(fDataArea); 228 delete_area(fCompareArea); 229 } 230 231 232 status_t 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 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& 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& 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& 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 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 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 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 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 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 528 Test::_CheckResults() 529 { 530 if (fIsWrite) 531 _CheckWrite(); 532 else 533 _CheckCompare(); 534 } 535 536 537 status_t 538 Test::_DoIO(IOOperation& operation) 539 { 540 return do_io(NULL, &operation); 541 } 542 543 544 void 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 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 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) 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 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, (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 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 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