| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <lib/dma-buffer/buffer.h> |
| #include <lib/fake-object/object.h> |
| #include <lib/zx/status.h> |
| #include <lib/zx/vmo.h> |
| |
| #include <map> |
| |
| #include <fbl/auto_lock.h> |
| #include <zxtest/zxtest.h> |
| |
| namespace { |
| |
| const zx::bti kFakeBti(42); |
| |
| struct VmoMetadata { |
| size_t size = 0; |
| uint32_t alignment_log2 = 0; |
| zx_handle_t bti_handle = ZX_HANDLE_INVALID; |
| uint32_t cache_policy = 0; |
| zx_paddr_t start_phys = 0; |
| void* virt = nullptr; |
| bool contiguous = false; |
| }; |
| |
| static bool unpinned = false; |
| |
| class VmoWrapper : public fake_object::Object { |
| public: |
| explicit VmoWrapper(zx::vmo vmo) : vmo_(std::move(vmo)) {} |
| virtual fake_object::HandleType type() const final { return fake_object::HandleType::CUSTOM; } |
| zx::unowned_vmo vmo() { return vmo_.borrow(); } |
| VmoMetadata& metadata() { return metadata_; } |
| |
| private: |
| zx::vmo vmo_; |
| VmoMetadata metadata_ = {}; |
| }; |
| |
| extern "C" { |
| zx_status_t zx_vmo_create_contiguous(zx_handle_t bti_handle, size_t size, uint32_t alignment_log2, |
| zx_handle_t* out) { |
| zx::vmo vmo = {}; |
| zx_status_t status = REAL_SYSCALL(zx_vmo_create)(size, 0, vmo.reset_and_get_address()); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| auto vmo_wrapper = fbl::AdoptRef(new VmoWrapper(std::move(vmo))); |
| vmo_wrapper->metadata().alignment_log2 = alignment_log2; |
| vmo_wrapper->metadata().bti_handle = bti_handle; |
| vmo_wrapper->metadata().size = size; |
| zx::status add_res = fake_object::FakeHandleTable().Add(std::move(vmo_wrapper)); |
| if (add_res.is_ok()) { |
| *out = add_res.value(); |
| } |
| return add_res.status_value(); |
| } |
| |
| zx_status_t zx_vmo_create(uint64_t size, uint32_t options, zx_handle_t* out) { |
| zx::vmo vmo = {}; |
| zx_status_t status = REAL_SYSCALL(zx_vmo_create)(size, options, vmo.reset_and_get_address()); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| auto vmo_wrapper = fbl::AdoptRef(new VmoWrapper(std::move(vmo))); |
| vmo_wrapper->metadata().size = size; |
| zx::status add_res = fake_object::FakeHandleTable().Add(std::move(vmo_wrapper)); |
| if (add_res.is_ok()) { |
| *out = add_res.value(); |
| } |
| return add_res.status_value(); |
| } |
| |
| zx_status_t zx_vmar_map(zx_handle_t vmar_handle, zx_vm_option_t options, uint64_t vmar_offset, |
| zx_handle_t vmo_handle, uint64_t vmo_offset, uint64_t len, |
| zx_vaddr_t* mapped_addr) { |
| zx::status get_res = fake_object::FakeHandleTable().Get(vmo_handle); |
| if (!get_res.is_ok()) { |
| return get_res.status_value(); |
| } |
| auto vmo = fbl::RefPtr<VmoWrapper>::Downcast(std::move(get_res.value())); |
| |
| zx_status_t status = REAL_SYSCALL(zx_vmar_map)(vmar_handle, options, vmar_offset, |
| vmo->vmo()->get(), vmo_offset, len, mapped_addr); |
| if (status == ZX_OK) { |
| vmo->metadata().virt = reinterpret_cast<void*>(*mapped_addr); |
| } |
| return status; |
| } |
| |
| zx_status_t zx_vmo_set_cache_policy(zx_handle_t handle, uint32_t cache_policy) { |
| zx::status get_res = fake_object::FakeHandleTable().Get(handle); |
| if (!get_res.is_ok()) { |
| return get_res.status_value(); |
| } |
| auto vmo = fbl::RefPtr<VmoWrapper>::Downcast(std::move(get_res.value())); |
| vmo->metadata().cache_policy = cache_policy; |
| return ZX_OK; |
| } |
| |
| zx_status_t zx_bti_pin(zx_handle_t bti_handle, uint32_t options, zx_handle_t vmo_handle, |
| uint64_t offset, uint64_t size, zx_paddr_t* addrs, size_t addrs_count, |
| zx_handle_t* out) { |
| static uint64_t current_phys = 0; |
| static fbl::Mutex phys_lock; |
| |
| if (bti_handle != kFakeBti.get()) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| |
| zx::status get_res = fake_object::FakeHandleTable().Get(vmo_handle); |
| if (!get_res.is_ok()) { |
| return get_res.status_value(); |
| } |
| auto vmo = fbl::RefPtr<VmoWrapper>::Downcast(std::move(get_res.value())); |
| |
| fbl::AutoLock lock(&phys_lock); |
| vmo->metadata().start_phys = current_phys; |
| *addrs = current_phys; |
| current_phys += vmo->metadata().size; |
| *out = ZX_HANDLE_INVALID; |
| return ZX_OK; |
| } |
| |
| zx_status_t zx_pmt_unpin(zx_handle_t handle) { |
| if (handle == ZX_HANDLE_INVALID) { |
| unpinned = true; |
| } |
| return ZX_OK; |
| } |
| |
| } // extern "C" |
| |
| } // namespace |
| |
| namespace dma_buffer { |
| TEST(DmaBufferTests, InitWithCacheEnabled) { |
| unpinned = false; |
| { |
| std::unique_ptr<ContiguousBuffer> buffer; |
| const size_t size = ZX_PAGE_SIZE * 4; |
| const size_t alignment = 2; |
| auto factory = CreateBufferFactory(); |
| ASSERT_OK(factory->CreateContiguous(kFakeBti, size, alignment, &buffer)); |
| auto test_f = [&buffer](fake_object::Object* obj) -> bool { |
| auto vmo = static_cast<VmoWrapper*>(obj); |
| ZX_ASSERT(vmo->metadata().alignment_log2 == alignment); |
| ZX_ASSERT(vmo->metadata().bti_handle == kFakeBti.get()); |
| ZX_ASSERT(vmo->metadata().cache_policy == 0); |
| ZX_ASSERT(vmo->metadata().size == size); |
| ZX_ASSERT(buffer->virt() == vmo->metadata().virt); |
| ZX_ASSERT(buffer->size() == vmo->metadata().size); |
| ZX_ASSERT(buffer->phys() == vmo->metadata().start_phys); |
| return false; |
| }; |
| fake_object::FakeHandleTable().ForEach(fake_object::HandleType::CUSTOM, test_f); |
| } |
| ASSERT_TRUE(unpinned); |
| } |
| |
| TEST(DmaBufferTests, InitWithCacheDisabled) { |
| unpinned = false; |
| { |
| std::unique_ptr<PagedBuffer> buffer; |
| auto factory = CreateBufferFactory(); |
| ASSERT_OK(factory->CreatePaged(kFakeBti, ZX_PAGE_SIZE, false, &buffer)); |
| auto test_f = [&buffer](fake_object::Object* object) -> bool { |
| auto vmo = static_cast<VmoWrapper*>(object); |
| ZX_ASSERT(vmo->metadata().alignment_log2 == 0); |
| ZX_ASSERT(vmo->metadata().cache_policy == ZX_CACHE_POLICY_UNCACHED_DEVICE); |
| ZX_ASSERT(vmo->metadata().size == ZX_PAGE_SIZE); |
| ZX_ASSERT(buffer->virt() == vmo->metadata().virt); |
| ZX_ASSERT(buffer->size() == vmo->metadata().size); |
| ZX_ASSERT(buffer->phys()[0] == vmo->metadata().start_phys); |
| return false; |
| }; |
| fake_object::FakeHandleTable().ForEach(fake_object::HandleType::CUSTOM, test_f); |
| } |
| ASSERT_TRUE(unpinned); |
| } |
| |
| TEST(DmaBufferTests, InitCachedMultiPageBuffer) { |
| unpinned = false; |
| { |
| std::unique_ptr<ContiguousBuffer> buffer; |
| auto factory = CreateBufferFactory(); |
| ASSERT_OK(factory->CreateContiguous(kFakeBti, ZX_PAGE_SIZE * 4, 0, &buffer)); |
| auto test_f = [&buffer](fake_object::Object* object) -> bool { |
| auto vmo = static_cast<VmoWrapper*>(object); |
| ZX_ASSERT(vmo->metadata().alignment_log2 == 0); |
| ZX_ASSERT(vmo->metadata().cache_policy == 0); |
| ZX_ASSERT(vmo->metadata().bti_handle == kFakeBti.get()); |
| ZX_ASSERT(vmo->metadata().size == ZX_PAGE_SIZE * 4); |
| ZX_ASSERT(buffer->virt() == vmo->metadata().virt); |
| ZX_ASSERT(buffer->size() == vmo->metadata().size); |
| ZX_ASSERT(buffer->phys() == vmo->metadata().start_phys); |
| return false; |
| }; |
| fake_object::FakeHandleTable().ForEach(fake_object::HandleType::CUSTOM, test_f); |
| } |
| ASSERT_TRUE(unpinned); |
| } |
| |
| } // namespace dma_buffer |