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
16 static constexpr size_t kMaxPacketsPerRange = 256;
17
18 namespace hypervisor {
19
BlockingPortAllocator()20 BlockingPortAllocator::BlockingPortAllocator() : semaphore_(kMaxPacketsPerRange) {}
21
Init()22 zx_status_t BlockingPortAllocator::Init() {
23 return arena_.Init("hypervisor-packets", kMaxPacketsPerRange);
24 }
25
AllocBlocking()26 PortPacket* BlockingPortAllocator::AllocBlocking() {
27 ktrace_vcpu(TAG_VCPU_BLOCK, VCPU_PORT);
28 zx_status_t status = semaphore_.Wait(ZX_TIME_INFINITE, kNoSlack);
29 ktrace_vcpu(TAG_VCPU_UNBLOCK, VCPU_PORT);
30 if (status != ZX_OK) {
31 return nullptr;
32 }
33 return Alloc();
34 }
35
Alloc()36 PortPacket* BlockingPortAllocator::Alloc() {
37 return arena_.New(nullptr, this);
38 }
39
Free(PortPacket * port_packet)40 void BlockingPortAllocator::Free(PortPacket* port_packet) {
41 arena_.Delete(port_packet);
42 semaphore_.Post();
43 }
44
Trap(uint32_t kind,zx_gpaddr_t addr,size_t len,fbl::RefPtr<PortDispatcher> port,uint64_t key)45 Trap::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_(ktl::move(port)), key_(key) {
48 (void) key_;
49 }
50
~Trap()51 Trap::~Trap() {
52 if (port_ == nullptr) {
53 return;
54 }
55 port_->CancelQueued(nullptr /* handle */, key_);
56 }
57
Init()58 zx_status_t Trap::Init() {
59 return port_allocator_.Init();
60 }
61
Queue(const zx_port_packet_t & packet,StateInvalidator * invalidator)62 zx_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
InsertTrap(uint32_t kind,zx_gpaddr_t addr,size_t len,fbl::RefPtr<PortDispatcher> port,uint64_t key)81 zx_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 ktl::unique_ptr<Trap> range(new (&ac) Trap(kind, addr, len, ktl::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(ktl::move(range));
106 }
107 return ZX_OK;
108 }
109
FindTrap(uint32_t kind,zx_gpaddr_t addr,Trap ** trap)110 zx_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
TreeOf(uint32_t kind)128 TrapMap::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