1// Copyright 2017 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#include <hypervisor/trap_map.h>
8
9#include <fbl/alloc_checker.h>
10#include <fbl/auto_lock.h>
11#include <hypervisor/ktrace.h>
12#include <lib/ktrace.h>
13#include <zircon/syscalls/hypervisor.h>
14#include <zircon/types.h>
15
16static constexpr size_t kMaxPacketsPerRange = 256;
17
18namespace hypervisor {
19
20BlockingPortAllocator::BlockingPortAllocator() : semaphore_(kMaxPacketsPerRange) {}
21
22zx_status_t BlockingPortAllocator::Init() {
23    return arena_.Init("hypervisor-packets", kMaxPacketsPerRange);
24}
25
26PortPacket* BlockingPortAllocator::AllocBlocking() {
27    ktrace_vcpu(TAG_VCPU_BLOCK, VCPU_PORT);
28    zx_status_t status = semaphore_.Wait(ZX_TIME_INFINITE);
29    ktrace_vcpu(TAG_VCPU_UNBLOCK, VCPU_PORT);
30    if (status != ZX_OK) {
31        return nullptr;
32    }
33    return Alloc();
34}
35
36PortPacket* BlockingPortAllocator::Alloc() {
37    return arena_.New(nullptr, this);
38}
39
40void BlockingPortAllocator::Free(PortPacket* port_packet) {
41    arena_.Delete(port_packet);
42    semaphore_.Post();
43}
44
45Trap::Trap(uint32_t kind, zx_gpaddr_t addr, size_t len, fbl::RefPtr<PortDispatcher> port,
46                     uint64_t key)
47    : kind_(kind), addr_(addr), len_(len), port_(fbl::move(port)), key_(key) {
48    (void) key_;
49}
50
51Trap::~Trap() {
52    if (port_ == nullptr) {
53        return;
54    }
55    port_->CancelQueued(nullptr /* handle */, key_);
56}
57
58zx_status_t Trap::Init() {
59    return port_allocator_.Init();
60}
61
62zx_status_t Trap::Queue(const zx_port_packet_t& packet, StateInvalidator* invalidator) {
63    if (invalidator != nullptr) {
64        invalidator->Invalidate();
65    }
66    if (port_ == nullptr) {
67        return ZX_ERR_NOT_FOUND;
68    }
69    PortPacket* port_packet = port_allocator_.AllocBlocking();
70    if (port_packet == nullptr) {
71        return ZX_ERR_NO_MEMORY;
72    }
73    port_packet->packet = packet;
74    zx_status_t status = port_->Queue(port_packet, ZX_SIGNAL_NONE, 0);
75    if (status != ZX_OK) {
76        port_allocator_.Free(port_packet);
77    }
78    return status;
79}
80
81zx_status_t TrapMap::InsertTrap(uint32_t kind, zx_gpaddr_t addr, size_t len,
82                                fbl::RefPtr<PortDispatcher> port, uint64_t key) {
83    TrapTree* traps = TreeOf(kind);
84    if (traps == nullptr) {
85        return ZX_ERR_INVALID_ARGS;
86    }
87    auto iter = traps->find(addr);
88    if (iter.IsValid()) {
89        dprintf(INFO, "Trap for kind %u (addr %#lx len %lu key %lu) already exists "
90                "(addr %#lx len %lu key %lu)\n", kind, addr, len, key, iter->addr(), iter->len(),
91                iter->key());
92        return ZX_ERR_ALREADY_EXISTS;
93    }
94    fbl::AllocChecker ac;
95    fbl::unique_ptr<Trap> range(new (&ac) Trap(kind, addr, len, fbl::move(port), key));
96    if (!ac.check()) {
97        return ZX_ERR_NO_MEMORY;
98    }
99    zx_status_t status = range->Init();
100    if (status != ZX_OK) {
101        return status;
102    }
103    {
104        fbl::AutoLock lock(&mutex_);
105        traps->insert(fbl::move(range));
106    }
107    return ZX_OK;
108}
109
110zx_status_t TrapMap::FindTrap(uint32_t kind, zx_gpaddr_t addr, Trap** trap) {
111    TrapTree* traps = TreeOf(kind);
112    if (traps == nullptr) {
113        return ZX_ERR_INVALID_ARGS;
114    }
115    TrapTree::iterator iter;
116    {
117        fbl::AutoLock lock(&mutex_);
118        iter = traps->upper_bound(addr);
119    }
120    --iter;
121    if (!iter.IsValid() || !iter->Contains(addr)) {
122        return ZX_ERR_NOT_FOUND;
123    }
124    *trap = const_cast<Trap*>(&*iter);
125    return ZX_OK;
126}
127
128TrapMap::TrapTree* TrapMap::TreeOf(uint32_t kind) {
129    switch (kind) {
130    case ZX_GUEST_TRAP_BELL:
131    case ZX_GUEST_TRAP_MEM:
132        return &mem_traps_;
133#ifdef ARCH_X86
134    case ZX_GUEST_TRAP_IO:
135        return &io_traps_;
136#endif // ARCH_X86
137    default:
138        return nullptr;
139    }
140}
141
142} // namespace hypervisor
143