blob: b207df7b465617d801eb890f9c3fd9f259a15e29 [file] [log] [blame]
// 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 <hypervisor/trap_map.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include <hypervisor/ktrace.h>
#include <lib/ktrace.h>
#include <zircon/syscalls/hypervisor.h>
#include <zircon/types.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(nullptr, this);
}
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) {
(void) key_;
}
Trap::~Trap() {
if (port_ == nullptr) {
return;
}
port_->CancelQueued(nullptr /* 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, 0);
if (status != ZX_OK) {
port_allocator_.Free(port_packet);
}
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