blob: 9078f00fac7f949bf713da3d64e1e4739f748907 [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/packet_mux.h>
#include <zircon/syscalls/hypervisor.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
__UNUSED static const size_t kMaxPacketsPerRange = 256;
BlockingPortAllocator::BlockingPortAllocator() : semaphore_(kMaxPacketsPerRange) {}
zx_status_t BlockingPortAllocator::Init() {
return arena_.Init("hypervisor-packets", kMaxPacketsPerRange);
}
PortPacket* BlockingPortAllocator::Alloc(StateReloader* reloader) {
PortPacket* port_packet = Alloc();
zx_status_t status = semaphore_.Wait(INFINITE_TIME);
// If port_packet is NULL, then Wait would have blocked. So we need to:
// reload our state, check the status of Wait, and Alloc again.
if (port_packet == nullptr) {
reloader->Reload();
if (status != ZX_OK)
return nullptr;
port_packet = Alloc();
}
return port_packet;
}
PortPacket* BlockingPortAllocator::Alloc() {
return arena_.New(nullptr, this);
}
void BlockingPortAllocator::Free(PortPacket* port_packet) {
arena_.Delete(port_packet);
if (semaphore_.Post() > 0)
thread_reschedule();
}
PortRange::PortRange(zx_vaddr_t addr, size_t len, fbl::RefPtr<PortDispatcher> port, uint64_t key)
: addr_(addr), len_(len), port_(fbl::move(port)), key_(key) {
(void) key_;
}
zx_status_t PortRange::Init() {
return port_allocator_.Init();
}
zx_status_t PortRange::Queue(const zx_port_packet_t& packet, StateReloader* reloader) {
PortPacket* port_packet = port_allocator_.Alloc(reloader);
if (port_packet == nullptr)
return ZX_ERR_NO_MEMORY;
port_packet->packet = packet;
port_packet->packet.key = key_;
port_packet->packet.type |= PKT_FLAG_EPHEMERAL;
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 PacketMux::AddPortRange(zx_vaddr_t addr, size_t len,
fbl::RefPtr<PortDispatcher> port, uint64_t key) {
fbl::AllocChecker ac;
fbl::unique_ptr<PortRange> range(new (&ac) PortRange(addr, len, fbl::move(port), key));
if (!ac.check())
return ZX_ERR_NO_MEMORY;
zx_status_t status = range->Init();
if (status != ZX_OK)
return status;
{
fbl::AutoLock lock(&mutex_);
ports_.insert(fbl::move(range));
}
return ZX_OK;
}
zx_status_t PacketMux::FindPortRange(zx_vaddr_t addr, PortRange** port_range) {
PortTree::iterator iter;
{
fbl::AutoLock lock(&mutex_);
iter = ports_.upper_bound(addr);
}
--iter;
if (!iter.IsValid() || !iter->InRange(addr))
return ZX_ERR_NOT_FOUND;
*port_range = const_cast<PortRange*>(&*iter);
return ZX_OK;
}
zx_status_t PacketMux::Queue(zx_vaddr_t addr, const zx_port_packet_t& packet,
StateReloader* reloader) {
PortRange* port_range;
zx_status_t status = FindPortRange(addr, &port_range);
if (status != ZX_OK)
return status;
return port_range->Queue(packet, reloader);
}