| // Copyright 2017 The Fuchsia Authors | |
| // | |
| // Use of this source code is governed by a MIT-style | |
| // license that can be found in the LICENSE file or at | |
| // https://opensource.org/licenses/MIT | |
| #include <lib/ktrace.h> | |
| #include <zircon/errors.h> | |
| #include <zircon/syscalls/hypervisor.h> | |
| #include <zircon/types.h> | |
| #include <fbl/alloc_checker.h> | |
| #include <fbl/auto_lock.h> | |
| #include <hypervisor/ktrace.h> | |
| #include <hypervisor/trap_map.h> | |
| static constexpr size_t kMaxPacketsPerRange = 256; | |
| namespace hypervisor { | |
| BlockingPortAllocator::BlockingPortAllocator() : semaphore_(kMaxPacketsPerRange) {} | |
| zx_status_t BlockingPortAllocator::Init() { | |
| return arena_.Init("hypervisor-packets", kMaxPacketsPerRange); | |
| } | |
| PortPacket* BlockingPortAllocator::AllocBlocking() { | |
| ktrace_vcpu(TAG_VCPU_BLOCK, VCPU_PORT); | |
| zx_status_t status = semaphore_.Wait(Deadline::infinite()); | |
| ktrace_vcpu(TAG_VCPU_UNBLOCK, VCPU_PORT); | |
| if (status != ZX_OK) { | |
| return nullptr; | |
| } | |
| return Alloc(); | |
| } | |
| PortPacket* BlockingPortAllocator::Alloc() { | |
| return arena_.New(this /* handle */, this /* allocator */); | |
| } | |
| void BlockingPortAllocator::Free(PortPacket* port_packet) { | |
| arena_.Delete(port_packet); | |
| semaphore_.Post(); | |
| } | |
| Trap::Trap(uint32_t kind, zx_gpaddr_t addr, size_t len, fbl::RefPtr<PortDispatcher> port, | |
| uint64_t key) | |
| : kind_(kind), addr_(addr), len_(len), port_(ktl::move(port)), key_(key) {} | |
| Trap::~Trap() { | |
| if (port_ == nullptr) { | |
| return; | |
| } | |
| port_->CancelQueued(&port_allocator_ /* handle */, key_); | |
| } | |
| zx_status_t Trap::Init() { return port_allocator_.Init(); } | |
| zx_status_t Trap::Queue(const zx_port_packet_t& packet, StateInvalidator* invalidator) { | |
| if (invalidator != nullptr) { | |
| invalidator->Invalidate(); | |
| } | |
| if (port_ == nullptr) { | |
| return ZX_ERR_NOT_FOUND; | |
| } | |
| PortPacket* port_packet = port_allocator_.AllocBlocking(); | |
| if (port_packet == nullptr) { | |
| return ZX_ERR_NO_MEMORY; | |
| } | |
| port_packet->packet = packet; | |
| zx_status_t status = port_->Queue(port_packet, ZX_SIGNAL_NONE); | |
| if (status != ZX_OK) { | |
| port_allocator_.Free(port_packet); | |
| if (status == ZX_ERR_BAD_HANDLE) { | |
| // If the last handle to the port has been closed, then we're in a bad state. | |
| status = ZX_ERR_BAD_STATE; | |
| } | |
| } | |
| return status; | |
| } | |
| zx_status_t TrapMap::InsertTrap(uint32_t kind, zx_gpaddr_t addr, size_t len, | |
| fbl::RefPtr<PortDispatcher> port, uint64_t key) { | |
| TrapTree* traps = TreeOf(kind); | |
| if (traps == nullptr) { | |
| return ZX_ERR_INVALID_ARGS; | |
| } | |
| auto iter = traps->find(addr); | |
| if (iter.IsValid()) { | |
| dprintf(INFO, | |
| "Trap for kind %u (addr %#lx len %lu key %lu) already exists " | |
| "(addr %#lx len %lu key %lu)\n", | |
| kind, addr, len, key, iter->addr(), iter->len(), iter->key()); | |
| return ZX_ERR_ALREADY_EXISTS; | |
| } | |
| fbl::AllocChecker ac; | |
| ktl::unique_ptr<Trap> range(new (&ac) Trap(kind, addr, len, ktl::move(port), key)); | |
| if (!ac.check()) { | |
| return ZX_ERR_NO_MEMORY; | |
| } | |
| zx_status_t status = range->Init(); | |
| if (status != ZX_OK) { | |
| return status; | |
| } | |
| { | |
| Guard<SpinLock, IrqSave> guard{&lock_}; | |
| traps->insert(ktl::move(range)); | |
| } | |
| return ZX_OK; | |
| } | |
| zx_status_t TrapMap::FindTrap(uint32_t kind, zx_gpaddr_t addr, Trap** trap) { | |
| TrapTree* traps = TreeOf(kind); | |
| if (traps == nullptr) { | |
| return ZX_ERR_INVALID_ARGS; | |
| } | |
| TrapTree::iterator iter; | |
| { | |
| Guard<SpinLock, IrqSave> guard{&lock_}; | |
| iter = traps->upper_bound(addr); | |
| } | |
| --iter; | |
| if (!iter.IsValid() || !iter->Contains(addr)) { | |
| return ZX_ERR_NOT_FOUND; | |
| } | |
| *trap = const_cast<Trap*>(&*iter); | |
| return ZX_OK; | |
| } | |
| TrapMap::TrapTree* TrapMap::TreeOf(uint32_t kind) { | |
| switch (kind) { | |
| case ZX_GUEST_TRAP_BELL: | |
| case ZX_GUEST_TRAP_MEM: | |
| return &mem_traps_; | |
| #ifdef ARCH_X86 | |
| case ZX_GUEST_TRAP_IO: | |
| return &io_traps_; | |
| #endif // ARCH_X86 | |
| default: | |
| return nullptr; | |
| } | |
| } | |
| } // namespace hypervisor |