blob: 97268d6c6a70e16f4daa85e7ab75335b30504376 [file] [log] [blame]
// Copyright 2020 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/x64.h"
#include "src/lib/uuid/uuid.h"
#include "src/storage/lib/paver/pave-logging.h"
#include "src/storage/lib/paver/utils.h"
#include "src/storage/lib/paver/validation.h"
namespace paver {
namespace {
using uuid::Uuid;
constexpr size_t kKibibyte = 1024;
constexpr size_t kMebibyte = kKibibyte * 1024;
constexpr size_t kGibibyte = kMebibyte * 1024;
// All X64 boards currently use the legacy partition scheme.
constexpr PartitionScheme kPartitionScheme = PartitionScheme::kLegacy;
// TODO: Remove support after July 9th 2021.
constexpr char kOldEfiName[] = "efi-system";
} // namespace
zx::status<std::unique_ptr<DevicePartitioner>> EfiDevicePartitioner::Initialize(
fbl::unique_fd devfs_root, fidl::UnownedClientEnd<::llcpp::fuchsia::io::Directory> svc_root,
Arch arch, const fbl::unique_fd& block_device) {
if (arch != Arch::kX64) {
return zx::error(ZX_ERR_NOT_FOUND);
}
auto status = GptDevicePartitioner::InitializeGpt(std::move(devfs_root), svc_root, block_device);
if (status.is_error()) {
return status.take_error();
}
auto partitioner = WrapUnique(new EfiDevicePartitioner(arch, std::move(status->gpt)));
if (status->initialize_partition_tables) {
if (auto status = partitioner->InitPartitionTables(); status.is_error()) {
return status.take_error();
}
}
LOG("Successfully initialized EFI Device Partitioner\n");
return zx::ok(std::move(partitioner));
}
bool EfiDevicePartitioner::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>> EfiDevicePartitioner::AddPartition(
const PartitionSpec& spec) const {
if (!SupportsPartition(spec)) {
ERROR("Unsupported partition %s\n", spec.ToString().c_str());
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
// NOTE: If you update the minimum sizes of partitions, please update the
// EfiDevicePartitionerTests.InitPartitionTables test.
size_t minimum_size_bytes = 0;
switch (spec.partition) {
case Partition::kBootloaderA:
minimum_size_bytes = 16 * kMebibyte;
break;
case Partition::kZirconA:
minimum_size_bytes = 128 * kMebibyte;
break;
case Partition::kZirconB:
minimum_size_bytes = 128 * kMebibyte;
break;
case Partition::kZirconR:
minimum_size_bytes = 192 * kMebibyte;
break;
case Partition::kVbMetaA:
minimum_size_bytes = 64 * kKibibyte;
break;
case Partition::kVbMetaB:
minimum_size_bytes = 64 * kKibibyte;
break;
case Partition::kVbMetaR:
minimum_size_bytes = 64 * kKibibyte;
break;
case Partition::kAbrMeta:
minimum_size_bytes = 4 * kKibibyte;
break;
case Partition::kFuchsiaVolumeManager:
minimum_size_bytes = 16 * kGibibyte;
break;
default:
ERROR("EFI partitioner cannot add unknown partition type\n");
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
const char* name = PartitionName(spec.partition, kPartitionScheme);
auto type = GptPartitionType(spec.partition);
if (type.is_error()) {
return type.take_error();
}
return gpt_->AddPartition(name, type.value(), minimum_size_bytes, /*optional_reserve_bytes*/ 0);
}
zx::status<std::unique_ptr<PartitionClient>> EfiDevicePartitioner::FindPartition(
const PartitionSpec& spec) const {
if (!SupportsPartition(spec)) {
ERROR("Unsupported partition %s\n", spec.ToString().c_str());
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
switch (spec.partition) {
case Partition::kBootloaderA: {
const auto filter = [](const gpt_partition_t& part) {
return FilterByTypeAndName(part, GUID_EFI_VALUE, GUID_EFI_NAME) ||
// TODO: Remove support after July 9th 2021.
FilterByTypeAndName(part, GUID_EFI_VALUE, kOldEfiName);
};
auto status = gpt_->FindPartition(filter);
if (status.is_error()) {
return status.take_error();
}
return zx::ok(std::move(status->partition));
}
case Partition::kZirconA:
case Partition::kZirconB:
case Partition::kZirconR:
case Partition::kVbMetaA:
case Partition::kVbMetaB:
case Partition::kVbMetaR:
case Partition::kAbrMeta: {
const auto filter = [&spec](const gpt_partition_t& part) {
auto status = GptPartitionType(spec.partition);
return status.is_ok() && FilterByType(part, status.value());
};
auto status = gpt_->FindPartition(filter);
if (status.is_error()) {
return status.take_error();
}
return zx::ok(std::move(status->partition));
}
case Partition::kFuchsiaVolumeManager: {
auto status = gpt_->FindPartition(IsFvmPartition);
if (status.is_error()) {
return status.take_error();
}
return zx::ok(std::move(status->partition));
}
default:
ERROR("EFI partitioner cannot find unknown partition type\n");
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
}
zx::status<> EfiDevicePartitioner::FinalizePartition(const PartitionSpec& spec) const {
if (!SupportsPartition(spec)) {
ERROR("Unsupported partition %s\n", spec.ToString().c_str());
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
return zx::make_status(gpt_->GetGpt()->Sync());
}
zx::status<> EfiDevicePartitioner::WipeFvm() const { return gpt_->WipeFvm(); }
zx::status<> EfiDevicePartitioner::InitPartitionTables() const {
const std::array<Partition, 9> partitions_to_add{
Partition::kBootloaderA, Partition::kZirconA, Partition::kZirconB,
Partition::kZirconR, Partition::kVbMetaA, Partition::kVbMetaB,
Partition::kVbMetaR, Partition::kAbrMeta, Partition::kFuchsiaVolumeManager,
};
// Wipe partitions.
// EfiDevicePartitioner operates on partition types.
auto status = gpt_->WipePartitions([&partitions_to_add](const gpt_partition_t& part) {
for (auto& partition : partitions_to_add) {
// Get the partition type GUID, and compare it.
auto status = GptPartitionType(partition);
if (status.is_error() || status.value() != Uuid(part.type)) {
continue;
}
// If we are wiping any non-bootloader partition, we are done.
if (partition != Partition::kBootloaderA) {
return true;
}
// If we are wiping the bootloader partition, only do so if it is the
// Fuchsia-installed bootloader partition. This is to allow dual-booting.
char cstring_name[GPT_NAME_LEN] = {};
utf16_to_cstring(cstring_name, part.name, GPT_NAME_LEN);
if (strncasecmp(cstring_name, GUID_EFI_NAME, GPT_NAME_LEN) == 0) {
return true;
}
// Support the old name.
// TODO: Remove support after July 9th 2021.
if (strncasecmp(cstring_name, kOldEfiName, GPT_NAME_LEN) == 0) {
return true;
}
}
return false;
});
if (status.is_error()) {
ERROR("Failed to wipe partitions: %s\n", status.status_string());
return status.take_error();
}
// Add partitions with default content_type.
for (auto type : partitions_to_add) {
auto status = AddPartition(PartitionSpec(type));
if (status.status_value() == ZX_ERR_ALREADY_BOUND) {
ERROR("Warning: Skipping existing partition \"%s\"\n", PartitionName(type, kPartitionScheme));
} else if (status.is_error()) {
ERROR("Failed to create partition \"%s\": %s\n", PartitionName(type, kPartitionScheme),
status.status_string());
return status.take_error();
}
}
LOG("Successfully initialized GPT\n");
return zx::ok();
} // namespace paver
zx::status<> EfiDevicePartitioner::WipePartitionTables() const {
return gpt_->WipePartitionTables();
}
zx::status<> EfiDevicePartitioner::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);
}
if (IsZirconPartitionSpec(spec)) {
if (!IsValidKernelZbi(arch_, data)) {
return zx::error(ZX_ERR_BAD_STATE);
}
}
return zx::ok();
}
zx::status<std::unique_ptr<DevicePartitioner>> X64PartitionerFactory::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 EfiDevicePartitioner::Initialize(std::move(devfs_root), svc_root, arch, block_device);
}
zx::status<std::unique_ptr<abr::Client>> X64AbrClientFactory::New(
fbl::unique_fd devfs_root, fidl::UnownedClientEnd<::llcpp::fuchsia::io::Directory> svc_root,
std::shared_ptr<paver::Context> context) {
fbl::unique_fd none;
auto partitioner = EfiDevicePartitioner::Initialize(std::move(devfs_root), std::move(svc_root),
paver::Arch::kX64, none);
if (partitioner.is_error()) {
return partitioner.take_error();
}
// 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.
auto partition = partitioner->FindPartition(paver::PartitionSpec(paver::Partition::kAbrMeta));
if (partition.is_error()) {
return partition.take_error();
}
return abr::AbrPartitionClient::Create(std::move(partition.value()));
}
} // namespace paver