blob: 6b1e7b0d1527d0657cc584d486e67741868d56c7 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/virtualization/bin/vmm/vmm.h"
#include <dirent.h>
#include <fuchsia/virtualization/cpp/fidl.h>
#include <inttypes.h>
#include <lib/async/cpp/task.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/namespace.h>
#include <lib/syslog/cpp/log_settings.h>
#include <lib/syslog/cpp/macros.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <zircon/process.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/hypervisor.h>
#include <atomic>
#include <unordered_map>
#include <vector>
#include "src/virtualization/bin/vmm/linux.h"
#include "src/virtualization/bin/vmm/pci.h"
#include "src/virtualization/bin/vmm/platform_device.h"
#include "src/virtualization/bin/vmm/uart.h"
#include "src/virtualization/bin/vmm/zircon.h"
namespace vmm {
using ::fuchsia::virtualization::BalloonController;
using ::fuchsia::virtualization::GuestConfig;
using ::fuchsia::virtualization::GuestError;
using ::fuchsia::virtualization::HostVsockEndpoint;
using ::fuchsia::virtualization::KernelType;
namespace {
bool IsValidConfig(const ::fuchsia::virtualization::GuestConfig& guest_config) {
if (!guest_config.has_guest_memory()) {
FX_LOGS(ERROR) << "Config must set the amount of required guest memory";
return false;
}
if (!guest_config.has_cpus()) {
FX_LOGS(ERROR) << "Config must set the number of cpus";
return false;
}
if (!guest_config.has_kernel_type()) {
FX_LOGS(ERROR) << "Config must set a kernel type";
return false;
}
switch (guest_config.kernel_type()) {
case KernelType::ZIRCON:
case KernelType::LINUX:
break;
default:
FX_LOGS(ERROR) << "Unknown kernel: " << static_cast<int64_t>(guest_config.kernel_type());
return false;
}
return true;
}
// Duplicates a socket, asserting on failure.
zx::socket DuplicateSocket(const zx::socket& socket) {
zx::socket duplicate_socket;
FX_CHECK(socket.duplicate(ZX_RIGHT_SAME_RIGHTS, &duplicate_socket) == ZX_OK);
return duplicate_socket;
}
} // namespace
Vmm::~Vmm() {
// Explicitly destroy the guest in the destructor to ensure it's the first object destroyed. The
// guest has ownership of VCPU threads that may attempt to access various other objects via the
// guest, and its destructor joins those threads avoiding any use after free problems.
guest_.reset();
if (outgoing_) {
outgoing_->RemovePublicService<Guest>();
}
}
fit::result<GuestError> Vmm::Initialize(GuestConfig cfg, ::sys::ComponentContext* context,
async_dispatcher_t* dispatcher) {
if (!IsValidConfig(cfg)) {
return fit::error(GuestError::BAD_CONFIG);
}
outgoing_ = context->outgoing();
DevMem dev_mem;
guest_ = std::make_unique<::Guest>();
uint64_t virtio_mem_region_size = 0;
uint64_t virtio_mem_region_alignment = 0;
if (cfg.has_virtio_mem() && cfg.virtio_mem()) {
virtio_mem_region_size = cfg.virtio_mem_region_size();
virtio_mem_region_alignment =
std::max(cfg.virtio_mem_block_size(), cfg.virtio_mem_region_alignment());
}
zx_status_t status =
guest_->Init(cfg.guest_memory(), virtio_mem_region_size, virtio_mem_region_alignment);
if (status != ZX_OK) {
return fit::error(GuestError::GUEST_INITIALIZATION_FAILURE);
}
// Setup interrupt controller.
interrupt_controller_ = std::make_unique<InterruptController>(guest_.get());
#if __aarch64__
status = interrupt_controller_->Init(cfg.cpus());
#elif __x86_64__
status = interrupt_controller_->Init();
#else
#error Unknown architecture.
#endif
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to create interrupt controller";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
platform_devices_.push_back(interrupt_controller_.get());
// Setup UARTs.
zx::socket host_serial_socket;
FX_CHECK(zx::socket::create(0, &host_serial_socket, &client_serial_socket_) == ZX_OK);
uart_ = std::make_unique<Uart>(std::move(host_serial_socket));
#if __aarch64__
status = uart_->Init(guest_.get());
#elif __x86_64__
status =
uart_->Init(guest_.get(), [this](uint32_t irq) { interrupt_controller_->Interrupt(irq); });
#else
#error Unknown architecture.
#endif
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to create UART";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
platform_devices_.push_back(uart_.get());
#if __aarch64__
// Setup PL031 RTC.
pl031_ = std::make_unique<Pl031>();
status = pl031_->Init(guest_.get());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to create PL031 RTC";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
platform_devices_.push_back(pl031_.get());
#elif __x86_64__
// Setup IO ports.
io_port_ = std::make_unique<IoPort>();
status = io_port_->Init(guest_.get());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to create IO ports";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
#else
#error Unknown architecture.
#endif
// Setup PCI.
pci_bus_ = std::make_unique<PciBus>(guest_.get(), interrupt_controller_.get());
status = pci_bus_->Init(dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to create PCI bus";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
platform_devices_.push_back(pci_bus_.get());
// Setup mem device.
if (cfg.has_virtio_mem() && cfg.virtio_mem()) {
mem_ = std::make_unique<VirtioMem>(guest_->phys_mem(), cfg.virtio_mem_block_size(),
guest_->mem_pluggable_region_addr(),
cfg.virtio_mem_region_size());
status = pci_bus_->Connect(mem_->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to connect mem device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
status = mem_->Start(guest_->object(), context, dispatcher, cfg.virtio_mem_block_size(),
cfg.virtio_mem_region_size());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to start mem device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
}
// Setup balloon device.
if (cfg.has_virtio_balloon() && cfg.virtio_balloon()) {
balloon_ = std::make_unique<VirtioBalloon>(guest_->phys_mem());
status = pci_bus_->Connect(balloon_->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to connect balloon device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
status = balloon_->Start(guest_->object(), context, dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to start balloon device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
}
// Create a new VirtioBlock device for each device requested.
for (auto& block_device : *cfg.mutable_block_devices()) {
auto block = std::make_unique<VirtioBlock>(guest_->phys_mem(), block_device.mode,
std::move(block_device.format));
status = pci_bus_->Connect(block->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to connect block device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
status =
block->Start(guest_->object(), block_device.id, context, dispatcher, block_devices_.size());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to start block device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
block_devices_.push_back(std::move(block));
}
// Setup console device.
if (cfg.has_virtio_console() && cfg.virtio_console()) {
console_ = std::make_unique<VirtioConsole>(guest_->phys_mem());
status = pci_bus_->Connect(console_->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to connect console device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
zx::socket host_console_socket;
FX_CHECK(zx::socket::create(0, &host_console_socket, &client_console_socket_) == ZX_OK);
status = console_->Start(guest_->object(), std::move(host_console_socket), context, dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to start console device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
}
if (cfg.has_virtio_gpu() && cfg.virtio_gpu()) {
gpu_ = std::make_unique<VirtioGpu>(guest_->phys_mem());
input_keyboard_ = std::make_unique<VirtioInput>(guest_->phys_mem(), VirtioInput::Keyboard);
input_pointer_ = std::make_unique<VirtioInput>(guest_->phys_mem(), VirtioInput::Pointer);
fidl::InterfaceHandle<fuchsia::ui::input3::KeyboardListener> keyboard_listener;
fidl::InterfaceHandle<fuchsia::ui::pointer::MouseSource> mouse_source;
auto mouse_source_request = mouse_source.NewRequest();
// Setup keyboard device.
status = pci_bus_->Connect(input_keyboard_->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to connect keyboard device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
status = input_keyboard_->Start(
guest_->object(),
fuchsia::virtualization::hardware::InputType::WithKeyboard(keyboard_listener.NewRequest()),
context, dispatcher, "virtio_input_keyboard");
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to start keyboard device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
// Setup pointer device.
status = pci_bus_->Connect(input_pointer_->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to connect mouse device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
status = input_pointer_->Start(
guest_->object(),
fuchsia::virtualization::hardware::InputType::WithMouse(std::move(mouse_source)), context,
dispatcher, "virtio_input_pointer");
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to start mouse device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
// Setup GPU device.
status = pci_bus_->Connect(gpu_->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to connect GPU device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
status = gpu_->Start(guest_->object(), std::move(keyboard_listener),
std::move(mouse_source_request), context, dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to start GPU device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
}
// Setup RNG device.
if (cfg.has_virtio_rng() && cfg.virtio_rng()) {
rng_ = std::make_unique<VirtioRng>(guest_->phys_mem());
status = pci_bus_->Connect(rng_->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to connect RNG device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
status = rng_->Start(guest_->object(), context, dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to start RNG device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
}
if (cfg.has_virtio_vsock() && cfg.virtio_vsock()) {
vsock_ = std::make_unique<controller::VirtioVsock>(guest_->phys_mem());
status = pci_bus_->Connect(vsock_->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to connect vsock device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
status = vsock_->Start(guest_->object(), std::move(*cfg.mutable_vsock_listeners()), context,
dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to start vsock device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
}
// Setup wayland device.
if (cfg.has_wayland_device()) {
wl_ = std::make_unique<VirtioWl>(guest_->phys_mem());
const size_t wl_dev_mem_size = cfg.wayland_device().memory;
const zx_gpaddr_t wl_dev_mem_offset = AllocDeviceAddr(wl_dev_mem_size);
if (!dev_mem.AddRange(wl_dev_mem_offset, wl_dev_mem_size)) {
FX_LOGS(INFO) << "Could not reserve device memory range for wayland device";
return fit::error(GuestError::INTERNAL_ERROR);
}
zx::vmar wl_vmar;
status = guest_->CreateSubVmar(wl_dev_mem_offset, wl_dev_mem_size, &wl_vmar);
if (status != ZX_OK) {
FX_LOGS(INFO) << "Could not create VMAR for wayland device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
status = pci_bus_->Connect(wl_->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_LOGS(INFO) << "Could not connect wayland device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem_allocator = nullptr;
status = fdio_service_connect("/svc/fuchsia.sysmem.Allocator",
sysmem_allocator.NewRequest().TakeChannel().release());
if (status != ZX_OK) {
FX_LOGS(INFO) << "Could not connect to sysmem allocator service";
return fit::error(GuestError::FAILED_SERVICE_CONNECT);
}
fidl::InterfaceHandle<fuchsia::ui::composition::Allocator> scenic_allocator = nullptr;
status = fdio_service_connect("/svc/fuchsia.ui.composition.Allocator",
scenic_allocator.NewRequest().TakeChannel().release());
if (status != ZX_OK) {
FX_LOGS(INFO) << "Could not connect to scenic allocator service";
return fit::error(GuestError::FAILED_SERVICE_CONNECT);
}
status = wl_->Start(guest_->object(), std::move(wl_vmar), std::move(sysmem_allocator),
std::move(scenic_allocator), context, dispatcher);
if (status != ZX_OK) {
FX_LOGS(INFO) << "Could not start wayland device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
}
// Setup magma device.
if (cfg.has_magma_device()) {
magma_ = std::make_unique<VirtioMagma>(guest_->phys_mem());
// TODO(https://fxbug.dev/42076944): simplify vmm launch configs
const size_t magma_dev_mem_size = cfg.magma_device().memory;
const zx_gpaddr_t magma_dev_mem_offset = AllocDeviceAddr(magma_dev_mem_size);
if (!dev_mem.AddRange(magma_dev_mem_offset, magma_dev_mem_size)) {
FX_PLOGS(INFO, status) << "Could not reserve device memory range for magma device";
return fit::error(GuestError::INTERNAL_ERROR);
}
zx::vmar magma_vmar;
status = guest_->CreateSubVmar(magma_dev_mem_offset, magma_dev_mem_size, &magma_vmar);
if (status != ZX_OK) {
FX_PLOGS(INFO, status) << "Could not create VMAR for magma device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
status = pci_bus_->Connect(magma_->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_PLOGS(INFO, status) << "Could not connect magma device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
fidl::InterfaceHandle<fuchsia::virtualization::hardware::VirtioWaylandImporter>
wayland_importer_handle = nullptr;
if (cfg.has_wayland_device()) {
status = wl_->GetImporter(wayland_importer_handle.NewRequest());
if (status != ZX_OK) {
FX_PLOGS(INFO, status) << "Could not get wayland importer";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
}
status = magma_->Start(guest_->object(), std::move(magma_vmar),
std::move(wayland_importer_handle), context, dispatcher);
if (status == ZX_ERR_NOT_FOUND) {
FX_LOGS(INFO) << "Magma device not supported by host";
} else if (status != ZX_OK) {
FX_PLOGS(INFO, status) << "Could not start magma device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
}
// Setup sound device.
if (cfg.has_virtio_sound() && cfg.virtio_sound()) {
sound_ = std::make_unique<VirtioSound>(guest_->phys_mem());
status = pci_bus_->Connect(sound_->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to connect sound device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
status = sound_->Start(guest_->object(), context, dispatcher,
cfg.has_virtio_sound_input() && cfg.virtio_sound_input());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to start sound device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
}
// Setup net device. We setup networking last, as this can cause a temporary loss of network
// access as we configure the bridge. If networking is lost while loading packages for devices,
// the VMM will fail.
for (auto& net_device : *cfg.mutable_net_devices()) {
auto net = std::make_unique<VirtioNet>(guest_->phys_mem());
status = pci_bus_->Connect(net->pci_device(), dispatcher);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to connect Ethernet device";
return fit::error(GuestError::DEVICE_INITIALIZATION_FAILURE);
}
status = net->Start(guest_->object(), net_device.mac_address, net_device.enable_bridge, context,
dispatcher, net_devices_.size());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Could not open Ethernet device";
return fit::error(GuestError::DEVICE_START_FAILURE);
}
net_devices_.push_back(std::move(net));
}
#if __x86_64__
if (auto result = CreatePageTable(guest_->phys_mem()); result.is_error()) {
FX_PLOGS(ERROR, result.status_value()) << "Failed to create page table";
return fit::error(GuestError::INTERNAL_ERROR);
}
const AcpiConfig acpi_cfg = {
.dsdt_path = kDsdtPath,
.mcfg_path = kMcfgPath,
.io_apic_addr = IoApic::kPhysBase,
.cpus = cfg.cpus(),
};
status = create_acpi_table(acpi_cfg, guest_->phys_mem());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to create ACPI table";
return fit::error(GuestError::INTERNAL_ERROR);
}
#endif // __x86_64__
// Add any trap ranges as device memory.
for (const IoMapping& mapping : guest_->mappings()) {
if ((mapping.kind() == ZX_GUEST_TRAP_MEM || mapping.kind() == ZX_GUEST_TRAP_BELL) &&
!dev_mem.AddRange(mapping.base(), mapping.size())) {
FX_LOGS(ERROR) << "Failed to add trap range as device memory";
return fit::error(GuestError::INTERNAL_ERROR);
}
}
// Device memory has been finalized. Ensure that there's no overlap with the generated guest
// memory ranges.
dev_mem.Finalize();
if (dev_mem.HasGuestMemoryOverlap(guest_->memory_regions())) {
// Logs faulty guest ranges internally.
return fit::error(GuestError::DEVICE_MEMORY_OVERLAP);
}
// Setup kernel.
switch (cfg.kernel_type()) {
case fuchsia::virtualization::KernelType::ZIRCON:
status = setup_zircon(&cfg, guest_->phys_mem(), dev_mem, guest_->memory_regions(),
platform_devices_, &entry_, &boot_ptr_);
break;
case fuchsia::virtualization::KernelType::LINUX:
status = setup_linux(&cfg, guest_->phys_mem(), dev_mem, guest_->memory_regions(),
platform_devices_, &entry_, &boot_ptr_);
break;
}
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to load kernel";
return fit::error(GuestError::KERNEL_LOAD_FAILURE);
}
auto result = AddPublicServices();
if (result.is_error()) {
return result;
}
return fit::ok();
}
fit::result<GuestError> Vmm::StartPrimaryVcpu(
fit::function<void(fit::result<GuestError>)> stop_callback) {
guest_->set_stop_callback(std::move(stop_callback));
zx_status_t status = guest_->StartVcpu(/*id=*/0, entry_, boot_ptr_);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to start VCPU-0";
return fit::error(GuestError::VCPU_START_FAILURE);
}
return fit::ok();
}
zx_gpaddr_t Vmm::AllocDeviceAddr(size_t device_size) {
const zx_gpaddr_t ret = next_device_address_;
next_device_address_ += device_size;
return ret;
}
fit::result<GuestError> Vmm::AddPublicServices() {
zx_status_t status = outgoing_->AddPublicService(guest_bindings_.GetHandler(this));
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to add guest controller public service";
return fit::error(GuestError::DUPLICATE_PUBLIC_SERVICES);
}
return fit::ok();
}
void Vmm::GetSerial(GetSerialCallback callback) {
FX_CHECK(client_serial_socket_.is_valid());
callback(DuplicateSocket(client_serial_socket_));
}
void Vmm::GetConsole(GetConsoleCallback callback) {
if (console_) {
FX_CHECK(client_serial_socket_.is_valid());
callback(fpromise::ok(DuplicateSocket(client_console_socket_)));
} else {
FX_LOGS(WARNING) << "Attempted to get console socket, but the console device is not present";
callback(fpromise::error(GuestError::DEVICE_NOT_PRESENT));
}
}
void Vmm::GetHostVsockEndpoint(fidl::InterfaceRequest<HostVsockEndpoint> endpoint,
GetHostVsockEndpointCallback callback) {
if (vsock_) {
vsock_->GetHostVsockEndpoint(std::move(endpoint));
callback(fpromise::ok());
} else {
FX_LOGS(WARNING) << "Attempted to get HostVsockEndpoint, but the vsock device is not present";
callback(fpromise::error(GuestError::DEVICE_NOT_PRESENT));
}
}
void Vmm::GetBalloonController(fidl::InterfaceRequest<BalloonController> endpoint,
GetBalloonControllerCallback callback) {
if (balloon_) {
balloon_->ConnectToBalloonController(std::move(endpoint));
callback(fpromise::ok());
} else {
FX_LOGS(WARNING) << "Attempted to get BalloonController, but the balloon device is not present";
callback(fpromise::error(GuestError::DEVICE_NOT_PRESENT));
}
}
void Vmm::GetMemController(fidl::InterfaceRequest<fuchsia::virtualization::MemController> endpoint,
GetMemControllerCallback callback) {
if (mem_) {
mem_->ConnectToMemController(std::move(endpoint));
callback(fpromise::ok());
} else {
FX_LOGS(WARNING) << "Attempted to get MemController, but the mem device is not present";
callback(fpromise::error(GuestError::DEVICE_NOT_PRESENT));
}
}
void Vmm::NotifyClientsShutdown(zx_status_t status) { guest_bindings_.CloseAll(status); }
} // namespace vmm