blob: 40e753c5eb513c24a9dc8badd87730daddeff6ed [file] [log] [blame]
// Copyright 2019 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/storage/lib/paver/abr-client.h"
#include <endian.h>
#include <fuchsia/boot/llcpp/fidl.h>
#include <fuchsia/io/llcpp/fidl.h>
#include <lib/abr/abr.h>
#include <lib/cksum.h>
#include <lib/fdio/directory.h>
#include <stdio.h>
#include <string.h>
#include <zircon/errors.h>
#include <zircon/status.h>
#include <string_view>
#include "src/storage/lib/paver/device-partitioner.h"
#include "src/storage/lib/paver/partition-client.h"
#include "src/storage/lib/paver/pave-logging.h"
namespace abr {
using ::llcpp::fuchsia::paver::Asset;
using ::llcpp::fuchsia::paver::Configuration;
zx::status<Configuration> QueryBootConfig(
fidl::UnownedClientEnd<::llcpp::fuchsia::io::Directory> svc_root) {
auto endpoints = fidl::CreateEndpoints<::llcpp::fuchsia::boot::Arguments>();
if (endpoints.is_error()) {
return endpoints.take_error();
}
auto status = zx::make_status(fdio_service_connect_at(svc_root.channel(),
::llcpp::fuchsia::boot::Arguments::Name,
endpoints->server.channel().release()));
if (status.is_error()) {
return status.take_error();
}
auto client = fidl::BindSyncClient(std::move(endpoints->client));
auto result = client.GetString(::fidl::StringView{"zvb.current_slot"});
if (!result.ok()) {
return zx::error(result.status());
}
const auto response = result.Unwrap();
if (response->value.is_null()) {
ERROR("Kernel cmdline param zvb.current_slot not found!\n");
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
auto slot = std::string_view{response->value.data(), response->value.size()};
// Some bootloaders prefix slot with dash or underscore. We strip them for consistency.
slot.remove_prefix(std::min(slot.find_first_not_of("_-"), slot.size()));
if (slot.compare("a") == 0) {
return zx::ok(Configuration::A);
} else if (slot.compare("b") == 0) {
return zx::ok(Configuration::B);
} else if (slot.compare("r") == 0) {
return zx::ok(Configuration::RECOVERY);
}
ERROR("Invalid value `%.*s` found in zvb.current_slot!\n", static_cast<int>(slot.size()),
slot.data());
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
namespace {
zx::status<> SupportsVerifiedBoot(
fidl::UnownedClientEnd<::llcpp::fuchsia::io::Directory> svc_root) {
return zx::make_status(QueryBootConfig(svc_root).status_value());
}
} // namespace
zx::status<std::unique_ptr<abr::Client>> AbrPartitionClient::Create(
std::unique_ptr<paver::PartitionClient> partition) {
auto status = partition->GetBlockSize();
if (status.is_error()) {
return status.take_error();
}
size_t block_size = status.value();
zx::vmo vmo;
if (auto status =
zx::make_status(zx::vmo::create(fbl::round_up(block_size, ZX_PAGE_SIZE), 0, &vmo));
status.is_error()) {
return status.take_error();
}
if (auto status = partition->Read(vmo, block_size); status.is_error()) {
return status.take_error();
}
return zx::ok(new AbrPartitionClient(std::move(partition), std::move(vmo), block_size));
}
zx::status<> AbrPartitionClient::Read(uint8_t* buffer, size_t size) {
if (auto status = partition_->Read(vmo_, block_size_); status.is_error()) {
return status.take_error();
}
if (auto status = zx::make_status(vmo_.read(buffer, 0, size)); status.is_error()) {
return status.take_error();
}
return zx::ok();
}
zx::status<> AbrPartitionClient::Write(const uint8_t* buffer, size_t size) {
if (auto status = zx::make_status(vmo_.write(buffer, 0, size)); status.is_error()) {
return status.take_error();
}
if (auto status = partition_->Write(vmo_, block_size_); status.is_error()) {
return status.take_error();
}
return zx::ok();
}
std::vector<std::unique_ptr<ClientFactory>>* ClientFactory::registered_factory_list() {
static std::vector<std::unique_ptr<ClientFactory>>* registered_factory_list = nullptr;
if (registered_factory_list == nullptr) {
registered_factory_list = new std::vector<std::unique_ptr<ClientFactory>>();
}
return registered_factory_list;
}
zx::status<std::unique_ptr<abr::Client>> ClientFactory::Create(
fbl::unique_fd devfs_root, fidl::UnownedClientEnd<::llcpp::fuchsia::io::Directory> svc_root,
std::shared_ptr<paver::Context> context) {
if (auto status = SupportsVerifiedBoot(svc_root); status.is_error()) {
return status.take_error();
}
for (auto& factory : *registered_factory_list()) {
if (auto status = factory->New(devfs_root.duplicate(), svc_root, context); status.is_ok()) {
return status.take_value();
}
}
return zx::error(ZX_ERR_NOT_FOUND);
}
void ClientFactory::Register(std::unique_ptr<ClientFactory> factory) {
registered_factory_list()->push_back(std::move(factory));
}
bool Client::ReadAbrMetaData(void* context, size_t size, uint8_t* buffer) {
if (auto res = static_cast<Client*>(context)->Read(buffer, size); res.is_error()) {
ERROR("Failed to read abr data from storage. %s\n", res.status_string());
return false;
}
return true;
}
bool Client::WriteAbrMetaData(void* context, const uint8_t* buffer, size_t size) {
if (auto res = static_cast<Client*>(context)->Write(buffer, size); res.is_error()) {
ERROR("Failed to write abr data to storage. %s\n", res.status_string());
return false;
}
return true;
}
zx::status<> Client::AbrResultToZxStatus(AbrResult status) {
switch (status) {
case kAbrResultOk:
return zx::ok();
case kAbrResultErrorIo:
return zx::error(ZX_ERR_IO);
case kAbrResultErrorInvalidData:
return zx::error(ZX_ERR_INVALID_ARGS);
case kAbrResultErrorUnsupportedVersion:
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
ERROR("Unknown Abr result code %d!\n", status);
return zx::error(ZX_ERR_INTERNAL);
}
extern "C" uint32_t AbrCrc32(const void* buf, size_t buf_size) {
return crc32(0UL, reinterpret_cast<const uint8_t*>(buf), buf_size);
}
} // namespace abr