blob: 62b14ff7c7e4f464b1f1c8111f37ed52a6a6ae89 [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_controller.h"
#include <lib/async/cpp/task.h>
#include <lib/syslog/cpp/log_settings.h>
#include <lib/syslog/cpp/macros.h>
namespace vmm {
using ::fuchsia::virtualization::GuestConfig;
using ::fuchsia::virtualization::GuestError;
using ::fuchsia::virtualization::GuestLifecycle;
VmmController::VmmController(fit::function<void()> stop_component_callback,
std::unique_ptr<sys::ComponentContext> context,
async_dispatcher_t* dispatcher)
: stop_component_callback_(std::move(stop_component_callback)),
context_(std::move(context)),
dispatcher_(dispatcher) {
FX_CHECK(stop_component_callback_);
FX_CHECK(dispatcher_ != nullptr);
bindings_.set_empty_set_handler([this]() { LifecycleChannelClosed(); });
FX_CHECK(context_->outgoing()->AddPublicService(bindings_.GetHandler(this)) == ZX_OK);
}
void VmmController::Create(::fuchsia::virtualization::GuestConfig guest_config,
CreateCallback callback) {
if (run_callback_.has_value()) {
callback(fpromise::error(GuestError::ALREADY_RUNNING));
return;
}
if (vmm_) {
vmm_.reset();
}
auto vmm = std::make_unique<vmm::Vmm>();
fit::result<GuestError> result =
vmm->Initialize(std::move(guest_config), context_.get(), dispatcher_);
if (!result.is_ok()) {
callback(fpromise::error(result.error_value()));
return;
}
vmm_ = std::move(vmm);
callback(fpromise::ok());
}
void VmmController::Run(RunCallback callback) {
if (!vmm_) {
callback(fpromise::error(GuestError::NOT_CREATED));
return;
}
if (run_callback_.has_value()) {
callback(fpromise::error(GuestError::ALREADY_RUNNING));
return;
}
auto result = vmm_->StartPrimaryVcpu(
[this](fit::result<GuestError> result) { ScheduleVmmTeardown(result); });
if (result.is_error()) {
vmm_.reset();
callback(fpromise::error(result.error_value()));
return;
}
run_callback_ = std::move(callback);
}
void VmmController::Bind(fidl::InterfaceRequest<::fuchsia::virtualization::Guest> guest) {
if (vmm_) {
vmm_->AddBinding(std::move(guest));
}
}
void VmmController::Stop(StopCallback callback) {
ScheduleVmmTeardown(fit::error(GuestError::CONTROLLER_FORCED_HALT));
callback();
}
void VmmController::LifecycleChannelClosed() {
FX_LOGS(INFO) << "A client closed the lifecycle channel, shutting down the VMM component";
stop_component_callback_();
}
void VmmController::ScheduleVmmTeardown(fit::result<GuestError> result) {
const zx_status_t status =
async::PostTask(dispatcher_, [this, result]() { DestroyAndRespond(result); });
// If ZX_OK, the task was successfully scheduled. If ZX_ERR_BAD_STATE, the component is already
// shutting down so there is nothing to do.
if (status != ZX_OK && status != ZX_ERR_BAD_STATE) {
FX_LOGS(WARNING) << "Failed to schedule a VMM teardown, so shutting down the component instead";
stop_component_callback_();
}
}
void VmmController::DestroyAndRespond(fit::result<::fuchsia::virtualization::GuestError> result) {
if (vmm_) {
vmm_->NotifyClientsShutdown(result.is_ok() ? ZX_OK : ZX_ERR_INTERNAL);
}
vmm_.reset();
if (run_callback_.has_value()) {
RunCallback callback = std::exchange(run_callback_, std::nullopt).value();
if (result.is_error()) {
callback(fpromise::error(result.error_value()));
} else {
callback(fpromise::ok());
}
}
}
} // namespace vmm