/* * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ #include #include #include #include #include "dma_resources.h" #include "io_requests.h" #include "IOSchedulerSimple.h" #define DMA_TEST_BLOCK_SIZE 512 #define DMA_TEST_BUFFER_COUNT 10 #define DMA_TEST_BOUNCE_BUFFER_COUNT 128 class TestSuite; class TestSuiteContext { public: TestSuiteContext(); ~TestSuiteContext(); status_t Init(size_t size, bool contiguous = true); addr_t DataBase() const { return fDataBase; } addr_t PhysicalDataBase() const { return fPhysicalDataBase; } addr_t SecondPhysicalDataBase() const { return fSecondPhysicalDataBase; } bool IsContiguous() const { return fSecondPhysicalDataBase == 0; } addr_t CompareBase() const { return fCompareBase; } size_t Size() const { return fSize; } private: void _Uninit(); area_id fDataArea; addr_t fDataBase; addr_t fPhysicalDataBase; addr_t fSecondPhysicalDataBase; area_id fCompareArea; addr_t fCompareBase; size_t fSize; }; class Test : public DoublyLinkedListLinkImpl { public: Test(TestSuite& suite, off_t offset, size_t length, bool isWrite, uint32 flags); Test& AddSource(addr_t base, size_t length); Test& NextResult(off_t offset, bool partialBegin, bool partialEnd); Test& AddTarget(addr_t base, size_t length, bool usesBounceBuffer); void Run(DMAResource& resource); private: addr_t _SourceToVirtual(addr_t source); addr_t _SourceToCompare(addr_t source); void _Prepare(); void _CheckCompare(); void _CheckWrite(); void _CheckResults(); status_t _DoIO(IOOperation& operation); void _Panic(const char* message,...); TestSuite& fSuite; off_t fOffset; size_t fLength; bool fIsWrite; uint32 fFlags; generic_io_vec fSourceVecs[32]; uint32 fSourceCount; struct target_t { addr_t address; size_t length; bool uses_bounce_buffer; }; struct result_t { off_t offset; target_t targets[32]; uint32 count; bool partial_begin; bool partial_end; }; result_t fResults[32]; uint32 fResultCount; }; typedef DoublyLinkedList TestList; class TestSuite { public: TestSuite(TestSuiteContext& context, const char* name, const dma_restrictions& restrictions, size_t blockSize) : fContext(context) { dprintf("----- Run \"%s\" tests ---------------------------\n", name); dprintf(" DMA restrictions: address %#" B_PRIxGENADDR " - %#" B_PRIxGENADDR ", align %" B_PRIuGENADDR ", boundary %" B_PRIuGENADDR ",\n max transfer %" B_PRIuGENADDR ", max segs %" B_PRIx32 ", max seg size %" B_PRIxGENADDR ", flags %" B_PRIx32 "\n\n", restrictions.low_address, restrictions.high_address, restrictions.alignment, restrictions.boundary, restrictions.max_transfer_size, restrictions.max_segment_count, restrictions.max_segment_size, restrictions.flags); status_t status = fDMAResource.Init(restrictions, blockSize, 10, 10); if (status != B_OK) panic("initializing DMA resource failed: %s\n", strerror(status)); } ~TestSuite() { while (Test* test = fTests.RemoveHead()) { delete test; } } Test& AddTest(off_t offset, size_t length, bool isWrite, uint32 flags) { Test* test = new(std::nothrow) Test(*this, offset, length, isWrite, flags); fTests.Add(test); return *test; } void Run() { TestList::Iterator iterator = fTests.GetIterator(); uint32 count = 1; while (Test* test = iterator.Next()) { dprintf("test %lu...\n", count++); test->Run(fDMAResource); } } addr_t DataBase() const { return fContext.DataBase(); } addr_t PhysicalDataBase() const { return fContext.PhysicalDataBase(); } addr_t SecondPhysicalDataBase() const { return fContext.SecondPhysicalDataBase(); } bool IsContiguous() const { return fContext.IsContiguous(); } addr_t CompareBase() const { return fContext.CompareBase(); } size_t Size() const { return fContext.Size(); } private: TestSuiteContext& fContext; DMAResource fDMAResource; uint8* fBase; uint8* fPhysicalBase; size_t fSize; TestList fTests; }; struct device_manager_info* sDeviceManager; static area_id sArea; static size_t sAreaSize; static void* sAreaAddress; static DMAResource* sDMAResource; static IOScheduler* sIOScheduler; status_t do_io(void* data, IOOperation* operation) { uint8* disk = (uint8*)sAreaAddress; off_t offset = operation->Offset(); for (uint32 i = 0; i < operation->VecCount(); i++) { const generic_io_vec& vec = operation->Vecs()[i]; generic_addr_t base = vec.base; generic_size_t length = vec.length; if (operation->IsWrite()) vm_memcpy_from_physical(disk + offset, base, length, false); else vm_memcpy_to_physical(base, disk + offset, length, false); } if (sIOScheduler != NULL) sIOScheduler->OperationCompleted(operation, B_OK, operation->Length()); return B_OK; } // #pragma mark - TestSuiteContext::TestSuiteContext() : fDataArea(-1), fCompareArea(-1), fSize(0) { } TestSuiteContext::~TestSuiteContext() { _Uninit(); } void TestSuiteContext::_Uninit() { delete_area(fDataArea); delete_area(fCompareArea); } status_t TestSuiteContext::Init(size_t size, bool contiguous) { if (!contiguous) { // we can't force this, so we have to try and see if (size != B_PAGE_SIZE * 2) return B_NOT_SUPPORTED; while (true) { fDataArea = create_area("data buffer", (void**)&fDataBase, B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); if (fDataArea < B_OK) return fDataArea; // get memory map to see if we succeeded physical_entry entry[2]; get_memory_map((void*)fDataBase, 2 * B_PAGE_SIZE, entry, 2); if (entry[0].size == B_PAGE_SIZE) { fPhysicalDataBase = (addr_t)entry[0].address; fSecondPhysicalDataBase = (addr_t)entry[1].address; dprintf("DMA Test area %p, physical %#" B_PRIxPHYSADDR ", second physical %#" B_PRIxPHYSADDR "\n", (void*)fDataBase, entry[0].address, entry[1].address); break; } // try again delete_area(fDataArea); } } else { fDataArea = create_area("data buffer", (void**)&fDataBase, B_ANY_KERNEL_ADDRESS, size, B_CONTIGUOUS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); if (fDataArea < B_OK) return fDataArea; physical_entry entry; get_memory_map((void*)fDataBase, B_PAGE_SIZE, &entry, 1); fPhysicalDataBase = (addr_t)entry.address; fSecondPhysicalDataBase = 0; dprintf("DMA Test area %p, physical %#" B_PRIxPHYSADDR "\n", (void*)fDataBase, entry.address); } fCompareArea = create_area("compare buffer", (void**)&fCompareBase, B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); if (fCompareArea < B_OK) return fCompareArea; fSize = size; return B_OK; } // #pragma mark - Test::Test(TestSuite& suite, off_t offset, size_t length, bool isWrite, uint32 flags) : fSuite(suite), fOffset(offset), fLength(length), fIsWrite(isWrite), fFlags(flags), fSourceCount(0), fResultCount(0) { } Test& Test::AddSource(addr_t address, size_t length) { if (fSuite.IsContiguous() || (fFlags & B_PHYSICAL_IO_REQUEST) != 0 || address < B_PAGE_SIZE) { fSourceVecs[fSourceCount].base = ((fFlags & B_PHYSICAL_IO_REQUEST) == 0 ? fSuite.DataBase() : fSuite.PhysicalDataBase()) + address; } else { fSourceVecs[fSourceCount].base = fSuite.SecondPhysicalDataBase() + address; } fSourceVecs[fSourceCount].length = length; fSourceCount++; return *this; } Test& Test::NextResult(off_t offset, bool partialBegin, bool partialEnd) { fResults[fResultCount].offset = offset; fResults[fResultCount].count = 0; fResults[fResultCount].partial_begin = partialBegin; fResults[fResultCount].partial_end = partialEnd; fResultCount++; return *this; } Test& Test::AddTarget(addr_t base, size_t length, bool usesBounceBuffer) { struct result_t& result = fResults[fResultCount - 1]; struct target_t& target = result.targets[result.count++]; target.address = base; target.length = length; target.uses_bounce_buffer = usesBounceBuffer; return *this; } addr_t Test::_SourceToVirtual(addr_t source) { if ((fFlags & B_PHYSICAL_IO_REQUEST) != 0) { if (!fSuite.IsContiguous() && source >= B_PAGE_SIZE) return source - fSuite.SecondPhysicalDataBase() + fSuite.DataBase(); return source - fSuite.PhysicalDataBase() + fSuite.DataBase(); } return source; } addr_t Test::_SourceToCompare(addr_t source) { if ((fFlags & B_PHYSICAL_IO_REQUEST) != 0) { if (!fSuite.IsContiguous() && source >= B_PAGE_SIZE) { return source - fSuite.SecondPhysicalDataBase() + fSuite.CompareBase(); } return source - fSuite.PhysicalDataBase() + fSuite.CompareBase(); } return source - fSuite.DataBase() + fSuite.CompareBase(); } void Test::_Prepare() { // prepare disk uint8* disk = (uint8*)sAreaAddress; for (size_t i = 0; i < sAreaSize; i++) { disk[i] = i % 26 + 'a'; } // prepare data memset((void*)fSuite.DataBase(), 0xcc, fSuite.Size()); if (fIsWrite) { off_t offset = fOffset; size_t length = fLength; for (uint32 i = 0; i < fSourceCount; i++) { uint8* data = (uint8*)_SourceToVirtual(fSourceVecs[i].base); size_t vecLength = min_c(fSourceVecs[i].length, length); for (uint32 j = 0; j < vecLength; j++) { data[j] = (offset + j) % 10 + '0'; } offset += vecLength; length -= vecLength; } } // prepare compare data memset((void*)fSuite.CompareBase(), 0xcc, fSuite.Size()); if (fIsWrite) { // copy data from source off_t offset = fOffset; size_t length = fLength; for (uint32 i = 0; i < fSourceCount; i++) { uint8* compare = (uint8*)_SourceToCompare(fSourceVecs[i].base); size_t vecLength = min_c(fSourceVecs[i].length, length); memcpy(compare, (void*)_SourceToVirtual(fSourceVecs[i].base), vecLength); offset += vecLength; length -= vecLength; } } else { // copy data from drive off_t offset = fOffset; size_t length = fLength; for (uint32 i = 0; i < fSourceCount; i++) { uint8* compare = (uint8*)_SourceToCompare(fSourceVecs[i].base); size_t vecLength = min_c(fSourceVecs[i].length, length); memcpy(compare, disk + offset, vecLength); offset += vecLength; length -= vecLength; } } if (fIsWrite) _CheckCompare(); } void Test::_CheckCompare() { uint8* data = (uint8*)fSuite.DataBase(); uint8* compare = (uint8*)fSuite.CompareBase(); for (size_t i = 0; i < fSuite.Size(); i++) { if (data[i] != compare[i]) { dprintf("offset %lu differs, %s:\n", i, fIsWrite ? "write" : "read"); i &= ~63; dump_block((char*)&data[i], min_c(64, fSuite.Size() - i), " "); dprintf("should be:\n"); dump_block((char*)&compare[i], min_c(64, fSuite.Size() - i), " "); _Panic("Data %s differs", fIsWrite ? "write" : "read"); } } } void Test::_CheckWrite() { _CheckCompare(); // check if we overwrote parts we shouldn't have uint8* disk = (uint8*)sAreaAddress; for (size_t i = 0; i < sAreaSize; i++) { if (i >= fOffset && i < fOffset + fLength) continue; if (disk[i] != i % 26 + 'a') { dprintf("disk[i] %c, expected %c, i %lu, fLength + fOffset %lld\n", disk[i], (int)(i % 26 + 'a'), i, fLength + fOffset); dprintf("offset %lu differs, touched innocent data:\n", i); i &= ~63; dump_block((char*)&disk[i], min_c(64, fSuite.Size() - i), " "); _Panic("Data %s differs", fIsWrite ? "write" : "read"); } } // check if the data we wanted to have on disk ended up there off_t offset = fOffset; size_t length = fLength; for (uint32 i = 0; i < fSourceCount; i++) { uint8* data = (uint8*)_SourceToVirtual(fSourceVecs[i].base); size_t vecLength = min_c(fSourceVecs[i].length, length); for (uint32 j = 0; j < vecLength; j++) { if (disk[offset + j] != data[j]) { dprintf("offset %lu differs, found on disk:\n", j); j &= ~63; dump_block((char*)&disk[offset + j], min_c(64, fSuite.Size() - i), " "); dprintf("should be:\n"); dump_block((char*)&data[j], min_c(64, fSuite.Size() - j), " "); _Panic("Data write differs"); } } offset += vecLength; length -= vecLength; } } void Test::_CheckResults() { if (fIsWrite) _CheckWrite(); else _CheckCompare(); } status_t Test::_DoIO(IOOperation& operation) { return do_io(NULL, &operation); } void Test::Run(DMAResource& resource) { _Prepare(); IORequest request; status_t status = request.Init(fOffset, fSourceVecs, fSourceCount, fLength, fIsWrite, fFlags); if (status != B_OK) _Panic("request init failed: %s\n", strerror(status)); uint32 resultIndex = 0; IOOperation operation; while (request.RemainingBytes() > 0) { if (resultIndex >= fResultCount) _Panic("no results left"); status_t status = resource.TranslateNext(&request, &operation, 0); if (status != B_OK) { _Panic("DMAResource::TranslateNext() failed: %s\n", strerror(status)); break; } DMABuffer* buffer = operation.Buffer(); dprintf("IOOperation: offset %" B_PRIdOFF ", length %" B_PRIuGENADDR " (%" B_PRIdOFF "/%" B_PRIuGENADDR ")\n", operation.Offset(), operation.Length(), operation.OriginalOffset(), operation.OriginalLength()); dprintf(" DMABuffer %p, %lu vecs, bounce buffer: %p (%p) %s\n", buffer, buffer->VecCount(), buffer->BounceBufferAddress(), (void*)buffer->PhysicalBounceBufferAddress(), operation.UsesBounceBuffer() ? "used" : "unused"); for (uint32 i = 0; i < buffer->VecCount(); i++) { dprintf(" [%" B_PRIu32 "] base %#" B_PRIxGENADDR ", length %" B_PRIuGENADDR "%s\n", i, buffer->VecAt(i).base, buffer->VecAt(i).length, buffer->UsesBounceBufferAt(i) ? ", bounce" : ""); } dprintf(" remaining bytes: %" B_PRIuGENADDR "\n", request.RemainingBytes()); // check results const result_t& result = fResults[resultIndex]; if (result.count != buffer->VecCount()) panic("result count differs (expected %lu)\n", result.count); for (uint32 i = 0; i < result.count; i++) { const target_t& target = result.targets[i]; const generic_io_vec& vec = buffer->VecAt(i); if (target.length != vec.length) _Panic("[%lu] length differs", i); generic_addr_t address; if (target.uses_bounce_buffer) { address = target.address + (addr_t)buffer->PhysicalBounceBufferAddress(); } else if (fSuite.IsContiguous() || target.address < B_PAGE_SIZE) { address = target.address + fSuite.PhysicalDataBase(); } else { address = target.address - B_PAGE_SIZE + fSuite.SecondPhysicalDataBase(); } if (address != vec.base) { _Panic("[%" B_PRIu32 "] address differs: %#" B_PRIxGENADDR ", should be %#" B_PRIuGENADDR "", i, vec.base, address); } } _DoIO(operation); operation.SetStatus(B_OK); bool finished = operation.Finish(); bool isPartial = result.partial_begin || result.partial_end; if (finished == (isPartial && fIsWrite)) _Panic("partial finished %s", finished ? "early" : "late"); if (!finished) { dprintf(" operation not done yet!\n"); _DoIO(operation); operation.SetStatus(B_OK); isPartial = result.partial_begin && result.partial_end; finished = operation.Finish(); if (finished == result.partial_begin && result.partial_end) _Panic("partial finished %s", finished ? "early" : "late"); if (!finished) { dprintf(" operation not done yet!\n"); _DoIO(operation); operation.SetStatus(B_OK); if (!operation.Finish()) _Panic("operation doesn't finish"); } } request.OperationFinished(&operation, operation.Status(), false, operation.OriginalOffset() - operation.Parent()->Offset() + operation.OriginalLength()); resultIndex++; } _CheckResults(); } void Test::_Panic(const char* message,...) { char buffer[1024]; va_list args; va_start(args, message); vsnprintf(buffer, sizeof(buffer), message, args); va_end(args); dprintf("test failed\n"); dprintf(" offset: %lld\n", fOffset); dprintf(" base: %p (physical: %p)\n", (void*)fSuite.DataBase(), (void*)fSuite.PhysicalDataBase()); dprintf(" length: %lu\n", fLength); dprintf(" write: %d\n", fIsWrite); dprintf(" flags: %#lx\n", fFlags); dprintf(" sources:\n"); for (uint32 i = 0; i < fSourceCount; i++) { dprintf(" [%#" B_PRIxGENADDR ", %" B_PRIuGENADDR "]\n", fSourceVecs[i].base, fSourceVecs[i].length); } for (uint32 i = 0; i < fResultCount; i++) { const result_t& result = fResults[i]; dprintf(" result %lu:\n", i); dprintf(" offset: %lld\n", result.offset); dprintf(" partial: %d/%d\n", result.partial_begin, result.partial_end); for (uint32 k = 0; k < result.count; k++) { const target_t& target = result.targets[k]; dprintf(" [%p, %lu, %d]\n", (void*)target.address, target.length, target.uses_bounce_buffer); } } panic("%s", buffer); } static void run_tests_no_restrictions(TestSuiteContext& context) { const dma_restrictions restrictions = { 0x0, // low 0x0, // high 0, // alignment 0, // boundary 0, // max transfer 0, // max segment count 0, // max segment size 0 // flags }; TestSuite suite(context, "no restrictions", restrictions, 512); suite.AddTest(0, 1024, false, 0) .AddSource(0, 1024) .NextResult(0, false, false) .AddTarget(0, 1024, false); // read partial begin/end suite.AddTest(23, 1024, false, 0) .AddSource(0, 1024) .NextResult(0, true, true) .AddTarget(0, 23, true) .AddTarget(0, 1024, false) .AddTarget(23, 512 - 23, true); // read less than a block suite.AddTest(23, 30, false, 0) .AddSource(0, 1024) .NextResult(0, true, true) .AddTarget(0, 23, true) .AddTarget(0, 30, false) .AddTarget(23, 512 - 53, true); // write begin/end suite.AddTest(23, 1024, true, 0) .AddSource(0, 1024) .NextResult(0, true, true) .AddTarget(0, 512, true) .AddTarget(489, 512, false) .AddTarget(512, 512, true); // read partial end, length < iovec length suite.AddTest(0, 1028, false, 0) .AddSource(0, 512) .AddSource(1024, 1024) .NextResult(0, false, true) .AddTarget(0, 512, false) .AddTarget(1024, 516, false) .AddTarget(0, 508, true); // write partial end, length < iovec length suite.AddTest(0, 1028, true, 0) .AddSource(0, 512) .AddSource(1024, 1024) .NextResult(0, false, true) .AddTarget(0, 512, false) .AddTarget(1024, 512, false) .AddTarget(0, 512, true); suite.Run(); } static void run_tests_address_restrictions(TestSuiteContext& context) { const dma_restrictions restrictions = { context.PhysicalDataBase() + 512, // low 0, // high 0, // alignment 0, // boundary 0, // max transfer 0, // max segment count 0, // max segment size 0 // flags }; TestSuite suite(context, "address", restrictions, 512); suite.AddTest(0, 1024, false, 0) .AddSource(0, 1024) .NextResult(0, false, false) .AddTarget(0, 512, true) .AddTarget(512, 512, false); suite.Run(); } static void run_tests_alignment_restrictions(TestSuiteContext& context) { const dma_restrictions restrictions = { 0x0, // low 0x0, // high 32, // alignment 0, // boundary 0, // max transfer 0, // max segment count 0, // max segment size 0 // flags }; TestSuite suite(context, "alignment", restrictions, 512); suite.AddTest(0, 1024, false, B_PHYSICAL_IO_REQUEST) .AddSource(16, 1024) .NextResult(0, false, false) .AddTarget(0, 1024, true); suite.AddTest(0, 2559, true, B_PHYSICAL_IO_REQUEST) .AddSource(0, 2559) .NextResult(0, false, true) .AddTarget(0, 2048, false) .AddTarget(0, 512, true); suite.AddTest(0, 51, true, B_PHYSICAL_IO_REQUEST) .AddSource(0, 4096) .NextResult(0, false, true) .AddTarget(0, 512, true); suite.AddTest(32, 51, true, B_PHYSICAL_IO_REQUEST) .AddSource(0, 4096) .NextResult(0, true, false) .AddTarget(0, 512, true); // Note: The operation has actually both partial begin and end, but // our Test is not clever enough to realize that it is only one // block and thus only one read phase is needed. suite.Run(); } static void run_tests_boundary_restrictions(TestSuiteContext& context) { const dma_restrictions restrictions = { 0x0, // low 0x0, // high 0, // alignment 1024, // boundary 0, // max transfer 0, // max segment count 0, // max segment size 0 // flags }; TestSuite suite(context, "boundary", restrictions, 512); suite.AddTest(0, 2000, false, 0) .AddSource(0, 2048) .NextResult(0, false, false) .AddTarget(0, 1024, false) .AddTarget(1024, 976, false) .AddTarget(0, 48, true); suite.Run(); } static void run_tests_segment_restrictions(TestSuiteContext& context) { const dma_restrictions restrictions = { 0x0, // low 0x0, // high 0, // alignment 0, // boundary 0, // max transfer 4, // max segment count 1024, // max segment size 0 // flags }; TestSuite suite(context, "segment", restrictions, 512); suite.AddTest(0, 4096, false, 0) .AddSource(0, 4096) .NextResult(0, false, false) .AddTarget(0, 1024, false) .AddTarget(1024, 1024, false) .AddTarget(2048, 1024, false) .AddTarget(3072, 1024, false); suite.AddTest(0, 2560, false, B_PHYSICAL_IO_REQUEST) .AddSource(0, 512) .AddSource(1024, 512) .AddSource(2048, 512) .AddSource(3072, 512) .AddSource(4096, 512) .NextResult(0, false, false) .AddTarget(0, 512, false) .AddTarget(1024, 512, false) .AddTarget(2048, 512, false) .AddTarget(3072, 512, false) .NextResult(2048, false, false) .AddTarget(4096, 512, false); suite.Run(); } static void run_tests_transfer_restrictions(TestSuiteContext& context) { const dma_restrictions restrictions = { 0x0, // low 0x0, // high 0, // alignment 0, // boundary 1024, // max transfer 0, // max segment count 0, // max segment size 0 // flags }; TestSuite suite(context, "transfer", restrictions, 512); suite.AddTest(0, 4000, false, 0) .AddSource(0, 4096) .NextResult(0, false, false) .AddTarget(0, 1024, false) .NextResult(0, false, false) .AddTarget(1024, 1024, false) .NextResult(0, false, false) .AddTarget(2048, 1024, false) .NextResult(0, false, false) .AddTarget(3072, 1024 - 96, false) .AddTarget(0, 96, true); suite.Run(); } static void run_tests_interesting_restrictions(TestSuiteContext& context) { dma_restrictions restrictions = { 0x0, // low 0x0, // high 32, // alignment 512, // boundary 0, // max transfer 0, // max segment count 0, // max segment size 0 // flags }; TestSuite suite(context, "interesting", restrictions, 512); // read with partial begin/end suite.AddTest(32, 1000, false, 0) .AddSource(0, 1024) .NextResult(0, true, true) .AddTarget(0, 32, true) .AddTarget(0, 512, false) .AddTarget(512, 480, false) .AddTarget(32, 480, true) .AddTarget(512, 32, true); // write with partial begin/end suite.AddTest(32, 1000, true, 0) .AddSource(0, 1024) .NextResult(0, true, true) .AddTarget(0, 512, true) .AddTarget(480, 32, false) .AddTarget(512, 480, false) .AddTarget(512, 512, true); suite.Run(); restrictions = (dma_restrictions){ 0x0, // low 0x0, // high 32, // alignment 512, // boundary 0, // max transfer 4, // max segment count 0, // max segment size 0 // flags }; TestSuite suite2(context, "interesting2", restrictions, 512); suite2.AddTest(32, 1000, false, 0) .AddSource(0, 1024) .NextResult(0, true, false) .AddTarget(0, 32, true) .AddTarget(0, 512, false) .AddTarget(512, 480, false) .NextResult(0, false, true) .AddTarget(0, 512, true); suite2.Run(); } static void run_tests_mean_restrictions(TestSuiteContext& context) { const dma_restrictions restrictions = { context.PhysicalDataBase() + 1024, // low 0x0, // high 32, // alignment 1024, // boundary 0, // max transfer 2, // max segment count 512, // max segment size 0 // flags }; TestSuite suite(context, "mean", restrictions, 512); suite.AddTest(0, 1024, false, 0) .AddSource(0, 1024) .NextResult(0, false, false) .AddTarget(0, 512, true) .AddTarget(512, 512, true); suite.AddTest(0, 1024, false, 0) .AddSource(1024 + 32, 1024) .NextResult(0, false, false) .AddTarget(1024 + 32, 512, false) .NextResult(0, false, false) .AddTarget(1568, 480, false) .AddTarget(1568 + 480, 32, false); suite.Run(); } static void run_tests_non_contiguous_no_restrictions(TestSuiteContext& context) { const dma_restrictions restrictions = { 0x0, // low 0x0, // high 0, // alignment 0, // boundary 0, // max transfer 0, // max segment count 0, // max segment size 0 // flags }; TestSuite suite(context, "non contiguous, no restrictions", restrictions, 512); suite.AddTest(0, 8192, false, 0) .AddSource(0, 8192) .NextResult(0, false, false) .AddTarget(0, 4096, false) .AddTarget(4096, 4096, false); suite.Run(); } static void run_test() { TestSuiteContext context; status_t status = context.Init(4 * B_PAGE_SIZE); if (status != B_OK) return; run_tests_no_restrictions(context); run_tests_address_restrictions(context); run_tests_alignment_restrictions(context); run_tests_boundary_restrictions(context); run_tests_segment_restrictions(context); run_tests_transfer_restrictions(context); run_tests_interesting_restrictions(context); run_tests_mean_restrictions(context); // physical non-contiguous status = context.Init(2 * B_PAGE_SIZE, false); if (status != B_OK) return; run_tests_non_contiguous_no_restrictions(context); dprintf("All tests passed!\n"); } // #pragma mark - driver static float dma_test_supports_device(device_node *parent) { const char* bus = NULL; if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) == B_OK && !strcmp(bus, "generic")) return 0.8; return -1; } static status_t dma_test_register_device(device_node *parent) { device_attr attrs[] = { {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "DMA Test"}}, {NULL} }; return sDeviceManager->register_node(parent, "drivers/disk/dma_resource_test/driver_v1", attrs, NULL, NULL); } static status_t dma_test_init_driver(device_node *node, void **_driverCookie) { sAreaSize = 10 * 1024 * 1024; sArea = create_area("dma test", &sAreaAddress, B_ANY_KERNEL_ADDRESS, sAreaSize, B_LAZY_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); if (sArea < B_OK) return sArea; *_driverCookie = node; run_test(); return B_OK; } static void dma_test_uninit_driver(void *driverCookie) { delete_area(sArea); } static status_t dma_test_register_child_devices(void *driverCookie) { return sDeviceManager->publish_device((device_node*)driverCookie, "disk/virtual/dma_test/raw", "drivers/disk/dma_resource_test/device_v1"); } // #pragma mark - device static status_t dma_test_init_device(void *driverCookie, void **_deviceCookie) { const dma_restrictions restrictions = { 0x0, // low 0x0, // high 4, // alignment 0, // boundary 0, // max transfer 0, // max segment count B_PAGE_SIZE, // max segment size 0 // flags }; *_deviceCookie = driverCookie; sDMAResource = new(std::nothrow) DMAResource; if (sDMAResource == NULL) return B_NO_MEMORY; status_t status = sDMAResource->Init(restrictions, DMA_TEST_BLOCK_SIZE, DMA_TEST_BUFFER_COUNT, DMA_TEST_BOUNCE_BUFFER_COUNT); if (status != B_OK) { delete sDMAResource; return status; } sIOScheduler = new(std::nothrow) IOSchedulerSimple(sDMAResource); if (sIOScheduler == NULL) { delete sDMAResource; return B_NO_MEMORY; } status = sIOScheduler->Init("dma test scheduler"); if (status != B_OK) { delete sIOScheduler; delete sDMAResource; return status; } sIOScheduler->SetCallback(&do_io, NULL); return B_OK; } static void dma_test_uninit_device(void *deviceCookie) { } static status_t dma_test_open(void *deviceCookie, const char *path, int openMode, void **_cookie) { return B_OK; } static status_t dma_test_close(void *cookie) { return B_OK; } static status_t dma_test_free(void *cookie) { return B_OK; } static status_t dma_test_read(void *cookie, off_t pos, void *buffer, size_t *_length) { size_t length = *_length; if (pos >= sAreaSize) return B_BAD_VALUE; if (pos + length > sAreaSize) length = sAreaSize - pos; #if 1 IORequest request; status_t status = request.Init(pos, (addr_t)buffer, length, false, 0); if (status != B_OK) return status; status = sIOScheduler->ScheduleRequest(&request); if (status != B_OK) return status; status = request.Wait(0, 0); dprintf("dma_test_read(): request.Wait() returned: %s\n", strerror(status)); #else status_t status = user_memcpy(buffer, (uint8*)sAreaAddress + pos, length); #endif if (status == B_OK) *_length = length; return status; } static status_t dma_test_write(void *cookie, off_t pos, const void *buffer, size_t *_length) { size_t length = *_length; if (pos >= sAreaSize) return B_BAD_VALUE; if (pos + length > sAreaSize) length = sAreaSize - pos; #if 1 IORequest request; status_t status = request.Init(pos, (addr_t)buffer, length, true, 0); if (status != B_OK) return status; status = sIOScheduler->ScheduleRequest(&request); if (status != B_OK) return status; status = request.Wait(0, 0); dprintf("dma_test_write(): request.Wait() returned: %s\n", strerror(status)); #else status_t status = user_memcpy((uint8*)sAreaAddress + pos, buffer, length); #endif if (status == B_OK) *_length = length; return status; } static status_t dma_test_io(void *cookie, io_request *request) { dprintf("dma_test_io(%p)\n", request); return sIOScheduler->ScheduleRequest(request); } static status_t dma_test_control(void *cookie, uint32 op, void *buffer, size_t length) { switch (op) { case B_GET_DEVICE_SIZE: return user_memcpy(buffer, &sAreaSize, sizeof(size_t)); case B_SET_NONBLOCKING_IO: case B_SET_BLOCKING_IO: return B_OK; case B_GET_READ_STATUS: case B_GET_WRITE_STATUS: { bool value = true; return user_memcpy(buffer, &value, sizeof(bool)); } case B_GET_GEOMETRY: case B_GET_BIOS_GEOMETRY: { device_geometry geometry; geometry.bytes_per_sector = DMA_TEST_BLOCK_SIZE; geometry.sectors_per_track = 1; geometry.cylinder_count = sAreaSize / DMA_TEST_BLOCK_SIZE; geometry.head_count = 1; geometry.device_type = B_DISK; geometry.removable = true; geometry.read_only = false; geometry.write_once = false; return user_memcpy(buffer, &geometry, sizeof(device_geometry)); } case B_GET_MEDIA_STATUS: { status_t status = B_OK; return user_memcpy(buffer, &status, sizeof(status_t)); } case B_SET_UNINTERRUPTABLE_IO: case B_SET_INTERRUPTABLE_IO: case B_FLUSH_DRIVE_CACHE: return B_OK; } return B_BAD_VALUE; } module_dependency module_dependencies[] = { {B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager}, {} }; static const struct driver_module_info sDMATestDriverModule = { { "drivers/disk/dma_resource_test/driver_v1", 0, NULL }, dma_test_supports_device, dma_test_register_device, dma_test_init_driver, dma_test_uninit_driver, dma_test_register_child_devices }; static const struct device_module_info sDMATestDeviceModule = { { "drivers/disk/dma_resource_test/device_v1", 0, NULL }, dma_test_init_device, dma_test_uninit_device, NULL, dma_test_open, dma_test_close, dma_test_free, dma_test_read, dma_test_write, dma_test_io, dma_test_control, NULL, // select NULL // deselect }; const module_info* modules[] = { (module_info*)&sDMATestDriverModule, (module_info*)&sDMATestDeviceModule, NULL };