blob: 0814de5a78dd84d06e76aa26c809e9d4eea9b191 [file] [log] [blame]
// Copyright 2020 The Chromium OS 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 <string>
#include <base/files/file_util.h>
#include "vm_tools/common/pstore.h"
#include "vm_tools/concierge/arc_vm.h"
#include "vm_tools/concierge/service.h"
#include "vm_tools/concierge/shared_data.h"
#include "vm_tools/concierge/vm_util.h"
namespace vm_tools {
namespace concierge {
namespace {
// Android data directory.
constexpr const char kAndroidDataDir[] = "/run/arcvm/android-data";
} // namespace
std::unique_ptr<dbus::Response> Service::StartArcVm(
dbus::MethodCall* method_call) {
LOG(INFO) << "Received StartArcVm request";
std::unique_ptr<dbus::Response> dbus_response(
dbus::Response::FromMethodCall(method_call));
dbus::MessageReader reader(method_call);
dbus::MessageWriter writer(dbus_response.get());
StartArcVmRequest request;
StartVmResponse response;
auto helper_result = StartVmHelper<StartArcVmRequest>(
method_call, &reader, &writer, true /* allow_zero_cpus */);
if (!helper_result) {
return dbus_response;
}
std::tie(request, response) = *helper_result;
VmInfo* vm_info = response.mutable_vm_info();
vm_info->set_vm_type(VmInfo::ARC_VM);
if (request.disks_size() > kMaxExtraDisks) {
LOG(ERROR) << "Rejecting request with " << request.disks_size()
<< " extra disks";
response.set_failure_reason("Too many extra disks");
writer.AppendProtoAsArrayOfBytes(response);
return dbus_response;
}
const base::FilePath kernel(request.vm().kernel());
const base::FilePath rootfs(request.vm().rootfs());
const base::FilePath fstab(request.fstab());
if (!base::PathExists(kernel)) {
LOG(ERROR) << "Missing VM kernel path: " << kernel.value();
response.set_failure_reason("Kernel path does not exist");
writer.AppendProtoAsArrayOfBytes(response);
return dbus_response;
}
if (!base::PathExists(rootfs)) {
LOG(ERROR) << "Missing VM rootfs path: " << rootfs.value();
response.set_failure_reason("Rootfs path does not exist");
writer.AppendProtoAsArrayOfBytes(response);
return dbus_response;
}
if (!base::PathExists(fstab)) {
LOG(ERROR) << "Missing VM fstab path: " << fstab.value();
response.set_failure_reason("Fstab path does not exist");
writer.AppendProtoAsArrayOfBytes(response);
return dbus_response;
}
std::vector<Disk> disks;
// The rootfs can be treated as a disk as well and needs to be added before
// other disks.
disks.push_back(Disk(std::move(rootfs), request.rootfs_writable()));
for (const auto& disk : request.disks()) {
if (!base::PathExists(base::FilePath(disk.path()))) {
LOG(ERROR) << "Missing disk path: " << disk.path();
response.set_failure_reason("One or more disk paths do not exist");
writer.AppendProtoAsArrayOfBytes(response);
return dbus_response;
}
disks.push_back(Disk(base::FilePath(disk.path()), disk.writable()));
}
// Create the runtime directory.
base::FilePath runtime_dir;
if (!base::CreateTemporaryDirInDir(base::FilePath(kRuntimeDir), "vm.",
&runtime_dir)) {
PLOG(ERROR) << "Unable to create runtime directory for VM";
response.set_failure_reason(
"Internal error: unable to create runtime directory");
writer.AppendProtoAsArrayOfBytes(response);
return dbus_response;
}
// Allocate resources for the VM.
uint32_t vsock_cid = vsock_cid_pool_.Allocate();
if (vsock_cid == 0) {
LOG(ERROR) << "Unable to allocate vsock context id";
response.set_failure_reason("Unable to allocate vsock cid");
writer.AppendProtoAsArrayOfBytes(response);
return dbus_response;
}
vm_info->set_cid(vsock_cid);
std::unique_ptr<patchpanel::Client> network_client =
patchpanel::Client::New(bus_);
if (!network_client) {
LOG(ERROR) << "Unable to open networking service client";
response.set_failure_reason("Unable to open network service client");
writer.AppendProtoAsArrayOfBytes(response);
return dbus_response;
}
// Map the chronos user (1000) and the chronos-access group (1001) to the
// AID_EXTERNAL_STORAGE user and group (1077).
uint32_t seneschal_server_port = next_seneschal_server_port_++;
std::unique_ptr<SeneschalServerProxy> server_proxy =
SeneschalServerProxy::CreateVsockProxy(bus_, seneschal_service_proxy_,
seneschal_server_port, vsock_cid,
{{1000, 1077}}, {{1001, 1077}});
if (!server_proxy) {
LOG(ERROR) << "Unable to start shared directory server";
response.set_failure_reason("Unable to start shared directory server");
writer.AppendProtoAsArrayOfBytes(response);
return dbus_response;
}
uint32_t seneschal_server_handle = server_proxy->handle();
vm_info->set_seneschal_server_handle(seneschal_server_handle);
// Build the plugin params.
std::vector<std::string> params(
std::make_move_iterator(request.mutable_params()->begin()),
std::make_move_iterator(request.mutable_params()->end()));
params.emplace_back(base::StringPrintf("androidboot.seneschal_server_port=%d",
seneschal_server_port));
// Start the VM and build the response.
ArcVmFeatures features;
features.rootfs_writable = request.rootfs_writable();
features.use_dev_conf = !request.ignore_dev_conf();
base::FilePath data_dir = base::FilePath(kAndroidDataDir);
if (!base::PathExists(data_dir)) {
LOG(WARNING) << "Android data directory does not exist";
response.set_failure_reason("Android data directory does not exist");
writer.AppendProtoAsArrayOfBytes(response);
return dbus_response;
}
VmId vm_id(request.owner_id(), request.name());
SendVmStartingUpSignal(vm_id, *vm_info);
std::string shared_data =
CreateSharedDataParam(data_dir, "_data", true, false);
std::string shared_data_media =
CreateSharedDataParam(data_dir, "_data_media", false, true);
VmBuilder vm_builder;
vm_builder.AppendDisks(std::move(disks))
.SetCpus(request.cpus())
.AppendKernelParam(base::JoinString(params, " "))
.AppendCustomParam("--android-fstab", fstab.value())
.AppendCustomParam("--pstore",
base::StringPrintf("path=%s,size=%d", kArcVmPstorePath,
kArcVmPstoreSize))
.AppendSharedDir(shared_data)
.AppendSharedDir(shared_data_media)
.EnableSmt(false /* enable */);
auto vm =
ArcVm::Create(std::move(kernel), vsock_cid, std::move(network_client),
std::move(server_proxy), std::move(runtime_dir), features,
std::move(vm_builder));
if (!vm) {
LOG(ERROR) << "Unable to start VM";
response.set_failure_reason("Unable to start VM");
writer.AppendProtoAsArrayOfBytes(response);
return dbus_response;
}
// ARCVM is ready.
LOG(INFO) << "Started VM with pid " << vm->pid();
response.set_success(true);
response.set_status(VM_STATUS_RUNNING);
vm_info->set_ipv4_address(vm->IPv4Address());
vm_info->set_pid(vm->pid());
writer.AppendProtoAsArrayOfBytes(response);
SendVmStartedSignal(vm_id, *vm_info, response.status());
vms_[vm_id] = std::move(vm);
return dbus_response;
}
} // namespace concierge
} // namespace vm_tools