blob: f5c9a6ee62664b1f4275204b2ff5f74ea505827c [file] [log] [blame]
// Copyright 2018 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/fake-object/object.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <lib/zx/vmo.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <utility>
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include <fbl/vector.h>
// Normally just defined in the kernel:
#define PAGE_SIZE_SHIFT 12
namespace {
class Bti final : public Object {
public:
virtual ~Bti() = default;
static zx_status_t Create(fbl::RefPtr<Object>* out) {
*out = fbl::AdoptRef(new Bti());
return ZX_OK;
}
zx_status_t get_info(zx_handle_t handle, uint32_t topic, void* buffer, size_t buffer_size,
size_t* actual_count, size_t* avail_count) override;
HandleType type() const final { return HandleType::BTI; }
private:
Bti() = default;
};
class Pmt final : public Object {
public:
virtual ~Pmt() = default;
static zx_status_t Create(zx::vmo vmo, uint64_t offset, uint64_t size, fbl::RefPtr<Object>* out) {
fbl::RefPtr<Pmt> pmt(fbl::AdoptRef(new Pmt(std::move(vmo), offset, size)));
// These lines exist because currently offset_ and size_ are unused, and
// GCC and Clang disagree about whether or not marking them as unused is acceptable.
(void)pmt->offset_;
(void)pmt->size_;
*out = std::move(pmt);
return ZX_OK;
}
HandleType type() const final { return HandleType::PMT; }
private:
Pmt(zx::vmo vmo, uint64_t offset, uint64_t size)
: vmo_(std::move(vmo)), offset_(offset), size_(size) {}
zx::vmo vmo_;
uint64_t offset_;
uint64_t size_;
};
} // namespace
// Fake BTI API
// Implements fake-bti's version of |zx_object_get_info|.
zx_status_t Bti::get_info(zx_handle_t handle, uint32_t topic, void* buffer, size_t buffer_size,
size_t* actual_count, size_t* avail_count) {
fbl::RefPtr<Object> obj;
zx_status_t status = FakeHandleTable().Get(handle, &obj);
ZX_ASSERT_MSG(status == ZX_OK, "fake_bti_get_info: Failed to find handle %u\n", handle);
if (obj->type() == HandleType::BTI) {
switch (topic) {
case ZX_INFO_BTI: {
if (avail_count) {
*avail_count = 1;
}
if (actual_count) {
*actual_count = 0;
}
if (buffer_size < sizeof(zx_info_bti_t)) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
zx_info_bti_t info = {
.minimum_contiguity = ZX_PAGE_SIZE,
.aspace_size = UINT64_MAX,
.pmo_count = 0,
.quarantine_count = 0,
};
memcpy(buffer, &info, sizeof(info));
if (actual_count) {
*actual_count = 1;
}
return ZX_OK;
}
default:
ZX_ASSERT_MSG(false, "fake object_get_info: Unsupported BTI topic %u\n", topic);
}
}
ZX_ASSERT_MSG(false, "fake object_get_info: Unsupported PMT topic %u\n", topic);
}
__EXPORT
zx_status_t fake_bti_create(zx_handle_t* out) {
fbl::RefPtr<Object> new_bti;
zx_status_t status = Bti::Create(&new_bti);
if (status != ZX_OK) {
return status;
}
return FakeHandleTable().Add(std::move(new_bti), out);
}
// Fake syscall implementations
__EXPORT
zx_status_t zx_bti_pin(zx_handle_t bti_handle, uint32_t options, zx_handle_t vmo, uint64_t offset,
uint64_t size, zx_paddr_t* addrs, size_t addrs_count, zx_handle_t* out) {
fbl::RefPtr<Object> bti_obj;
zx_status_t status = FakeHandleTable().Get(bti_handle, &bti_obj);
ZX_ASSERT_MSG(status == ZX_OK && bti_obj->type() == HandleType::BTI,
"fake bti_pin: Bad handle %u\n", bti_handle);
fbl::RefPtr<Bti> bti(static_cast<Bti*>(bti_obj.get()));
zx::vmo vmo_clone;
status = zx::unowned_vmo(vmo)->duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_clone);
if (status != ZX_OK) {
return status;
}
zx_info_handle_basic_t handle_info;
status =
vmo_clone.get_info(ZX_INFO_HANDLE_BASIC, &handle_info, sizeof(handle_info), nullptr, nullptr);
ZX_ASSERT_MSG(status == ZX_OK, "fake bti_pin: Failed to get VMO info\n");
const zx_rights_t vmo_rights = handle_info.rights;
if (!(vmo_rights & ZX_RIGHT_MAP)) {
return ZX_ERR_ACCESS_DENIED;
}
// Check argument validity
if (offset % ZX_PAGE_SIZE != 0) {
return ZX_ERR_INVALID_ARGS;
}
if (size % ZX_PAGE_SIZE != 0) {
return ZX_ERR_INVALID_ARGS;
}
// Validate options
bool compress_results = false;
bool contiguous = false;
if (options & ZX_BTI_PERM_READ) {
if (!(vmo_rights & ZX_RIGHT_READ)) {
return ZX_ERR_ACCESS_DENIED;
}
options &= ~ZX_BTI_PERM_READ;
}
if (options & ZX_BTI_PERM_WRITE) {
if (!(vmo_rights & ZX_RIGHT_WRITE)) {
return ZX_ERR_ACCESS_DENIED;
}
options &= ~ZX_BTI_PERM_WRITE;
}
if (options & ZX_BTI_PERM_EXECUTE) {
// Note: We check ZX_RIGHT_READ instead of ZX_RIGHT_EXECUTE
// here because the latter applies to execute permission of
// the host CPU, whereas ZX_BTI_PERM_EXECUTE applies to
// transactions initiated by the bus device.
if (!(vmo_rights & ZX_RIGHT_READ)) {
return ZX_ERR_ACCESS_DENIED;
}
options &= ~ZX_BTI_PERM_EXECUTE;
}
if (!((options & ZX_BTI_COMPRESS) && (options & ZX_BTI_CONTIGUOUS))) {
if (options & ZX_BTI_COMPRESS) {
compress_results = true;
options &= ~ZX_BTI_COMPRESS;
}
if (options & ZX_BTI_CONTIGUOUS) {
contiguous = true;
options &= ~ZX_BTI_CONTIGUOUS;
}
}
if (options) {
return ZX_ERR_INVALID_ARGS;
}
if (compress_results || !contiguous) {
if (addrs_count != size / ZX_PAGE_SIZE) {
return ZX_ERR_INVALID_ARGS;
}
} else {
if (addrs_count != 1) {
return ZX_ERR_INVALID_ARGS;
}
}
// Fill |addrs| with the fake physical address.
for (size_t i = 0; i != addrs_count; ++i) {
addrs[i] = FAKE_BTI_PHYS_ADDR;
}
fbl::RefPtr<Object> new_pmt;
status = Pmt::Create(std::move(vmo_clone), offset, size, &new_pmt);
if (status != ZX_OK) {
return status;
}
return FakeHandleTable().Add(std::move(new_pmt), out);
}
__EXPORT
zx_status_t zx_bti_release_quarantine(zx_handle_t handle) {
fbl::RefPtr<Object> obj;
zx_status_t status = FakeHandleTable().Get(handle, &obj);
ZX_ASSERT_MSG(status == ZX_OK && obj->type() == HandleType::BTI,
"fake bti_release_quarantine: Bad handle %u\n", handle);
return ZX_OK;
}
__EXPORT
zx_status_t zx_pmt_unpin(zx_handle_t handle) {
fbl::RefPtr<Object> obj;
zx_status_t status = FakeHandleTable().Get(handle, &obj);
ZX_ASSERT_MSG(status == ZX_OK && obj->type() == HandleType::PMT,
"fake pmt_unpin: Bad handle %u\n", handle);
status = FakeHandleTable().Remove(handle);
ZX_ASSERT_MSG(status == ZX_OK, "fake pmt_unpin: Failed to remove handle %u: %s\n", handle,
zx_status_get_string(status));
return ZX_OK;
}
// A fake version of zx_vmo_create_contiguous. This version just creates a normal vmo.
__EXPORT
zx_status_t zx_vmo_create_contiguous(zx_handle_t bti_handle, size_t size, uint32_t alignment_log2,
zx_handle_t* out) {
if (size == 0) {
return ZX_ERR_INVALID_ARGS;
}
if (alignment_log2 == 0) {
alignment_log2 = PAGE_SIZE_SHIFT;
}
// catch obviously wrong values
if (alignment_log2 < PAGE_SIZE_SHIFT || alignment_log2 >= (8 * sizeof(uint64_t))) {
return ZX_ERR_INVALID_ARGS;
}
// Make sure this is a valid fake bti:
fbl::RefPtr<Object> bti_obj;
zx_status_t status = FakeHandleTable().Get(bti_handle, &bti_obj);
ZX_ASSERT_MSG(status == ZX_OK && bti_obj->type() == HandleType::BTI,
"fake bti_pin: Bad handle %u\n", bti_handle);
// For this fake implementation, just create a normal vmo:
return zx_vmo_create(size, 0, out);
}