| // 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/fake-resource/resource.h> |
| #include <lib/zx/resource.h> |
| #include <lib/zx/vmar.h> |
| #include <lib/zx/vmo.h> |
| #include <zircon/syscalls/resource.h> |
| #include <zircon/types.h> |
| |
| #include <array> |
| #include <climits> // PAGE_SIZE |
| #include <utility> |
| |
| #include <zxtest/zxtest.h> |
| |
| namespace { |
| |
| class FakeResource : public zxtest::Test { |
| public: |
| zx_handle_t root_resource() const { return root_; } |
| |
| protected: |
| void SetUp() final { ASSERT_OK(fake_root_resource_create(&root_)); } |
| |
| private: |
| zx_handle_t root_; |
| }; |
| |
| bool validate_resource_info(zx_handle_t hnd, zx_paddr_t base, size_t size, zx_rsrc_kind_t kind, |
| const char* name) { |
| zx_info_resource_t info; |
| zx_status_t st = zx_object_get_info(hnd, ZX_INFO_RESOURCE, &info, sizeof(info), nullptr, nullptr); |
| if (st != ZX_OK) { |
| return false; |
| } |
| |
| return (info.kind == kind && info.base == base && info.size == size && |
| strncmp(info.name, name, sizeof(info.name)) == 0); |
| } |
| |
| TEST_F(FakeResource, ChildBoundsTest) { |
| zx_handle_t parent; |
| std::array<char, ZX_MAX_NAME_LEN> parent_name = {"parent"}; |
| std::array<char, ZX_MAX_NAME_LEN> child_name = {"child"}; |
| // Create a parent resource from |4096-8192| |
| const uintptr_t parent_base = PAGE_SIZE; |
| const size_t parent_size = PAGE_SIZE; |
| ASSERT_OK(zx_resource_create(root_resource(), ZX_RSRC_KIND_MMIO, parent_base, parent_size, |
| parent_name.data(), parent_name.size(), &parent)); |
| ASSERT_TRUE(validate_resource_info(parent, parent_base, parent_size, ZX_RSRC_KIND_MMIO, |
| parent_name.data())); |
| zx_handle_t child; |
| // Same span. |
| ASSERT_OK(zx_resource_create(parent, ZX_RSRC_KIND_MMIO, parent_base, parent_size, |
| child_name.data(), child_name.size(), &child)); |
| ASSERT_OK(zx_handle_close(child)); |
| // Subset of parent |
| ASSERT_OK(zx_resource_create(parent, ZX_RSRC_KIND_MMIO, parent_base + 1024, 1024, |
| child_name.data(), child_name.size(), &child)); |
| ASSERT_OK(zx_handle_close(child)); |
| // Superset of parent. |
| ASSERT_NOT_OK(zx_resource_create(parent, ZX_RSRC_KIND_MMIO, parent_base - 2048, |
| parent_size + 4096, child_name.data(), child_name.size(), |
| &child)); |
| // Before parent base. |
| ASSERT_NOT_OK(zx_resource_create(parent, ZX_RSRC_KIND_MMIO, parent_base - 2048, parent_size, |
| child_name.data(), child_name.size(), &child)); |
| // Past parent length. |
| ASSERT_NOT_OK(zx_resource_create(parent, ZX_RSRC_KIND_MMIO, parent_base + 2048, parent_size, |
| child_name.data(), child_name.size(), &child)); |
| |
| ASSERT_OK(zx_handle_close(parent)); |
| } |
| |
| TEST_F(FakeResource, ExclusiveBoundsTest) { |
| zx_handle_t first, second; |
| std::array<char, ZX_MAX_NAME_LEN> first_name = {"first"}; |
| std::array<char, ZX_MAX_NAME_LEN> second_name = {"second"}; |
| // Create a first resource from |4096-20480| |
| const uintptr_t first_base = PAGE_SIZE; |
| const size_t first_size = PAGE_SIZE * 4; |
| uint32_t flags = ZX_RSRC_KIND_MMIO | ZX_RSRC_FLAG_EXCLUSIVE; |
| ASSERT_OK(zx_resource_create(root_resource(), flags, first_base, first_size, first_name.data(), |
| first_name.size(), &first)); |
| ASSERT_TRUE(validate_resource_info(first, first_base, first_size, ZX_RSRC_EXTRACT_KIND(flags), |
| first_name.data())); |
| // Same span. |
| ASSERT_NOT_OK(zx_resource_create(root_resource(), flags, first_base, first_size, |
| second_name.data(), second_name.size(), &second)); |
| // Subset of first |
| ASSERT_NOT_OK(zx_resource_create(root_resource(), flags, first_base + PAGE_SIZE, PAGE_SIZE, |
| second_name.data(), second_name.size(), &second)); |
| // Superset of first. |
| ASSERT_NOT_OK(zx_resource_create(root_resource(), flags, first_base - PAGE_SIZE, |
| first_size + PAGE_SIZE, second_name.data(), second_name.size(), |
| &second)); |
| // Before first base. |
| ASSERT_NOT_OK(zx_resource_create(root_resource(), flags, first_base - PAGE_SIZE, first_size, |
| second_name.data(), second_name.size(), &second)); |
| // Past first length. |
| ASSERT_NOT_OK(zx_resource_create(root_resource(), flags, first_base + PAGE_SIZE, first_size, |
| second_name.data(), second_name.size(), &second)); |
| // Separate region entirely |
| ASSERT_OK(zx_resource_create(root_resource(), flags, first_base + first_size + PAGE_SIZE, |
| PAGE_SIZE, second_name.data(), second_name.size(), &second)); |
| |
| ASSERT_OK(zx_handle_close(first)); |
| ASSERT_OK(zx_handle_close(second)); |
| } |
| |
| TEST_F(FakeResource, ExclusiveNewAfterExisting) { |
| zx_handle_t first, second; |
| std::array<char, ZX_MAX_NAME_LEN> first_name = {"first"}; |
| std::array<char, ZX_MAX_NAME_LEN> second_name = {"second"}; |
| uintptr_t first_base = 0x1000; |
| uintptr_t size = 0x4000; |
| uint32_t flags = ZX_RSRC_KIND_MMIO | ZX_RSRC_FLAG_EXCLUSIVE; |
| |
| ASSERT_OK(zx_resource_create(root_resource(), flags, first_base, size, first_name.data(), |
| first_name.size(), &first)); |
| ASSERT_OK(zx_resource_create(root_resource(), flags, first_base + size, size, second_name.data(), |
| second_name.size(), &second)); |
| } |
| |
| TEST_F(FakeResource, IOPortTest) { |
| zx_handle_t io_child; |
| zx_handle_t null_child; |
| zx_handle_t mmio_child; |
| std::array<char, ZX_MAX_NAME_LEN> child_name = {"child"}; |
| ASSERT_OK(zx_resource_create(root_resource(), ZX_RSRC_KIND_IOPORT, 128, 128, child_name.data(), |
| child_name.size(), &io_child)); |
| ASSERT_OK(zx_resource_create(root_resource(), ZX_RSRC_KIND_IOPORT, 0, 0, child_name.data(), |
| child_name.size(), &null_child)); |
| ASSERT_OK(zx_resource_create(root_resource(), ZX_RSRC_KIND_MMIO, 128, 128, child_name.data(), |
| child_name.size(), &mmio_child)); |
| zx_info_resource_t info; |
| ASSERT_OK(zx_object_get_info(io_child, ZX_INFO_RESOURCE, &info, sizeof(info), nullptr, nullptr)); |
| // Within the span |
| ASSERT_OK(zx_ioports_request(io_child, static_cast<uint16_t>(info.base + 64), 32)); |
| ASSERT_OK(zx_ioports_release(io_child, static_cast<uint16_t>(info.base + 64), 32)); |
| // MMIO resources should not work |
| ASSERT_NOT_OK(zx_ioports_request(mmio_child, 64, 32)); |
| ASSERT_NOT_OK(zx_ioports_release(mmio_child, 64, 32)); |
| // IOPort resources with no allowable window shouldn't work either |
| ASSERT_NOT_OK(zx_ioports_request(null_child, 512, 512)); |
| ASSERT_NOT_OK(zx_ioports_release(null_child, 512, 512)); |
| } |
| |
| TEST_F(FakeResource, VmoTest) { |
| const uint64_t MAP_LEN = 64u; |
| zx::resource child; |
| zx::resource zx_root(root_resource()); |
| std::array<char, ZX_MAX_NAME_LEN> child_name = {"child"}; |
| ASSERT_OK(zx::resource::create(zx_root, ZX_RSRC_KIND_MMIO, 0, PAGE_SIZE, child_name.data(), |
| child_name.size(), &child)); |
| ASSERT_TRUE( |
| validate_resource_info(child.get(), 0, PAGE_SIZE, ZX_RSRC_KIND_MMIO, child_name.data())); |
| zx::vmo vmo; |
| uintptr_t vaddr; |
| ASSERT_OK(zx::vmo::create_physical(child, 0, PAGE_SIZE, &vmo)); |
| ASSERT_OK(vmo.set_cache_policy(ZX_CACHE_POLICY_UNCACHED_DEVICE)); |
| ASSERT_OK( |
| zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, vmo, 0, MAP_LEN, &vaddr)); |
| |
| // Perform some operations on the fake physical VMO we created to make sure |
| // nothing was screwed up in the chain. |
| std::array<uint8_t, MAP_LEN> buf = {}; |
| memset(buf.data(), 0xA5, MAP_LEN); |
| memcpy(reinterpret_cast<uint8_t*>(vaddr), buf.data(), MAP_LEN); |
| ASSERT_BYTES_EQ(reinterpret_cast<uint8_t*>(vaddr), buf.data(), MAP_LEN); |
| ASSERT_OK(zx::vmar::root_self()->unmap(vaddr, MAP_LEN)); |
| } |
| |
| } // namespace |