| // 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 <utility> |
| |
| #include <fbl/auto_lock.h> |
| #include <fbl/mutex.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/ref_ptr.h> |
| #include <fbl/vector.h> |
| #include <lib/zx/vmo.h> |
| #include <zircon/assert.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/thread_annotations.h> |
| |
| namespace { |
| |
| enum class HandleType { |
| BTI, |
| PMT, |
| }; |
| |
| class Object : public fbl::RefCounted<Object> { |
| public: |
| virtual ~Object() = default; |
| virtual HandleType type() const = 0; |
| }; |
| |
| 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; |
| } |
| |
| 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_; |
| }; |
| |
| // Thread-safe implementation of a handle table for the fake BTI/PMT handles |
| class HandleTable { |
| public: |
| HandleTable() = default; |
| ~HandleTable() = default; |
| |
| HandleTable(const HandleTable&) = delete; |
| HandleTable& operator=(const HandleTable&) = delete; |
| HandleTable(HandleTable&&) = delete; |
| HandleTable& operator=(HandleTable&&) = delete; |
| |
| // Handle values are always odd, so we can use even numbers to identify |
| // fake BTI and PMT objects. |
| // TODO(ZX-3131): This guarantee should be documented or we should change |
| // this code to do something else. |
| static bool IsValidFakeHandle(zx_handle_t handle) { |
| return (handle & 1) == 0; |
| } |
| |
| zx_status_t Get(zx_handle_t handle, fbl::RefPtr<Object>* out) { |
| fbl::AutoLock guard(&lock_); |
| |
| size_t idx = HandleToIndex(handle); |
| if (idx >= handles_.size()) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| const fbl::RefPtr<Object>& h = handles_[idx]; |
| if (!h) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| *out = h; |
| return ZX_OK; |
| } |
| |
| zx_status_t Remove(zx_handle_t handle) { |
| fbl::AutoLock guard(&lock_); |
| |
| size_t idx = HandleToIndex(handle); |
| if (idx >= handles_.size()) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| fbl::RefPtr<Object>* h = &handles_[idx]; |
| if (!*h) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| h->reset(); |
| return ZX_OK; |
| } |
| |
| zx_status_t Add(fbl::RefPtr<Object> obj, zx_handle_t* out) { |
| fbl::AutoLock guard(&lock_); |
| |
| for (size_t i = 0; i < handles_.size(); ++i) { |
| if (!handles_[i]) { |
| handles_[i] = std::move(obj); |
| *out = IndexToHandle(i); |
| return ZX_OK; |
| } |
| } |
| |
| handles_.push_back(std::move(obj)); |
| *out = IndexToHandle(handles_.size() - 1); |
| return ZX_OK; |
| } |
| private: |
| static size_t HandleToIndex(zx_handle_t handle) { |
| ZX_ASSERT(IsValidFakeHandle(handle)); |
| return handle >> 1; |
| } |
| |
| static zx_handle_t IndexToHandle(size_t idx) { |
| return static_cast<zx_handle_t>(idx << 1); |
| } |
| |
| fbl::Mutex lock_; |
| fbl::Vector<fbl::RefPtr<Object>> handles_ TA_GUARDED(lock_); |
| }; |
| |
| HandleTable gHandleTable; |
| |
| } // namespace |
| |
| // Fake BTI API |
| |
| 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 gHandleTable.Add(std::move(new_bti), out); |
| } |
| |
| void fake_bti_destroy(zx_handle_t h) { |
| fbl::RefPtr<Object> obj; |
| zx_status_t status = gHandleTable.Get(h, &obj); |
| ZX_ASSERT_MSG(status == ZX_OK, "fake_bti_destroy: Failed to find handle %u\n", h); |
| ZX_ASSERT_MSG(obj->type() == HandleType::BTI, "fake_bti_destroy: Wrong handle type: %u\n", |
| static_cast<uint32_t>(obj->type())); |
| status = gHandleTable.Remove(h); |
| ZX_ASSERT_MSG(status == ZX_OK, "fake_bti_destroy: Failed to destroy handle %u: %s\n", h, |
| zx_status_get_string(status)); |
| } |
| |
| // Fake syscall implementations |
| |
| 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 = gHandleTable.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 zeroes |
| memset(addrs, 0, sizeof(addrs[0]) * addrs_count); |
| |
| fbl::RefPtr<Object> new_pmt; |
| status = Pmt::Create(std::move(vmo_clone), offset, size, &new_pmt); |
| if (status != ZX_OK) { |
| return status; |
| } |
| return gHandleTable.Add(std::move(new_pmt), out); |
| } |
| |
| zx_status_t zx_bti_release_quarantine(zx_handle_t handle) { |
| fbl::RefPtr<Object> obj; |
| zx_status_t status = gHandleTable.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; |
| } |
| |
| zx_status_t zx_pmt_unpin(zx_handle_t handle) { |
| fbl::RefPtr<Object> obj; |
| zx_status_t status = gHandleTable.Get(handle, &obj); |
| ZX_ASSERT_MSG(status == ZX_OK && obj->type() == HandleType::PMT, |
| "fake pmt_unpin: Bad handle %u\n", handle); |
| status = gHandleTable.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; |
| } |
| |
| zx_status_t zx_object_get_info(zx_handle_t handle, uint32_t topic, void* buffer, |
| size_t buffer_size, size_t* actual_count, size_t* avail_count) { |
| if (!HandleTable::IsValidFakeHandle(handle)) { |
| return _zx_object_get_info(handle, topic, buffer, buffer_size, actual_count, avail_count); |
| } |
| |
| fbl::RefPtr<Object> obj; |
| zx_status_t status = gHandleTable.Get(handle, &obj); |
| ZX_ASSERT_MSG(status == ZX_OK, "fake object_get_info: Bad 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, |
| }; |
| 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); |
| } |