| // Copyright 2020 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-msi/msi.h> |
| #include <lib/fake-object/object.h> |
| #include <lib/stdcompat/bit.h> |
| #include <lib/zx/interrupt.h> |
| #include <lib/zx/msi.h> |
| #include <lib/zx/result.h> |
| #include <zircon/assert.h> |
| #include <zircon/compiler.h> |
| #include <zircon/errors.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/syscalls/object.h> |
| #include <zircon/types.h> |
| |
| #include <atomic> |
| #include <memory> |
| #include <mutex> |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| #include <fbl/algorithm.h> |
| |
| namespace fake_object { |
| std::atomic_uint64_t Msi::out_of_scope_while_holding_reservations_count_ = 0; |
| std::atomic_bool Msi::ids_in_use_assert_disabled_ = false; |
| |
| // Implements fake-msi's version of |zx_object_get_info|. |
| zx_status_t Msi::get_info(zx_handle_t /*handle*/, uint32_t topic, void* buffer, size_t buffer_size, |
| size_t* /*actual_count*/, size_t* /*avail_count*/) { |
| if (buffer_size != sizeof(zx_info_msi_t) || !buffer || topic != ZX_INFO_MSI) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| std::lock_guard guard(lock_); |
| ClearClosedHandles(); |
| auto* info = static_cast<zx_info_msi_t*>(buffer); |
| info->target_addr = 0xCAFE; |
| info->target_data = 0xC0FE; |
| info->base_irq_id = 1024; |
| info->num_irq = irq_count_; |
| info->interrupt_count = ids_in_use_.size(); |
| return ZX_OK; |
| } |
| |
| } // namespace fake_object |
| |
| // TODO(https://fxbug.dev/42108122): Pull some of these structures out of their parent headers so |
| // that both the tests and the real implementations can use the same information. |
| constexpr size_t MsiCapabilitySize = 24u; |
| |
| // Fake syscall implementations |
| __EXPORT |
| zx_status_t zx_msi_allocate(zx_handle_t, uint32_t count, zx_handle_t* msi_out) { |
| if (!count || !msi_out || !cpp20::has_single_bit(count)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| std::shared_ptr<fake_object::Object> new_msi = std::make_shared<fake_object::Msi>(count); |
| if (auto res = fake_object::FakeHandleTable().Add(new_msi); res.is_ok()) { |
| *msi_out = res.value(); |
| return ZX_OK; |
| } else { |
| return res.status_value(); |
| } |
| } |
| |
| __EXPORT |
| zx_status_t zx_msi_create(zx_handle_t msi_handle, uint32_t options, uint32_t msi_id, |
| zx_handle_t vmo_hnd, size_t cap_offset, zx_handle_t* out) { |
| zx::result get_res = fake_object::FakeHandleTable().Get(msi_handle); |
| if (!get_res.is_ok()) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| |
| if (get_res->type() != ZX_OBJ_TYPE_MSI) { |
| return ZX_ERR_WRONG_TYPE; |
| } |
| std::shared_ptr<fake_object::Msi> msi = |
| std::static_pointer_cast<fake_object::Msi>(get_res.value()); |
| if (msi_id >= msi->irq_count()) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| zx_info_vmo_t vmo_info; |
| zx::unowned_vmo vmo(vmo_hnd); |
| ZX_ASSERT(vmo->get_info(ZX_INFO_VMO, &vmo_info, sizeof(vmo_info), nullptr, nullptr) == ZX_OK); |
| |
| if (cap_offset > vmo_info.size_bytes - MsiCapabilitySize || |
| vmo_info.cache_policy != ZX_CACHE_POLICY_UNCACHED_DEVICE || options & ~ZX_MSI_MODE_MSI_X) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| // After creation here, this handle is only used by the caller. We want no ownership of it, |
| // it's only stored so we can check if it remains unclosed. |
| zx::interrupt interrupt = {}; |
| ZX_ASSERT(zx::interrupt::create(*zx::unowned_resource(ZX_HANDLE_INVALID), 0, ZX_INTERRUPT_VIRTUAL, |
| &interrupt) == ZX_OK); |
| zx_status_t status = msi->ReserveId(interrupt.borrow(), msi_id); |
| if (status == ZX_OK) { |
| *out = interrupt.release(); |
| } |
| |
| return status; |
| } |