blob: db7b13c08ceb37aed5da8442f2d582735f071a98 [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 "abr-client.h"
#include <endian.h>
#include <fuchsia/boot/llcpp/fidl.h>
#include <lib/cksum.h>
#include <lib/fdio/directory.h>
#include <stdio.h>
#include <string.h>
#include <zircon/status.h>
#include <libabr/libabr.h>
#include <string_view>
#include "device-partitioner.h"
#include "partition-client.h"
#include "pave-logging.h"
#include "zircon/errors.h"
namespace abr {
namespace {
using ::llcpp::fuchsia::paver::Asset;
using ::llcpp::fuchsia::paver::Configuration;
zx_status_t QueryBootConfig(const zx::channel& svc_root, Configuration* out) {
zx::channel local, remote;
if (zx_status_t status = zx::channel::create(0, &local, &remote); status != ZX_OK) {
return status;
}
auto status = fdio_service_connect_at(svc_root.get(), ::llcpp::fuchsia::boot::Arguments::Name,
remote.release());
if (status != ZX_OK) {
return status;
}
::llcpp::fuchsia::boot::Arguments::SyncClient client(std::move(local));
auto result = client.GetString(::fidl::StringView{"zvb.current_slot"});
if (!result.ok()) {
return result.status();
}
const auto response = result.Unwrap();
if (response->value.is_null()) {
ERROR("Kernel cmdline param zvb.current_slot not found!\n");
return 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) {
*out = Configuration::A;
} else if (slot.compare("b") == 0) {
*out = Configuration::B;
} else if (slot.compare("r") == 0) {
*out = Configuration::RECOVERY;
} else {
ERROR("Invalid value `%.*s` found in zvb.current_slot!\n", static_cast<int>(slot.size()),
slot.data());
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t SupportsVerifiedBoot(const zx::channel& svc_root) {
Configuration config;
if (zx_status_t status = QueryBootConfig(svc_root, &config); status != ZX_OK) {
return status;
}
return ZX_OK;
}
// Implementation of abr::Client which works with a contiguous partition storing AbrData.
class PartitionClient : public Client {
public:
// |partition| should contain AbrData with no offset.
static zx_status_t Create(std::unique_ptr<paver::PartitionClient> partition,
std::unique_ptr<abr::Client>* out);
private:
PartitionClient(std::unique_ptr<paver::PartitionClient> partition, zx::vmo vmo, size_t block_size)
: partition_(std::move(partition)), vmo_(std::move(vmo)), block_size_(block_size) {}
std::unique_ptr<paver::PartitionClient> partition_;
zx::vmo vmo_;
size_t block_size_;
zx_status_t Read(uint8_t* buffer, size_t size) override;
zx_status_t Write(const uint8_t* buffer, size_t size) override;
};
zx_status_t PartitionClient::Create(std::unique_ptr<paver::PartitionClient> partition,
std::unique_ptr<abr::Client>* out) {
size_t block_size;
if (zx_status_t status = partition->GetBlockSize(&block_size); status != ZX_OK) {
return status;
}
zx::vmo vmo;
if (zx_status_t status = zx::vmo::create(fbl::round_up(block_size, ZX_PAGE_SIZE), 0, &vmo);
status != ZX_OK) {
return status;
}
if (zx_status_t status = partition->Read(vmo, block_size); status != ZX_OK) {
return status;
}
out->reset(new PartitionClient(std::move(partition), std::move(vmo), block_size));
return ZX_OK;
}
zx_status_t PartitionClient::Read(uint8_t* buffer, size_t size) {
if (zx_status_t status = partition_->Read(vmo_, block_size_); status != ZX_OK) {
return status;
}
if (zx_status_t status = vmo_.read(buffer, 0, size); status != ZX_OK) {
return status;
}
return ZX_OK;
}
zx_status_t PartitionClient::Write(const uint8_t* buffer, size_t size) {
if (zx_status_t status = vmo_.write(buffer, 0, size); status != ZX_OK) {
return status;
}
if (zx_status_t status = partition_->Write(vmo_, block_size_); status != ZX_OK) {
return status;
}
if (zx_status_t status = partition_->Flush(); status != ZX_OK) {
return status;
}
return ZX_OK;
}
} // namespace
zx_status_t Client::Create(fbl::unique_fd devfs_root, const zx::channel& svc_root,
std::unique_ptr<abr::Client>* out) {
if (zx_status_t status = SupportsVerifiedBoot(svc_root); status != ZX_OK) {
return status;
}
if (AstroClient::Create(devfs_root.duplicate(), out) == ZX_OK ||
SherlockClient::Create(std::move(devfs_root), out) == ZX_OK) {
(*out)->abr_ops_ = {out->get(), Client::ReadAbrMetaData, Client::WriteAbrMetaData};
return ZX_OK;
}
return ZX_ERR_NOT_FOUND;
}
bool Client::ReadAbrMetaData(void* context, size_t size, uint8_t* buffer) {
if (auto res = static_cast<Client*>(context)->Read(buffer, size); res != ZX_OK) {
ERROR("Failed to read abr data from storage. %s\n", zx_status_get_string(res));
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 != ZX_OK) {
ERROR("Failed to write abr data to storage. %s\n", zx_status_get_string(res));
return false;
}
return true;
}
zx_status_t Client::AbrResultToZxStatus(AbrResult status) {
switch (status) {
case kAbrResultOk:
return ZX_OK;
case kAbrResultErrorIo:
return ZX_ERR_IO;
case kAbrResultErrorInvalidData:
return ZX_ERR_INVALID_ARGS;
case kAbrResultErrorUnsupportedVersion:
return ZX_ERR_NOT_SUPPORTED;
}
ERROR("Unknown Abr result code %d!\n", status);
return ZX_ERR_INTERNAL;
}
zx_status_t AstroClient::Create(fbl::unique_fd devfs_root, std::unique_ptr<abr::Client>* out) {
std::unique_ptr<paver::DevicePartitioner> partitioner;
zx_status_t status = paver::AstroPartitioner::Initialize(std::move(devfs_root), &partitioner);
if (status != ZX_OK) {
return status;
}
// ABR metadata has no need of a content type since it's always local rather
// than provided in an update package, so just use the default content type.
std::unique_ptr<paver::PartitionClient> partition;
if (zx_status_t status =
partitioner->FindPartition(paver::PartitionSpec(paver::Partition::kAbrMeta), &partition);
status != ZX_OK) {
return status;
}
return PartitionClient::Create(std::move(partition), out);
}
zx_status_t SherlockClient::Create(fbl::unique_fd devfs_root, std::unique_ptr<abr::Client>* out) {
std::unique_ptr<paver::DevicePartitioner> partitioner;
zx_status_t status =
paver::SherlockPartitioner::Initialize(std::move(devfs_root), std::nullopt, &partitioner);
if (status != ZX_OK) {
return status;
}
// ABR metadata has no need of a content type since it's always local rather
// than provided in an update package, so just use the default content type.
std::unique_ptr<paver::PartitionClient> partition;
if (zx_status_t status =
partitioner->FindPartition(paver::PartitionSpec(paver::Partition::kAbrMeta), &partition);
status != ZX_OK) {
return status;
}
return PartitionClient::Create(std::move(partition), out);
}
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