| // Copyright 2016 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/zx/interrupt.h> |
| #include <lib/zx/resource.h> |
| #include <lib/zx/vmo.h> |
| #include <limits.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unittest/unittest.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/syscalls/object.h> |
| #include <zircon/syscalls/port.h> |
| #include <zircon/syscalls/resource.h> |
| #include <zircon/types.h> |
| |
| __BEGIN_CDECLS; |
| extern zx_handle_t get_root_resource(void); |
| __END_CDECLS; |
| |
| static const size_t mmio_test_size = (PAGE_SIZE * 4); |
| static uint64_t mmio_test_base; |
| |
| const zx::unowned_resource root() { |
| static zx_handle_t root = get_root_resource(); |
| return zx::unowned_resource(root); |
| } |
| |
| // Physical memory is reserved during boot and its location varies based on |
| // system and architecture. What this 'test' does is scan MMIO space looking |
| // for a valid region to test against, ensuring that the only errors it sees |
| // are 'ZX_ERR_NOT_FOUND', which indicates that it is missing from the |
| // region allocator. |
| // |
| // TODO(ZX-2419): Figure out a way to test IRQs in the same manner, without |
| // hardcoding target-specific IRQ vectors in these tests. That information is |
| // stored in the kernel and is not exposed to userspace, so we can't simply |
| // guess/probe valid vectors like we can MMIO and still assume the tests are |
| // valid. |
| |
| static bool probe_address_space(void) { |
| BEGIN_TEST; |
| |
| zx_status_t status; |
| // Scan mmio in chunks until we find a gap that isn't exclusively reserved physical memory. |
| uint64_t step = 0x100000000; |
| for (uint64_t base = 0; base < UINT64_MAX - step; base += step) { |
| zx::resource handle; |
| status = zx::resource::create(*root(), ZX_RSRC_KIND_MMIO, base, |
| mmio_test_size, NULL, 0, &handle); |
| if (status == ZX_OK) { |
| mmio_test_base = base; |
| break; |
| } |
| |
| // If ZX_OK wasn't returned, then we should see ZX_ERR_NOT_FOUND and nothing else. |
| ASSERT_EQ(ZX_ERR_NOT_FOUND, status); |
| } |
| |
| END_TEST; |
| } |
| |
| // This is a basic smoketest for creating resources and verifying the internals |
| // returned by zx_object_get_info match what the caller passed for creation. |
| static bool TestBasicActions(void) { |
| BEGIN_TEST; |
| |
| zx::resource new_root; |
| zx_info_resource_t info; |
| char root_name[] = "root"; |
| |
| // Create a root and verify the fields are still zero, but the name matches. |
| EXPECT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_ROOT, 0, 0, |
| root_name, sizeof(root_name), &new_root), |
| ZX_OK); |
| ASSERT_EQ(new_root.get_info(ZX_INFO_RESOURCE, &info, sizeof(info), NULL, NULL), ZX_OK); |
| EXPECT_EQ(info.kind, ZX_RSRC_KIND_ROOT); |
| EXPECT_EQ(info.base, 0u); |
| EXPECT_EQ(info.size, 0u); |
| EXPECT_EQ(info.flags, 0u); |
| EXPECT_EQ(0, strncmp(root_name, info.name, ZX_MAX_NAME_LEN)); |
| |
| // Check that a resource is created with all the parameters passed to the syscall, and use |
| // the new root resource created for good measure. |
| zx::resource mmio; |
| uint32_t kind = ZX_RSRC_KIND_MMIO; |
| uint32_t flags = ZX_RSRC_FLAG_EXCLUSIVE; |
| char mmio_name[] = "test_resource_name"; |
| ASSERT_EQ(zx::resource::create(new_root, kind | flags, mmio_test_base, mmio_test_size, |
| mmio_name, sizeof(mmio_name), &mmio), |
| ZX_OK); |
| ASSERT_EQ(mmio.get_info(ZX_INFO_RESOURCE, &info, sizeof(info), NULL, NULL), ZX_OK); |
| EXPECT_EQ(info.kind, kind); |
| EXPECT_EQ(info.flags, flags); |
| EXPECT_EQ(info.base, mmio_test_base); |
| EXPECT_EQ(info.size, mmio_test_size); |
| EXPECT_EQ(0, strncmp(info.name, mmio_name, ZX_MAX_NAME_LEN)); |
| |
| END_TEST; |
| } |
| |
| // This test covers every path that returns ZX_ERR_INVALID_ARGS from the the syscall. |
| static bool TestInvalidArgs(void) { |
| BEGIN_TEST; |
| zx::resource temp; |
| zx::resource fail_hnd; |
| // test privilege inversion by seeing if an MMIO resource can other resources. |
| EXPECT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_MMIO, mmio_test_base, |
| mmio_test_size, NULL, 0, &temp), |
| ZX_OK); |
| EXPECT_EQ(zx::resource::create(temp, ZX_RSRC_KIND_ROOT, 0, 0, NULL, 0, &fail_hnd), |
| ZX_ERR_ACCESS_DENIED); |
| EXPECT_EQ(zx::resource::create(temp, ZX_RSRC_KIND_MMIO, mmio_test_base, mmio_test_size, NULL, |
| 0, &fail_hnd), |
| ZX_ERR_ACCESS_DENIED); |
| |
| // test invalid kind |
| EXPECT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_COUNT, mmio_test_base, |
| mmio_test_size, NULL, 0, &temp), |
| ZX_ERR_INVALID_ARGS); |
| EXPECT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_COUNT + 1, |
| mmio_test_base, mmio_test_size, NULL, 0, &temp), |
| ZX_ERR_INVALID_ARGS); |
| |
| // test invalid base |
| EXPECT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_MMIO, UINT64_MAX, 1024, |
| NULL, 0, &temp), |
| ZX_ERR_INVALID_ARGS); |
| // test invalid size |
| EXPECT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_MMIO, 1024, UINT64_MAX, |
| NULL, 0, &temp), |
| ZX_ERR_INVALID_ARGS); |
| // test invalid options |
| EXPECT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_MMIO | 0xFF0000, mmio_test_base, |
| mmio_test_size, NULL, 0, &temp), |
| ZX_ERR_INVALID_ARGS); |
| |
| END_TEST; |
| } |
| |
| static bool TestExclusiveShared(void) { |
| // Try to create a shared resource and ensure it blocks an exclusive |
| // resource. |
| BEGIN_TEST; |
| zx::resource mmio_1, mmio_2; |
| EXPECT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_MMIO | ZX_RSRC_FLAG_EXCLUSIVE, |
| mmio_test_base, mmio_test_size, NULL, 0, &mmio_1), |
| ZX_OK); |
| EXPECT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_MMIO, mmio_test_base, |
| mmio_test_size, NULL, 0, &mmio_2), |
| ZX_ERR_NOT_FOUND); |
| END_TEST; |
| } |
| |
| static bool TestSharedExclusive(void) { |
| // Try to create a shared resource and ensure it blocks an exclusive |
| // resource. |
| BEGIN_TEST; |
| zx::resource mmio_1, mmio_2; |
| EXPECT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_MMIO, mmio_test_base, |
| mmio_test_size, NULL, 0, &mmio_1), |
| ZX_OK); |
| EXPECT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_MMIO | ZX_RSRC_FLAG_EXCLUSIVE, |
| mmio_test_base, mmio_test_size, NULL, 0, &mmio_2), |
| ZX_ERR_NOT_FOUND); |
| END_TEST; |
| } |
| |
| static bool TestVmoCreation(void) { |
| // Attempt to create a resource and then a vmo using that resource. |
| BEGIN_TEST; |
| zx::resource mmio; |
| zx::vmo vmo; |
| ASSERT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_MMIO, mmio_test_base, |
| mmio_test_size, NULL, 0, &mmio), |
| ZX_OK); |
| EXPECT_EQ(zx_vmo_create_physical(mmio.get(), mmio_test_base, PAGE_SIZE, |
| vmo.reset_and_get_address()), |
| ZX_OK); |
| END_TEST; |
| } |
| |
| static bool TestVmoCreationSmaller(void) { |
| // Attempt to create a resource smaller than a page and ensure it still expands access to the |
| // entire page. |
| BEGIN_TEST; |
| zx::resource mmio; |
| zx::vmo vmo; |
| ASSERT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_MMIO, mmio_test_base, |
| PAGE_SIZE/2, NULL, 0, &mmio), |
| ZX_OK); |
| EXPECT_EQ(zx_vmo_create_physical(mmio.get(), mmio_test_base, PAGE_SIZE, |
| vmo.reset_and_get_address()), |
| ZX_OK); |
| END_TEST; |
| } |
| |
| static bool TestVmoCreationUnaligned(void) { |
| // Attempt to create an unaligned resource and ensure that the bounds are rounded appropriately |
| // to the proper PAGE_SIZE. |
| BEGIN_TEST; |
| zx::resource mmio; |
| zx::vmo vmo; |
| ASSERT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_MMIO, |
| mmio_test_base + 0x7800, 0x2000, NULL, 0, &mmio), |
| ZX_OK); |
| EXPECT_EQ(zx_vmo_create_physical(mmio.get(), mmio_test_base + 0x7000, 0x2000, |
| vmo.reset_and_get_address()), |
| ZX_OK); |
| END_TEST; |
| } |
| |
| // Returns zero on failure. |
| static zx_rights_t get_vmo_rights(const zx::vmo& vmo) { |
| zx_info_handle_basic_t info; |
| zx_status_t s = zx_object_get_info(vmo.get(), ZX_INFO_HANDLE_BASIC, &info, |
| sizeof(info), nullptr, nullptr); |
| if (s != ZX_OK) { |
| EXPECT_EQ(s, ZX_OK); // Poison the test |
| return 0; |
| } |
| return info.rights; |
| } |
| |
| static bool TestVmoReplaceAsExecutable() { |
| BEGIN_TEST; |
| |
| zx::resource vmex; |
| zx::vmo vmo, vmo2, vmo3; |
| |
| // allocate an object |
| ASSERT_EQ(ZX_OK, zx_vmo_create(PAGE_SIZE, 0, vmo.reset_and_get_address())); |
| |
| // set-exec with valid VMEX resource |
| ASSERT_EQ(0, zx::resource::create(*root(), ZX_RSRC_KIND_VMEX, 0, 0, NULL, 0, &vmex)); |
| ASSERT_EQ(0, zx_handle_duplicate(vmo.get(), ZX_RIGHT_READ, vmo2.reset_and_get_address())); |
| ASSERT_EQ(0, zx_vmo_replace_as_executable(vmo2.get(), vmex.get(), vmo3.reset_and_get_address())); |
| EXPECT_EQ(ZX_RIGHT_READ|ZX_RIGHT_EXECUTE, get_vmo_rights(vmo3)); |
| |
| // set-exec with ZX_HANDLE_INVALID |
| // TODO(mdempsky): Disallow. |
| ASSERT_EQ(0, zx_handle_duplicate(vmo.get(), ZX_RIGHT_READ, vmo2.reset_and_get_address())); |
| ASSERT_EQ(0, zx_vmo_replace_as_executable(vmo2.get(), ZX_HANDLE_INVALID, vmo3.reset_and_get_address())); |
| EXPECT_EQ(ZX_RIGHT_READ|ZX_RIGHT_EXECUTE, get_vmo_rights(vmo3)); |
| |
| // verify invalid handle fails |
| ASSERT_EQ(0, zx_handle_duplicate(vmo.get(), ZX_RIGHT_READ, vmo2.reset_and_get_address())); |
| EXPECT_EQ(ZX_ERR_WRONG_TYPE, zx_vmo_replace_as_executable(vmo2.get(), vmo.get(), vmo3.reset_and_get_address())); |
| |
| END_TEST; |
| } |
| |
| #if defined(__x86_64__) |
| static bool test_ioports(void) { |
| BEGIN_TEST; |
| // On x86 create an ioport resource and attempt to have the privilege bits |
| // set for the process. |
| zx::resource io; |
| uint16_t io_base = 0xCF8; |
| uint32_t io_size = 8; // CF8 - CFC (inclusive to 4 bytes each) |
| char io_name[] = "ports!"; |
| ASSERT_EQ(zx::resource::create(*root(), ZX_RSRC_KIND_IOPORT, io_base, |
| io_size, io_name, sizeof(io_name), &io), |
| ZX_OK); |
| EXPECT_EQ(zx_ioports_request(io.get(), io_base, io_size), ZX_OK); |
| |
| END_TEST; |
| } |
| #endif |
| |
| BEGIN_TEST_CASE(resource_tests) |
| RUN_TEST(probe_address_space); |
| RUN_TEST(TestBasicActions); |
| RUN_TEST(TestExclusiveShared); |
| RUN_TEST(TestSharedExclusive); |
| RUN_TEST(TestInvalidArgs); |
| RUN_TEST(TestVmoCreation); |
| RUN_TEST(TestVmoCreationSmaller); |
| RUN_TEST(TestVmoCreationUnaligned); |
| RUN_TEST(TestVmoReplaceAsExecutable); |
| #if defined(__x86_64__) |
| RUN_TEST(test_ioports); |
| #endif |
| END_TEST_CASE(resource_tests) |