blob: dffb070f106be5c371fa26b17ff41dd76cfe80f3 [file] [log] [blame]
// Copyright 2018 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 "device-partitioner.h"
#include <lib/fdio/fd.h>
#include <lib/zx/status.h>
#include <zircon/errors.h>
#include <memory>
#include <unordered_map>
#include <utility>
#include <fbl/span.h>
#include <fbl/string.h>
#include <fbl/string_printf.h>
#include <gpt/gpt.h>
#include "src/lib/uuid/uuid.h"
#include "src/storage/lib/paver/pave-logging.h"
#include "src/storage/lib/paver/utils.h"
namespace paver {
using uuid::Uuid;
// Information for each Partition enum value.
struct PartitionInfo {
// Default on-disk name and type GUID. Device aren't required to use these,
// they may instead use the legacy values below or their own custom values.
const char* name;
Uuid type;
// Legacy on-disk name and type GUID.
const char* legacy_name;
Uuid legacy_type;
// Device-agnostic debug name. Something we can print to the logs that will
// make sense for any device regardless of on-disk partition layout.
const char* debug_name;
};
const PartitionInfo* GetPartitionInfo(Partition partition) {
static std::unordered_map<Partition, PartitionInfo> map = {
// TODO(fxbug.dev/52708): add support for bootloader A/B/R once we have devices
// ready for it. For now just default to bootloader_a.
{Partition::kBootloaderA, PartitionInfo{.name = GPT_BOOTLOADER_A_NAME,
.type = GPT_BOOTLOADER_ABR_TYPE_GUID,
.legacy_name = GUID_EFI_NAME,
.legacy_type = GUID_BOOTLOADER_VALUE,
.debug_name = "Bootloader A"}},
{Partition::kBootloaderB, PartitionInfo{.name = GPT_BOOTLOADER_B_NAME,
.type = GPT_BOOTLOADER_ABR_TYPE_GUID,
.legacy_name = GUID_EFI_NAME,
.legacy_type = GUID_BOOTLOADER_VALUE,
.debug_name = "Bootloader B"}},
{Partition::kBootloaderR, PartitionInfo{.name = GPT_BOOTLOADER_R_NAME,
.type = GPT_BOOTLOADER_ABR_TYPE_GUID,
.legacy_name = GUID_EFI_NAME,
.legacy_type = GUID_BOOTLOADER_VALUE,
.debug_name = "Bootloader R"}},
{Partition::kZirconA, PartitionInfo{.name = GPT_ZIRCON_A_NAME,
.type = GPT_ZIRCON_ABR_TYPE_GUID,
.legacy_name = GUID_ZIRCON_A_NAME,
.legacy_type = GUID_ZIRCON_A_VALUE,
.debug_name = "Zircon A"}},
{Partition::kZirconB, PartitionInfo{.name = GPT_ZIRCON_B_NAME,
.type = GPT_ZIRCON_ABR_TYPE_GUID,
.legacy_name = GUID_ZIRCON_B_NAME,
.legacy_type = GUID_ZIRCON_B_VALUE,
.debug_name = "Zircon B"}},
{Partition::kZirconR, PartitionInfo{.name = GPT_ZIRCON_R_NAME,
.type = GPT_ZIRCON_ABR_TYPE_GUID,
.legacy_name = GUID_ZIRCON_R_NAME,
.legacy_type = GUID_ZIRCON_R_VALUE,
.debug_name = "Zircon R"}},
{Partition::kVbMetaA, PartitionInfo{.name = GPT_VBMETA_A_NAME,
.type = GPT_VBMETA_ABR_TYPE_GUID,
.legacy_name = GUID_VBMETA_A_NAME,
.legacy_type = GUID_VBMETA_A_VALUE,
.debug_name = "VBMeta A"}},
{Partition::kVbMetaB, PartitionInfo{.name = GPT_VBMETA_B_NAME,
.type = GPT_VBMETA_ABR_TYPE_GUID,
.legacy_name = GUID_VBMETA_B_NAME,
.legacy_type = GUID_VBMETA_B_VALUE,
.debug_name = "VBMeta B"}},
{Partition::kVbMetaR, PartitionInfo{.name = GPT_VBMETA_R_NAME,
.type = GPT_VBMETA_ABR_TYPE_GUID,
.legacy_name = GUID_VBMETA_R_NAME,
.legacy_type = GUID_VBMETA_R_VALUE,
.debug_name = "VBMeta R"}},
{Partition::kAbrMeta, PartitionInfo{.name = GPT_DURABLE_BOOT_NAME,
.type = GPT_DURABLE_BOOT_TYPE_GUID,
.legacy_name = GUID_ABR_META_NAME,
.legacy_type = GUID_ABR_META_VALUE,
.debug_name = "A/B/R Metadata"}},
{Partition::kFuchsiaVolumeManager, PartitionInfo{.name = GPT_FVM_NAME,
.type = GPT_FVM_TYPE_GUID,
.legacy_name = GUID_FVM_NAME,
.legacy_type = GUID_FVM_VALUE,
.debug_name = "FVM"}},
};
auto iter = map.find(partition);
if (iter == map.end()) {
return nullptr;
}
return &iter->second;
}
const char* PartitionName(Partition partition, PartitionScheme scheme) {
const PartitionInfo* info = GetPartitionInfo(partition);
if (info) {
return scheme == PartitionScheme::kNew ? info->name : info->legacy_name;
}
return "Unknown";
}
// Returns the given partition's type GUID.
zx::status<Uuid> PartitionTypeUuid(Partition partition, PartitionScheme scheme) {
const PartitionInfo* info = GetPartitionInfo(partition);
if (info) {
return zx::ok(scheme == PartitionScheme::kNew ? info->type : info->legacy_type);
}
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
fbl::String PartitionSpec::ToString() const {
const char* debug_name = "<Unknown Partition>";
const PartitionInfo* info = GetPartitionInfo(partition);
if (info) {
debug_name = info->debug_name;
}
if (content_type.empty()) {
return debug_name;
}
return fbl::StringPrintf("%s (%.*s)", debug_name, static_cast<int>(content_type.size()),
content_type.data());
}
std::vector<std::unique_ptr<DevicePartitionerFactory>>*
DevicePartitionerFactory::registered_factory_list() {
static std::vector<std::unique_ptr<DevicePartitionerFactory>>* registered_factory_list = nullptr;
if (registered_factory_list == nullptr) {
registered_factory_list = new std::vector<std::unique_ptr<DevicePartitionerFactory>>();
}
return registered_factory_list;
}
std::unique_ptr<DevicePartitioner> DevicePartitionerFactory::Create(
fbl::unique_fd devfs_root, fidl::UnownedClientEnd<::llcpp::fuchsia::io::Directory> svc_root,
Arch arch, std::shared_ptr<Context> context, zx::channel block_device) {
fbl::unique_fd block_dev;
if (block_device) {
int fd;
zx_status_t status = fdio_fd_create(block_device.release(), &fd);
if (status != ZX_OK) {
ERROR(
"Unable to create fd from block_device channel. Does it implement fuchsia.io.Node?: %s\n",
zx_status_get_string(status));
return nullptr;
}
block_dev.reset(fd);
}
for (auto& factory : *registered_factory_list()) {
if (auto status = factory->New(devfs_root.duplicate(), svc_root, arch, context, block_dev);
status.is_ok()) {
return std::move(status.value());
}
}
return nullptr;
}
void DevicePartitionerFactory::Register(std::unique_ptr<DevicePartitionerFactory> factory) {
registered_factory_list()->push_back(std::move(factory));
}
/*====================================================*
* FIXED PARTITION MAP *
*====================================================*/
constexpr PartitionScheme kFixedDevicePartitionScheme = PartitionScheme::kLegacy;
zx::status<std::unique_ptr<DevicePartitioner>> FixedDevicePartitioner::Initialize(
fbl::unique_fd devfs_root) {
if (HasSkipBlockDevice(devfs_root)) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
LOG("Successfully initialized FixedDevicePartitioner Device Partitioner\n");
return zx::ok(new FixedDevicePartitioner(std::move(devfs_root)));
}
bool FixedDevicePartitioner::SupportsPartition(const PartitionSpec& spec) const {
const PartitionSpec supported_specs[] = {PartitionSpec(paver::Partition::kBootloaderA),
PartitionSpec(paver::Partition::kZirconA),
PartitionSpec(paver::Partition::kZirconB),
PartitionSpec(paver::Partition::kZirconR),
PartitionSpec(paver::Partition::kVbMetaA),
PartitionSpec(paver::Partition::kVbMetaB),
PartitionSpec(paver::Partition::kVbMetaR),
PartitionSpec(paver::Partition::kAbrMeta),
PartitionSpec(paver::Partition::kFuchsiaVolumeManager)};
for (const auto& supported : supported_specs) {
if (SpecMatches(spec, supported)) {
return true;
}
}
return false;
}
zx::status<std::unique_ptr<PartitionClient>> FixedDevicePartitioner::AddPartition(
const PartitionSpec& spec) const {
ERROR("Cannot add partitions to a fixed-map partition device\n");
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx::status<std::unique_ptr<PartitionClient>> FixedDevicePartitioner::FindPartition(
const PartitionSpec& spec) const {
if (!SupportsPartition(spec)) {
ERROR("Unsupported partition %s\n", spec.ToString().c_str());
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx::status<Uuid> type_or = PartitionTypeUuid(spec.partition, kFixedDevicePartitionScheme);
if (type_or.is_error()) {
ERROR("partition_type is invalid!\n");
return type_or.take_error();
}
Uuid type = type_or.value();
auto partition = OpenBlockPartition(devfs_root_, std::nullopt, type, ZX_SEC(5));
if (partition.is_error()) {
return partition.take_error();
}
return zx::ok(new BlockPartitionClient(std::move(partition.value())));
}
zx::status<> FixedDevicePartitioner::WipeFvm() const {
if (auto status = WipeBlockPartition(devfs_root_, std::nullopt, Uuid(GUID_FVM_VALUE));
status.is_error()) {
ERROR("Failed to wipe FVM.\n");
} else {
LOG("Wiped FVM successfully.\n");
}
LOG("Immediate reboot strongly recommended\n");
return zx::ok();
}
zx::status<> FixedDevicePartitioner::InitPartitionTables() const {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx::status<> FixedDevicePartitioner::WipePartitionTables() const {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx::status<> FixedDevicePartitioner::ValidatePayload(const PartitionSpec& spec,
fbl::Span<const uint8_t> data) const {
if (!SupportsPartition(spec)) {
ERROR("Unsupported partition %s\n", spec.ToString().c_str());
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
return zx::ok();
}
zx::status<std::unique_ptr<DevicePartitioner>> DefaultPartitionerFactory::New(
fbl::unique_fd devfs_root, fidl::UnownedClientEnd<::llcpp::fuchsia::io::Directory> svc_root,
Arch arch, std::shared_ptr<Context> context, const fbl::unique_fd& block_device) {
return FixedDevicePartitioner::Initialize(std::move(devfs_root));
}
} // namespace paver