blob: bf62057f02af4f07446ad59e681ee8679166cb23 [file] [log] [blame] [edit]
// 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 <fcntl.h>
#include <fidl/fuchsia.buildinfo/cpp/wire.h>
#include <fidl/fuchsia.fshost/cpp/wire.h>
#include <fidl/fuchsia.paver/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/default.h>
#include <lib/component/incoming/cpp/service_client.h>
#include <lib/fastboot/fastboot.h>
#include <lib/fdio/directory.h>
#include <lib/fidl-async/cpp/bind.h>
#include <zircon/status.h>
#include <optional>
#include <string_view>
#include <thread>
#include <vector>
#include <fbl/unique_fd.h>
#include <sdk/lib/syslog/cpp/macros.h>
#include "payload-streamer.h"
#include "sparse_format.h"
#include "src/lib/fxl/strings/split_string.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace fastboot {
namespace {
constexpr char kFastbootLogTag[] = __FILE__;
struct FlashPartitionInfo {
std::string_view partition;
std::optional<fuchsia_paver::wire::Configuration> configuration;
};
FlashPartitionInfo GetPartitionInfo(std::string_view partition_label) {
size_t len = partition_label.length();
if (len < 2) {
return {partition_label, std::nullopt};
}
FlashPartitionInfo ret;
ret.partition = partition_label.substr(0, len - 2);
std::string_view slot_suffix = partition_label.substr(len - 2, 2);
// TODO(b/241150035): Some platforms such as x64 still use legacy kernel partition name
// zircon-a/b/r. Hardcode these cases for backward compatibility. Once all products migrate to new
// name. Remove them.
if (slot_suffix == "_a" || partition_label == "zircon-a") {
ret.configuration = fuchsia_paver::wire::Configuration::kA;
} else if (slot_suffix == "_b" || partition_label == "zircon-b") {
ret.configuration = fuchsia_paver::wire::Configuration::kB;
} else if (slot_suffix == "_r" || partition_label == "zircon-r") {
ret.configuration = fuchsia_paver::wire::Configuration::kRecovery;
} else {
ret.partition = partition_label;
}
return ret;
}
bool IsAndroidSparseImage(const void* img, size_t size) {
if (size < sizeof(sparse_header_t)) {
return false;
}
sparse_header_t header;
memcpy(&header, img, sizeof(sparse_header_t));
return header.magic == SPARSE_HEADER_MAGIC;
}
} // namespace
const std::vector<Fastboot::CommandEntry>& Fastboot::GetCommandTable() {
// Using a static pointer and allocate with `new` so that the static instance
// never gets deleted.
static const std::vector<CommandEntry>* kCommandTable = new std::vector<CommandEntry>({
{
.name = "getvar",
.cmd = &Fastboot::GetVar,
},
{
.name = "flash",
.cmd = &Fastboot::Flash,
},
{
.name = "set_active",
.cmd = &Fastboot::SetActive,
},
{
.name = "reboot",
.cmd = &Fastboot::Reboot,
},
{
.name = "continue",
.cmd = &Fastboot::Continue,
},
{
.name = "reboot-bootloader",
.cmd = &Fastboot::RebootBootloader,
},
{
.name = "oem add-staged-bootloader-file",
.cmd = &Fastboot::OemAddStagedBootloaderFile,
},
{
.name = "oem init-partition-tables",
.cmd = &Fastboot::OemInitPartitionTables,
},
{
.name = "oem wipe-partition-tables",
.cmd = &Fastboot::OemWipePartitionTables,
},
});
return *kCommandTable;
}
const Fastboot::VariableHashTable& Fastboot::GetVariableTable() {
// Using a static pointer and allocate with `new` so that the static instance
// never gets deleted.
static const VariableHashTable* kVariableTable = new VariableHashTable({
{"max-download-size", &Fastboot::GetVarMaxDownloadSize},
{"slot-count", &Fastboot::GetVarSlotCount},
{"is-userspace", &Fastboot::GetVarIsUserspace},
{"hw-revision", &Fastboot::GetVarHwRevision},
{"product", &Fastboot::GetVarHwRevision},
{"version", &Fastboot::GetVarVersion},
});
return *kVariableTable;
}
Fastboot::Fastboot(size_t max_download_size) : max_download_size_(max_download_size) {}
Fastboot::Fastboot(size_t max_download_size, fidl::ClientEnd<fuchsia_io::Directory> svc_root)
: max_download_size_(max_download_size), svc_root_(std::move(svc_root)) {}
zx::result<> Fastboot::ProcessCommand(std::string_view command, Transport* transport) {
for (const CommandEntry& cmd : GetCommandTable()) {
if (MatchCommand(command, cmd.name)) {
return (this->*cmd.cmd)(command.data(), transport);
}
}
return SendResponse(ResponseType::kFail, "Unsupported command", transport);
}
void Fastboot::DoClearDownload() { download_vmo_mapper_.Reset(); }
zx::result<void*> Fastboot::GetDownloadBuffer(size_t total_download_size) {
if (zx_status_t ret = download_vmo_mapper_.CreateAndMap(total_download_size, "fastboot download");
ret != ZX_OK) {
return zx::error(ret);
}
if (zx_status_t ret = download_vmo_mapper_.vmo().set_prop_content_size(total_download_size);
ret != ZX_OK) {
return zx::error(ret);
}
return zx::ok(download_vmo_mapper_.start());
}
zx::result<> Fastboot::GetVar(const std::string& command, Transport* transport) {
std::vector<std::string_view> args =
fxl::SplitString(command, ":", fxl::kTrimWhitespace, fxl::kSplitWantNonEmpty);
if (args.size() < 2) {
return SendResponse(ResponseType::kFail, "Not enough arguments", transport);
}
const VariableHashTable& var_table = GetVariableTable();
const VariableHashTable::const_iterator find_res = var_table.find(args[1].data());
if (find_res == var_table.end()) {
return SendResponse(ResponseType::kFail, "Unknown variable", transport);
}
zx::result<std::string> var_ret = (this->*(find_res->second))(args, transport);
if (var_ret.is_error()) {
return SendResponse(ResponseType::kFail, "Fail to get variable", transport,
zx::error(var_ret.status_value()));
}
return SendResponse(ResponseType::kOkay, var_ret.value(), transport);
}
zx::result<std::string> Fastboot::GetVarVersion(const std::vector<std::string_view>&, Transport*) {
return zx::ok("0.4");
}
zx::result<std::string> Fastboot::GetVarMaxDownloadSize(const std::vector<std::string_view>&,
Transport*) {
return zx::ok(fxl::StringPrintf("0x%08zx", max_download_size_));
}
zx::result<std::string> Fastboot::GetVarHwRevision(const std::vector<std::string_view>&,
Transport*) {
auto svc_root = GetSvcRoot();
if (svc_root.is_error()) {
return zx::error(svc_root.status_value());
}
auto connect_result = component::ConnectAt<fuchsia_buildinfo::Provider>(*svc_root.value());
if (connect_result.is_error()) {
return zx::error(connect_result.status_value());
}
auto provider = fidl::WireSyncClient(std::move(connect_result.value()));
auto resp = provider->GetBuildInfo();
if (!resp.ok()) {
return zx::error(resp.status());
}
return zx::ok(resp->build_info.board_config().data());
}
zx::result<std::string> Fastboot::GetVarSlotCount(const std::vector<std::string_view>&,
Transport* transport) {
auto boot_manager_res = FindBootManager();
if (boot_manager_res.is_error()) {
auto ret = SendResponse(ResponseType::kFail, "Failed to find boot manager", transport,
zx::error(boot_manager_res.status_value()));
return zx::error(ret.status_value());
}
// `fastboot set_active` only cares whether the device has >1 slots. Doesn't care how many
// exactly.
return boot_manager_res.value()->QueryCurrentConfiguration().ok() ? zx::ok("2") : zx::ok("1");
}
zx::result<std::string> Fastboot::GetVarIsUserspace(const std::vector<std::string_view>&,
Transport*) {
return zx::ok("yes");
}
zx::result<fidl::ClientEnd<fuchsia_io::Directory>*> Fastboot::GetSvcRoot() {
// If `svc_root_` is not set, use the system svc root.
if (!svc_root_) {
zx::channel request, service_root;
zx_status_t status = zx::channel::create(0, &request, &service_root);
if (status != ZX_OK) {
FX_LOGST(ERROR, kFastbootLogTag)
<< "Failed to create channel " << zx_status_get_string(status);
return zx::error(ZX_ERR_INTERNAL);
}
status = fdio_service_connect("/svc", request.release());
if (status != ZX_OK) {
FX_LOGST(ERROR, kFastbootLogTag)
<< "Failed to connect to svc root " << zx_status_get_string(status);
return zx::error(ZX_ERR_INTERNAL);
}
svc_root_ = fidl::ClientEnd<fuchsia_io::Directory>(std::move(service_root));
}
return zx::ok(&svc_root_);
}
zx::result<fidl::WireSyncClient<fuchsia_paver::Paver>> Fastboot::ConnectToPaver() {
// Connect to the paver
auto svc_root = GetSvcRoot();
if (svc_root.is_error()) {
return zx::error(svc_root.status_value());
}
auto paver_svc = component::ConnectAt<fuchsia_paver::Paver>(*svc_root.value());
if (!paver_svc.is_ok()) {
FX_LOGST(ERROR, kFastbootLogTag) << "Unable to open /svc/fuchsia.paver.Paver";
return zx::error(paver_svc.error_value());
}
return zx::ok(fidl::WireSyncClient(std::move(*paver_svc)));
}
fuchsia_mem::wire::Buffer Fastboot::GetWireBufferFromDownload() {
fuchsia_mem::wire::Buffer buf;
buf.size = download_vmo_mapper_.size();
buf.vmo = download_vmo_mapper_.Release();
return buf;
}
zx::result<> Fastboot::WriteFirmware(fuchsia_paver::wire::Configuration config,
std::string_view firmware_type, Transport* transport,
fidl::WireSyncClient<fuchsia_paver::DataSink>& data_sink) {
auto ret = data_sink->WriteFirmware(config, fidl::StringView::FromExternal(firmware_type),
GetWireBufferFromDownload());
if (ret.status() != ZX_OK) {
return SendResponse(ResponseType::kFail, "Failed to invoke paver bootloader write", transport,
zx::error(ret.status()));
}
if (ret.value().result.is_status() && ret.value().result.status() != ZX_OK) {
return SendResponse(ResponseType::kFail, "Failed to write bootloader", transport,
zx::error(ret.value().result.status()));
}
if (ret.value().result.is_unsupported() && ret.value().result.unsupported()) {
return SendResponse(ResponseType::kFail, "Firmware type is not supported", transport);
}
return SendResponse(ResponseType::kOkay, "", transport);
}
zx::result<> Fastboot::WriteAsset(fuchsia_paver::wire::Configuration config,
fuchsia_paver::wire::Asset asset, Transport* transport,
fidl::WireSyncClient<fuchsia_paver::DataSink>& data_sink) {
auto ret = data_sink->WriteAsset(config, asset, GetWireBufferFromDownload());
zx_status_t status = ret.status() == ZX_OK ? ret.value().status : ret.status();
if (status != ZX_OK) {
return SendResponse(ResponseType::kFail, "Failed to flash asset", transport, zx::error(status));
}
return SendResponse(ResponseType::kOkay, "", transport);
}
zx::result<> Fastboot::Flash(const std::string& command, Transport* transport) {
if (IsAndroidSparseImage(download_vmo_mapper_.start(), download_vmo_mapper_.size())) {
return SendResponse(ResponseType::kFail, "Android sparse image is not supported.", transport);
}
std::vector<std::string_view> args =
fxl::SplitString(command, ":", fxl::kTrimWhitespace, fxl::kSplitWantNonEmpty);
if (args.size() < 2) {
return SendResponse(ResponseType::kFail, "Not enough arguments", transport);
}
auto paver_client_res = ConnectToPaver();
if (paver_client_res.is_error()) {
return SendResponse(ResponseType::kFail, "Failed to connect to paver", transport,
zx::error(paver_client_res.status_value()));
}
// Connect to the data sink
auto data_sink_endpoints = fidl::CreateEndpoints<fuchsia_paver::DataSink>();
if (data_sink_endpoints.is_error()) {
return SendResponse(ResponseType::kFail, "Unable to create data sink endpoint", transport,
zx::error(data_sink_endpoints.status_value()));
}
auto [data_sink_local, data_sink_remote] = std::move(*data_sink_endpoints);
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)paver_client_res.value()->FindDataSink(std::move(data_sink_remote));
fidl::WireSyncClient data_sink{std::move(data_sink_local)};
FlashPartitionInfo info = GetPartitionInfo(args[1]);
if (info.partition == "bootloader") {
// If abr suffix is not given, assume that firmware ABR is not supported and just provide a
// A slot configuration. It will be ignored by the paver.
fuchsia_paver::wire::Configuration config =
info.configuration ? *info.configuration : fuchsia_paver::wire::Configuration::kA;
std::string_view firmware_type = args.size() == 3 ? args[2] : "";
return WriteFirmware(config, firmware_type, transport, data_sink);
} else if (info.partition == "fuchsia-esp") {
// x64 platform uses 'fuchsia-esp' for bootloader partition . We should eventually move to use
// "bootloader"
// For legacy `fuchsia-esp` we don't consider firmware ABR or type.
return WriteFirmware(fuchsia_paver::wire::Configuration::kA, "", transport, data_sink);
} else if (info.partition == "zircon" && info.configuration) {
return WriteAsset(*info.configuration, fuchsia_paver::wire::Asset::kKernel, transport,
data_sink);
} else if (info.partition == "vbmeta" && info.configuration) {
return WriteAsset(*info.configuration, fuchsia_paver::wire::Asset::kVerifiedBootMetadata,
transport, data_sink);
} else if (info.partition == "fvm") {
auto ret = data_sink->WriteOpaqueVolume(GetWireBufferFromDownload());
zx_status_t status = ZX_OK;
if (ret.status() != ZX_OK) {
status = ret.status();
} else if (ret->is_error()) {
status = ret->error_value();
}
if (status != ZX_OK) {
return SendResponse(ResponseType::kFail, "Failed to flash opaque fvm", transport,
zx::error(status));
}
return SendResponse(ResponseType::kOkay, "", transport);
} else if (info.partition == "fvm.sparse") {
// Flashing the sparse format FVM image via the paver. Note that at the time this code is
// written, the format of FVM for fuchsia has not reached at a stable point yet. However, the
// implementation of the paver fidl interface `WriteVolumes()` depends on the format of the FVM.
// Therefore, it is important make sure that the device is running the latest version of paver
// before using this fastboot command. This typically means flashing the latest kernel and
// reboot first. Otherwise, if FVM format changes and the currently running paver is not
// up-to-date, the FVM may be flashed wrongly.
auto streamer_endpoints = fidl::CreateEndpoints<fuchsia_paver::PayloadStream>();
if (streamer_endpoints.is_error()) {
return SendResponse(ResponseType::kFail, "Failed to create payload streamer", transport,
zx::error(streamer_endpoints.status_value()));
}
auto [client, server] = std::move(*streamer_endpoints);
// Launch thread which implements interface.
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
internal::PayloadStreamer streamer(std::move(server), download_vmo_mapper_.start(),
download_vmo_mapper_.size());
loop.StartThread("fastboot-payload-stream");
auto result = data_sink->WriteVolumes(std::move(client));
zx_status_t status = result.ok() ? result.value().status : result.status();
if (status != ZX_OK) {
return SendResponse(ResponseType::kFail, "Failed to write fvm", transport, zx::error(status));
}
download_vmo_mapper_.Reset();
return SendResponse(ResponseType::kOkay, "", transport);
} else {
return SendResponse(ResponseType::kFail, "Unsupported partition", transport);
}
return zx::ok();
}
zx::result<fidl::WireSyncClient<fuchsia_paver::BootManager>> Fastboot::FindBootManager() {
auto paver_client_res = ConnectToPaver();
if (!paver_client_res.is_ok()) {
return zx::error(paver_client_res.status_value());
}
zx::result endpoints = fidl::CreateEndpoints<fuchsia_paver::BootManager>();
if (endpoints.is_error()) {
FX_LOGST(ERROR, kFastbootLogTag) << "Failed to create endpoint";
return zx::error(endpoints.status_value());
}
fidl::Status res = paver_client_res.value()->FindBootManager(std::move(endpoints->server));
if (!res.ok()) {
FX_LOGST(ERROR, kFastbootLogTag) << "Failed to find boot manager";
return zx::error(res.status());
}
return zx::ok(fidl::WireSyncClient(std::move(endpoints->client)));
}
zx::result<> Fastboot::SetActive(const std::string& command, Transport* transport) {
std::vector<std::string_view> args =
fxl::SplitString(command, ":", fxl::kTrimWhitespace, fxl::kSplitWantNonEmpty);
if (args.size() < 2) {
return SendResponse(ResponseType::kFail, "Not enough arguments", transport);
}
auto boot_manager_res = FindBootManager();
if (boot_manager_res.is_error()) {
return SendResponse(ResponseType::kFail, "Failed to find boot manager", transport,
zx::error(boot_manager_res.status_value()));
}
fuchsia_paver::wire::Configuration config = fuchsia_paver::wire::Configuration::kB;
if (args[1] == "a") {
config = fuchsia_paver::wire::Configuration::kA;
} else if (args[1] != "b") {
return SendResponse(ResponseType::kFail, "Invalid slot", transport);
}
auto result = boot_manager_res.value()->SetConfigurationActive(config);
zx_status_t status = result.ok() ? result.value().status : result.status();
if (status != ZX_OK) {
return SendResponse(ResponseType::kFail, "Failed to set configuration active: ", transport,
zx::error(status));
}
return SendResponse(ResponseType::kOkay, "", transport);
}
zx::result<fidl::WireSyncClient<fuchsia_hardware_power_statecontrol::Admin>>
Fastboot::ConnectToPowerStateControl() {
auto svc_root = GetSvcRoot();
if (svc_root.is_error()) {
return zx::error(svc_root.status_value());
}
auto connect_result =
component::ConnectAt<fuchsia_hardware_power_statecontrol::Admin>(*svc_root.value());
if (connect_result.is_error()) {
return zx::error(connect_result.status_value());
}
return zx::ok(fidl::WireSyncClient(std::move(connect_result.value())));
}
zx::result<> Fastboot::Reboot(const std::string& command, Transport* transport) {
auto connect_result = ConnectToPowerStateControl();
if (connect_result.is_error()) {
return SendResponse(ResponseType::kFail,
"Failed to connect to power state control service: ", transport,
zx::error(connect_result.status_value()));
}
// Send an okay response regardless of the result. Because once system reboots, we have
// no chance to send any response.
zx::result<> ret = SendResponse(ResponseType::kOkay, "", transport);
if (ret.is_error()) {
return ret;
}
// Wait for 1s to make sure the response is sent over to the transport
std::this_thread::sleep_for(std::chrono::seconds(1));
auto resp = connect_result.value()->Reboot(
fuchsia_hardware_power_statecontrol::RebootReason::kUserRequest);
if (!resp.ok()) {
return zx::error(resp.status());
}
return zx::ok();
}
zx::result<> Fastboot::Continue(const std::string& command, Transport* transport) {
zx::result<> ret = SendResponse(
ResponseType::kInfo, "userspace fastboot cannot continue, rebooting instead", transport);
if (ret.is_error()) {
return ret;
}
return Reboot(command, transport);
}
zx::result<> Fastboot::RebootBootloader(const std::string& command, Transport* transport) {
zx::result<> ret = SendResponse(
ResponseType::kInfo,
"userspace fastboot cannot reboot to bootloader, rebooting to recovery instead", transport);
if (ret.is_error()) {
return ret;
}
auto boot_manager_res = FindBootManager();
if (boot_manager_res.is_error()) {
return SendResponse(ResponseType::kFail, "Failed to find boot manager", transport,
zx::error(boot_manager_res.status_value()));
}
// User A/B/R one shot recovery instead of platform-sepcific reboot-recovery mechanism. Because
// the latter is not always supported, especially in the stage of early bringup. But we don't want
// device-flashing to be hindered by it.
//
// As of the time the code is written, there's a discussion on whether power-manager can support
// one-shot-recovery as well. If the approach is taken, we can switch to RebootToRecovery instead.
auto one_shot_recovery_result = boot_manager_res.value()->SetOneShotRecovery();
if (one_shot_recovery_result->is_error()) {
return SendResponse(ResponseType::kFail, "Failed to set one shot recovery: ", transport,
zx::error(one_shot_recovery_result->error_value()));
}
auto connect_result = ConnectToPowerStateControl();
if (connect_result.is_error()) {
return SendResponse(ResponseType::kFail,
"Failed to connect to power state control service: ", transport,
zx::error(connect_result.status_value()));
}
// Send an okay response regardless of the result. Because once system reboots, we have
// no chance to send any response.
ret = SendResponse(ResponseType::kOkay, "", transport);
if (ret.is_error()) {
return ret;
}
// Wait for 1s to make sure the response is sent over to the transport
std::this_thread::sleep_for(std::chrono::seconds(1));
auto resp = connect_result.value()->Reboot(
fuchsia_hardware_power_statecontrol::RebootReason::kUserRequest);
if (!resp.ok()) {
return zx::error(resp.status());
}
return zx::ok();
}
zx::result<> Fastboot::OemAddStagedBootloaderFile(const std::string& command,
Transport* transport) {
std::vector<std::string_view> args =
fxl::SplitString(command, " ", fxl::kTrimWhitespace, fxl::kSplitWantNonEmpty);
if (args.size() != 3) {
return SendResponse(ResponseType::kFail, "Invalid number of arguments", transport);
}
if (args[2] != sshd_host::kAuthorizedKeysBootloaderFileName) {
return SendResponse(ResponseType::kFail, "Unsupported file: " + std::string(args[2]),
transport);
}
auto svc_root = GetSvcRoot();
if (svc_root.is_error()) {
return zx::error(svc_root.status_value());
}
auto connect_result = component::ConnectAt<fuchsia_fshost::Admin>(*svc_root.value());
if (connect_result.is_error()) {
return SendResponse(ResponseType::kFail, "Failed to connect to fshost", transport,
zx::error(connect_result.status_value()));
}
fidl::WireSyncClient fshost_admin{std::move(connect_result.value())};
auto resp = fshost_admin->WriteDataFile(
fidl::StringView::FromExternal(sshd_host::kAuthorizedKeyPathInData),
download_vmo_mapper_.Release());
zx_status_t status = ZX_OK;
if (resp.status() != ZX_OK) {
status = resp.status();
} else if (resp->is_error()) {
status = resp->error_value();
}
if (status != ZX_OK) {
return SendResponse(ResponseType::kFail, "Failed to write ssh key", transport,
zx::error(status));
}
return SendResponse(ResponseType::kOkay, "", transport);
}
bool Fastboot::FindGptDevices(paver::GptDevicePartitioner::GptDevices& gpt_devices) {
auto fd = fbl::unique_fd(open("/dev", O_RDONLY));
return paver::GptDevicePartitioner::FindGptDevices(fd, &gpt_devices);
}
zx::result<fidl::WireSyncClient<fuchsia_paver::DynamicDataSink>> Fastboot::ConnectToDynamicDataSink(
Transport* transport) {
auto paver_client_res = ConnectToPaver();
if (paver_client_res.is_error()) {
return zx::error(SendResponse(ResponseType::kFail, "Failed to connect to paver", transport,
zx::error(paver_client_res.status_value()))
.status_value());
}
paver::GptDevicePartitioner::GptDevices gpt_devices;
if (!FindGptDevices(gpt_devices)) {
return zx::error(SendResponse(ResponseType::kFail, "Failed to find gpt devices", transport,
zx::error(ZX_ERR_INTERNAL))
.status_value());
}
// Filter out ramdisk block devices.
paver::GptDevicePartitioner::GptDevices non_ramdisk_devices;
for (auto& [path, fd] : gpt_devices) {
if (path.find(kRamDiskString) != std::string::npos) {
continue;
}
non_ramdisk_devices.push_back((std::make_pair(path, std::move(fd))));
}
if (non_ramdisk_devices.empty()) {
return zx::error(SendResponse(ResponseType::kFail, "No suitable block devices", transport,
zx::error(ZX_ERR_INTERNAL))
.status_value());
} else if (non_ramdisk_devices.size() > 1) {
return zx::error(SendResponse(ResponseType::kFail, "More than one suitable devices", transport,
zx::error(ZX_ERR_INTERNAL))
.status_value());
}
auto data_sink = fidl::CreateEndpoints<fuchsia_paver::DynamicDataSink>();
if (data_sink.is_error()) {
return zx::error(SendResponse(ResponseType::kFail, "Failed to create end points", transport,
zx::error(data_sink.status_value()))
.status_value());
}
auto [data_sink_local, data_sink_remote] = std::move(*data_sink);
fdio_cpp::FdioCaller caller(std::move(non_ramdisk_devices[0].second));
auto channel = caller.take_channel();
if (channel.is_error()) {
return zx::error(SendResponse(ResponseType::kFail, "Failed to take channel", transport,
zx::error(channel.status_value()))
.status_value());
}
fidl::ClientEnd<fuchsia_hardware_block::Block> block_device(std::move(*channel));
auto res = paver_client_res.value()->UseBlockDevice(
std::move(block_device),
fidl::ServerEnd<fuchsia_paver::DynamicDataSink>(data_sink_remote.TakeChannel()));
if (!res.ok()) {
return zx::error(SendResponse(ResponseType::kFail, "Failed to create dynamic data sink",
transport, zx::error(res.status()))
.status_value());
}
return zx::ok(fidl::WireSyncClient(std::move(data_sink_local)));
}
zx::result<> Fastboot::OemInitPartitionTables(const std::string& command, Transport* transport) {
auto data_sink_client = ConnectToDynamicDataSink(transport);
if (data_sink_client.is_error()) {
return zx::error(data_sink_client.status_value());
}
auto res = data_sink_client.value()->InitializePartitionTables();
auto status = res.ok() ? res.value().status : res.status();
if (status != ZX_OK) {
return SendResponse(ResponseType::kFail, "Failed to init partition table", transport,
zx::error(status));
}
return SendResponse(ResponseType::kOkay, "", transport);
}
zx::result<> Fastboot::OemWipePartitionTables(const std::string& command, Transport* transport) {
auto data_sink_client = ConnectToDynamicDataSink(transport);
if (data_sink_client.is_error()) {
return zx::error(data_sink_client.status_value());
}
auto res = data_sink_client.value()->WipePartitionTables();
auto status = res.ok() ? res.value().status : res.status();
if (status != ZX_OK) {
return SendResponse(ResponseType::kFail, "Failed to wipe partition table", transport,
zx::error(status));
}
return SendResponse(ResponseType::kOkay, "", transport);
}
} // namespace fastboot