blob: 58f0e1a380584bf3e7c270ecebe94d25c0608f6f [file] [log] [blame]
// 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-bti/bti.h>
#include <lib/zx/event.h>
#include <lib/zx/vmo.h>
#include <zircon/rights.h>
#include <zircon/syscalls.h>
#include <climits> // PAGE_SIZE
#include <utility>
#include <vector>
#include <fbl/ref_ptr.h>
#include <zxtest/zxtest.h>
namespace {
static constexpr size_t kVmoTestSize = 512 << 10; // 512KB
static constexpr uint32_t kPageCount = kVmoTestSize / PAGE_SIZE;
TEST(FakeBti, CreateFakeBti) {
zx_handle_t bti = ZX_HANDLE_INVALID;
EXPECT_OK(fake_bti_create(&bti));
EXPECT_NE(bti, ZX_HANDLE_INVALID);
ASSERT_NO_DEATH(([bti]() { zx_handle_close(bti); }));
}
TEST(FakeBti, PinVmo) {
zx_handle_t bti = ZX_HANDLE_INVALID;
EXPECT_OK(fake_bti_create(&bti));
EXPECT_NE(bti, ZX_HANDLE_INVALID);
zx_handle_t vmo_handle, pmt_handle;
EXPECT_OK(zx_vmo_create(kVmoTestSize, 0, &vmo_handle));
// Create an address array with one extra entry and mark it with a sentinel value.
zx_paddr_t addrs[kPageCount + 1];
addrs[kPageCount] = 42;
// Now actually pin the region
EXPECT_OK(zx_bti_pin(bti, 0, vmo_handle, 0, kVmoTestSize, addrs, kPageCount, &pmt_handle));
EXPECT_NE(pmt_handle, ZX_HANDLE_INVALID);
// Check that the addresses returned are correct, including that the sentinel value wasn't
// touched.
for (size_t i = 0; i != kPageCount; ++i) {
EXPECT_EQ(addrs[i], FAKE_BTI_PHYS_ADDR);
}
EXPECT_EQ(addrs[kPageCount], 42);
ASSERT_NO_DEATH(([pmt_handle]() { EXPECT_OK(zx_pmt_unpin(pmt_handle)); }));
ASSERT_NO_DEATH(([bti]() { zx_handle_close(bti); }));
}
TEST(FakeBti, GetPinnedVmos) {
zx_handle_t bti = ZX_HANDLE_INVALID;
EXPECT_OK(fake_bti_create(&bti));
EXPECT_NE(bti, ZX_HANDLE_INVALID);
zx_handle_t vmo_handle, pmt_handle;
EXPECT_OK(zx_vmo_create(kVmoTestSize, 0, &vmo_handle));
zx_paddr_t addrs[kPageCount];
// Now actually pin the region
EXPECT_OK(zx_bti_pin(bti, 0, vmo_handle, 0, kVmoTestSize, addrs, kPageCount, &pmt_handle));
EXPECT_NE(pmt_handle, ZX_HANDLE_INVALID);
// Get VMO handle
uint64_t num_pinned_vmos = 0u;
std::vector<fake_bti_pinned_vmo_info_t> pinned_vmo_info;
EXPECT_OK(fake_bti_get_pinned_vmos(bti, nullptr, 0u, &num_pinned_vmos));
EXPECT_EQ(num_pinned_vmos, 1u);
pinned_vmo_info.resize(num_pinned_vmos);
EXPECT_OK(
fake_bti_get_pinned_vmos(bti, pinned_vmo_info.data(), num_pinned_vmos, &num_pinned_vmos));
uint64_t size1 = 0u, size2 = 0u;
zx_vmo_get_size(pinned_vmo_info[0].vmo, &size1);
zx_vmo_get_size(vmo_handle, &size2);
EXPECT_NE(size1, 0u);
EXPECT_NE(size2, 0u);
EXPECT_EQ(size1, size2);
EXPECT_EQ(pinned_vmo_info[0].size, size1);
EXPECT_EQ(pinned_vmo_info[0].offset, 0u);
// Close the returned VMO handle.
EXPECT_EQ(zx_handle_close(pinned_vmo_info[0].vmo), ZX_OK);
// Unpin all the PMT handles.
ASSERT_NO_DEATH(([pmt_handle]() { EXPECT_OK(zx_pmt_unpin(pmt_handle)); }));
EXPECT_OK(fake_bti_get_pinned_vmos(bti, nullptr, 0u, &num_pinned_vmos));
EXPECT_EQ(num_pinned_vmos, 0u);
ASSERT_NO_DEATH(([bti]() { zx_handle_close(bti); }));
}
TEST(FakeBti, GetPinnedVmosWithOffset) {
zx_handle_t bti = ZX_HANDLE_INVALID;
EXPECT_OK(fake_bti_create(&bti));
EXPECT_NE(bti, ZX_HANDLE_INVALID);
zx_handle_t vmo_handle, pmt_handle;
EXPECT_OK(zx_vmo_create(kVmoTestSize, 0, &vmo_handle));
zx_paddr_t addrs[kPageCount];
// Now actually pin the region (with offset)
EXPECT_OK(zx_bti_pin(bti, 0, vmo_handle, /*offset=*/PAGE_SIZE, kVmoTestSize - PAGE_SIZE, addrs,
kPageCount - 1u, &pmt_handle));
EXPECT_NE(pmt_handle, ZX_HANDLE_INVALID);
// Get VMO handle
uint64_t num_pinned_vmos = 0u;
std::vector<fake_bti_pinned_vmo_info_t> pinned_vmo_info;
EXPECT_OK(fake_bti_get_pinned_vmos(bti, nullptr, 0u, &num_pinned_vmos));
EXPECT_EQ(num_pinned_vmos, 1u);
pinned_vmo_info.resize(num_pinned_vmos);
EXPECT_OK(fake_bti_get_pinned_vmos(bti, pinned_vmo_info.data(), num_pinned_vmos, nullptr));
uint64_t size = 0u;
zx_vmo_get_size(pinned_vmo_info[0].vmo, &size);
EXPECT_EQ(size, kVmoTestSize);
EXPECT_EQ(pinned_vmo_info[0].size, kVmoTestSize - PAGE_SIZE);
EXPECT_EQ(pinned_vmo_info[0].offset, PAGE_SIZE);
// Try writing to the duplicated VMO and read it back from pinned VMO.
zx_vmo_op_range(pinned_vmo_info[0].vmo, ZX_VMO_OP_COMMIT, /*offset=*/PAGE_SIZE,
/*size=*/PAGE_SIZE, nullptr, 0);
uint8_t val = 42;
uint8_t read_val = 0u;
EXPECT_EQ(zx_vmo_write(pinned_vmo_info[0].vmo, &val, /*offset=*/PAGE_SIZE, sizeof(val)), ZX_OK);
EXPECT_EQ(zx_vmo_read(vmo_handle, &read_val, /*offset=*/PAGE_SIZE, sizeof(read_val)), ZX_OK);
EXPECT_EQ(val, read_val);
// Close the returned VMO handle.
EXPECT_EQ(zx_handle_close(pinned_vmo_info[0].vmo), ZX_OK);
// Unpin all the PMT handles.
ASSERT_NO_DEATH(([pmt_handle]() { EXPECT_OK(zx_pmt_unpin(pmt_handle)); }));
EXPECT_OK(fake_bti_get_pinned_vmos(bti, nullptr, 0u, &num_pinned_vmos));
EXPECT_EQ(num_pinned_vmos, 0u);
ASSERT_NO_DEATH(([bti]() { zx_handle_close(bti); }));
}
TEST(FakeBti, GetMultiplePinnedVmos) {
zx_handle_t bti = ZX_HANDLE_INVALID;
EXPECT_OK(fake_bti_create(&bti));
EXPECT_NE(bti, ZX_HANDLE_INVALID);
zx_handle_t vmo_handle, pmt_handle;
zx_handle_t vmo2_handle, pmt2_handle;
EXPECT_OK(zx_vmo_create(kVmoTestSize, 0, &vmo_handle));
EXPECT_OK(zx_vmo_create(kVmoTestSize, 0, &vmo2_handle));
zx_paddr_t addrs[kPageCount];
// Pin the first VMO.
EXPECT_OK(zx_bti_pin(bti, 0, vmo_handle, 0, kVmoTestSize, addrs, kPageCount, &pmt_handle));
EXPECT_NE(pmt_handle, ZX_HANDLE_INVALID);
// Pin the second VMO region with a non-zero offset.
EXPECT_OK(zx_bti_pin(bti, 0, vmo2_handle, /*offset=*/PAGE_SIZE, kVmoTestSize - PAGE_SIZE, addrs,
kPageCount - 1u, &pmt2_handle));
EXPECT_NE(pmt2_handle, ZX_HANDLE_INVALID);
// Get VMO handles
uint64_t num_pinned_vmos = 0u;
std::vector<fake_bti_pinned_vmo_info_t> pinned_vmo_info;
EXPECT_OK(fake_bti_get_pinned_vmos(bti, nullptr, 0u, &num_pinned_vmos));
EXPECT_EQ(num_pinned_vmos, 2u);
pinned_vmo_info.resize(num_pinned_vmos);
EXPECT_OK(fake_bti_get_pinned_vmos(bti, pinned_vmo_info.data(), num_pinned_vmos, nullptr));
uint64_t size;
zx_vmo_get_size(pinned_vmo_info[0].vmo, &size);
EXPECT_EQ(size, kVmoTestSize);
EXPECT_EQ(pinned_vmo_info[0].size, kVmoTestSize);
EXPECT_EQ(pinned_vmo_info[0].offset, 0);
zx_vmo_get_size(pinned_vmo_info[1].vmo, &size);
EXPECT_EQ(size, kVmoTestSize);
EXPECT_EQ(pinned_vmo_info[1].size, kVmoTestSize - PAGE_SIZE);
EXPECT_EQ(pinned_vmo_info[1].offset, PAGE_SIZE);
// Close the returned VMO handles.
EXPECT_EQ(zx_handle_close(pinned_vmo_info[0].vmo), ZX_OK);
EXPECT_EQ(zx_handle_close(pinned_vmo_info[1].vmo), ZX_OK);
// Unpin all the PMT handles.
ASSERT_NO_DEATH(([pmt_handle]() { EXPECT_OK(zx_pmt_unpin(pmt_handle)); }));
EXPECT_OK(fake_bti_get_pinned_vmos(bti, nullptr, 0u, &num_pinned_vmos));
EXPECT_EQ(num_pinned_vmos, 1u);
ASSERT_NO_DEATH(([pmt2_handle]() { EXPECT_OK(zx_pmt_unpin(pmt2_handle)); }));
EXPECT_OK(fake_bti_get_pinned_vmos(bti, nullptr, 0u, &num_pinned_vmos));
EXPECT_EQ(num_pinned_vmos, 0u);
ASSERT_NO_DEATH(([bti]() { zx_handle_close(bti); }));
}
TEST(FakeBti, PinVmoWithPaddrGenerator) {
zx_paddr_t expected_addrs[kPageCount + 1];
for (size_t i = 0; i < countof(expected_addrs); i++) {
expected_addrs[i] = FAKE_BTI_PHYS_ADDR * (i + 1);
}
zx_handle_t bti = ZX_HANDLE_INVALID;
EXPECT_OK(fake_bti_create_with_paddrs(expected_addrs, countof(expected_addrs), &bti));
EXPECT_NE(bti, ZX_HANDLE_INVALID);
zx_handle_t vmo_handle, pmt_handle;
EXPECT_OK(zx_vmo_create(kVmoTestSize, 0, &vmo_handle));
// Create an address array with one extra entry and mark it with a sentinel value.
zx_paddr_t addrs[kPageCount + 1];
addrs[kPageCount] = 42;
// Now actually pin the region
EXPECT_OK(zx_bti_pin(bti, 0, vmo_handle, 0, kVmoTestSize, addrs, kPageCount, &pmt_handle));
EXPECT_NE(pmt_handle, ZX_HANDLE_INVALID);
// Check that the addresses returned are correct, including that the sentinel value wasn't
// touched.
for (size_t i = 0; i != kPageCount; ++i) {
EXPECT_EQ(addrs[i], FAKE_BTI_PHYS_ADDR * (i + 1));
}
EXPECT_EQ(addrs[kPageCount], 42);
ASSERT_NO_DEATH(([pmt_handle]() { EXPECT_OK(zx_pmt_unpin(pmt_handle)); }));
ASSERT_NO_DEATH(([bti]() { zx_handle_close(bti); }));
}
TEST(FakeBti, CreateContiguousVmo) {
zx_handle_t bti = ZX_HANDLE_INVALID;
EXPECT_OK(fake_bti_create(&bti));
EXPECT_NE(bti, ZX_HANDLE_INVALID);
zx_handle_t vmo_handle, pmt_handle;
EXPECT_OK(zx_vmo_create_contiguous(bti, kVmoTestSize, 0, &vmo_handle));
EXPECT_NE(vmo_handle, ZX_HANDLE_INVALID);
zx_paddr_t addr;
EXPECT_OK(zx_bti_pin(bti, ZX_BTI_CONTIGUOUS, vmo_handle, 0, kVmoTestSize, &addr, 1, &pmt_handle));
EXPECT_NE(pmt_handle, ZX_HANDLE_INVALID);
EXPECT_EQ(addr, FAKE_BTI_PHYS_ADDR);
ASSERT_NO_DEATH(([pmt_handle]() { EXPECT_OK(zx_pmt_unpin(pmt_handle)); }));
ASSERT_NO_DEATH(([bti]() { zx_handle_close(bti); }));
}
TEST(FakeBti, PmoCount) {
zx_handle_t bti = ZX_HANDLE_INVALID;
EXPECT_OK(fake_bti_create(&bti));
EXPECT_NE(bti, ZX_HANDLE_INVALID);
zx_handle_t vmo_handle, pmt_handle;
EXPECT_OK(zx_vmo_create_contiguous(bti, kVmoTestSize, 0, &vmo_handle));
EXPECT_NE(vmo_handle, ZX_HANDLE_INVALID);
zx_paddr_t addr;
EXPECT_OK(zx_bti_pin(bti, ZX_BTI_CONTIGUOUS, vmo_handle, 0, kVmoTestSize, &addr, 1, &pmt_handle));
EXPECT_NE(pmt_handle, ZX_HANDLE_INVALID);
EXPECT_EQ(addr, FAKE_BTI_PHYS_ADDR);
size_t actual = 0, avail = 0;
zx_info_bti_t bti_info;
EXPECT_OK(zx_object_get_info(bti, ZX_INFO_BTI, &bti_info, sizeof(bti_info), &actual, &avail));
// After pinning, pmo_count should be 1.
EXPECT_EQ(1, bti_info.pmo_count);
EXPECT_OK(zx_pmt_unpin(pmt_handle));
// After unpinning, pmo_count should be zero.
EXPECT_OK(zx_object_get_info(bti, ZX_INFO_BTI, &bti_info, sizeof(bti_info), &actual, &avail));
EXPECT_EQ(0, bti_info.pmo_count);
}
// TODO(fxbug.dev/32963): when functionality is available, check that pinning a
// vmo with the ZX_BTI_CONTIGUOUS flag fails if the vmo was not created with
// zx_vmo_create_contiguous.
} // namespace