| // 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/stdcompat/span.h> |
| #include <lib/zx/result.h> |
| #include <zircon/errors.h> |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <memory> |
| #include <unordered_map> |
| #include <utility> |
| |
| #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 = { |
| {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::result<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; |
| } |
| |
| zx::result<std::unique_ptr<DevicePartitioner>> DevicePartitionerFactory::Create( |
| fbl::unique_fd devfs_root, fidl::UnownedClientEnd<fuchsia_io::Directory> svc_root, Arch arch, |
| std::shared_ptr<Context> context, BlockAndController block_device) { |
| for (auto& factory : *registered_factory_list()) { |
| fidl::ClientEnd<fuchsia_device::Controller> controller; |
| if (block_device.controller) { |
| auto [controller_client, controller_server] = |
| fidl::Endpoints<fuchsia_device::Controller>::Create(); |
| if (zx_status_t status = fidl::WireCall(block_device.controller) |
| ->ConnectToController(std::move(controller_server)) |
| .status(); |
| status != ZX_OK) { |
| return zx::error(status); |
| } |
| controller = std::move(controller_client); |
| } |
| if (auto status = factory->New(devfs_root.duplicate(), svc_root, arch, std::move(context), |
| std::move(controller)); |
| status.is_ok()) { |
| return zx::ok(std::move(status.value())); |
| } |
| } |
| return zx::error(ZX_ERR_NOT_FOUND); |
| } |
| |
| 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::result<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)}; |
| return std::any_of(std::cbegin(supported_specs), std::cend(supported_specs), |
| [&](const PartitionSpec& supported) { return SpecMatches(spec, supported); }); |
| } |
| |
| zx::result<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::result<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::result<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(); |
| |
| zx::result 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->controller), |
| fidl::ClientEnd<fuchsia_hardware_block::Block>(std::move(partition->device)))); |
| } |
| |
| zx::result<> 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::result<> FixedDevicePartitioner::InitPartitionTables() const { |
| return zx::error(ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| zx::result<> FixedDevicePartitioner::WipePartitionTables() const { |
| return zx::error(ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| zx::result<> FixedDevicePartitioner::ValidatePayload(const PartitionSpec& spec, |
| cpp20::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::result<std::unique_ptr<DevicePartitioner>> DefaultPartitionerFactory::New( |
| fbl::unique_fd devfs_root, fidl::UnownedClientEnd<fuchsia_io::Directory> svc_root, Arch arch, |
| std::shared_ptr<Context> context, fidl::ClientEnd<fuchsia_device::Controller> block_device) { |
| return FixedDevicePartitioner::Initialize(std::move(devfs_root)); |
| } |
| |
| } // namespace paver |