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