blob: 5bd26b2a5fb9470ddd8d3c39cf56acbc4b822936 [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 "src/storage/lib/paver/device-partitioner.h"
#include <dirent.h>
#include <fcntl.h>
#include <fidl/fuchsia.device/cpp/wire.h>
#include <fidl/fuchsia.fshost/cpp/wire_test_base.h>
#include <fidl/fuchsia.hardware.block/cpp/wire.h>
#include <fidl/fuchsia.hardware.power.statecontrol/cpp/wire.h>
#include <fidl/fuchsia.kernel/cpp/wire.h>
#include <fidl/fuchsia.scheduler/cpp/wire.h>
#include <fidl/fuchsia.system.state/cpp/common_types.h>
#include <fidl/fuchsia.system.state/cpp/fidl.h>
#include <fidl/fuchsia.system.state/cpp/markers.h>
#include <fidl/fuchsia.system.state/cpp/wire.h>
#include <fidl/fuchsia.tracing.provider/cpp/wire.h>
#include <lib/abr/util.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/default.h>
#include <lib/component/incoming/cpp/directory.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/component/incoming/cpp/service.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/driver-integration-test/fixture.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/fzl/owned-vmo-mapper.h>
#include <lib/sys/cpp/testing/component_context_provider.h>
#include <lib/sys/cpp/testing/service_directory_provider.h>
#include <lib/zbi-format/zbi.h>
#include <lib/zx/result.h>
#include <lib/zx/time.h>
#include <unistd.h>
#include <zircon/errors.h>
#include <zircon/hw/gpt.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include <array>
#include <format>
#include <memory>
#include <span>
#include <string_view>
#include <utility>
#include <fbl/unique_fd.h>
#include <gpt/gpt.h>
#include <soc/aml-common/aml-guid.h>
#include <zxtest/zxtest.h>
#include "src/lib/files/directory.h"
#include "src/lib/uuid/uuid.h"
#include "src/storage/lib/block_client/cpp/fake_block_device.h"
#include "src/storage/lib/block_client/cpp/remote_block_device.h"
#include "src/storage/lib/paver/android.h"
#include "src/storage/lib/paver/luis.h"
#include "src/storage/lib/paver/moonflower.h"
#include "src/storage/lib/paver/nelson.h"
#include "src/storage/lib/paver/sherlock.h"
#include "src/storage/lib/paver/system_shutdown_state.h"
#include "src/storage/lib/paver/test/test-utils.h"
#include "src/storage/lib/paver/uefi.h"
#include "src/storage/lib/paver/utils.h"
namespace paver {
extern zx_duration_t g_wipe_timeout;
}
namespace {
constexpr fidl::UnownedClientEnd<fuchsia_io::Directory> kInvalidSvcRoot =
fidl::UnownedClientEnd<fuchsia_io::Directory>(ZX_HANDLE_INVALID);
constexpr uint64_t kMebibyte{UINT64_C(1024) * 1024};
constexpr uint64_t kGibibyte{kMebibyte * 1024};
using device_watcher::RecursiveWaitForFile;
using driver_integration_test::IsolatedDevmgr;
using fuchsia_system_state::SystemPowerState;
using fuchsia_system_state::SystemStateTransition;
using paver::PartitionSpec;
using uuid::Uuid;
namespace fio = fuchsia_io;
// New Type GUID's
constexpr uint8_t kDurableBootType[GPT_GUID_LEN] = GPT_DURABLE_BOOT_TYPE_GUID;
constexpr uint8_t kVbMetaType[GPT_GUID_LEN] = GPT_VBMETA_ABR_TYPE_GUID;
constexpr uint8_t kZirconType[GPT_GUID_LEN] = GPT_ZIRCON_ABR_TYPE_GUID;
constexpr uint8_t kNewFvmType[GPT_GUID_LEN] = GPT_FVM_TYPE_GUID;
// Legacy Type GUID's
constexpr uint8_t kBootloaderType[GPT_GUID_LEN] = GUID_BOOTLOADER_VALUE;
constexpr uint8_t kEfiType[GPT_GUID_LEN] = GUID_EFI_VALUE;
constexpr uint8_t kZirconAType[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE;
constexpr uint8_t kZirconBType[GPT_GUID_LEN] = GUID_ZIRCON_B_VALUE;
constexpr uint8_t kZirconRType[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE;
constexpr uint8_t kVbMetaAType[GPT_GUID_LEN] = GUID_VBMETA_A_VALUE;
constexpr uint8_t kVbMetaBType[GPT_GUID_LEN] = GUID_VBMETA_B_VALUE;
constexpr uint8_t kVbMetaRType[GPT_GUID_LEN] = GUID_VBMETA_R_VALUE;
constexpr uint8_t kFvmType[GPT_GUID_LEN] = GUID_FVM_VALUE;
constexpr uint8_t kEmptyType[GPT_GUID_LEN] = GUID_EMPTY_VALUE;
constexpr uint8_t kAbrMetaType[GPT_GUID_LEN] = GUID_ABR_META_VALUE;
constexpr uint8_t kStateLinuxGuid[GPT_GUID_LEN] = GUID_LINUX_FILESYSTEM_DATA_VALUE;
constexpr uint8_t kBoot0Type[GPT_GUID_LEN] = GUID_EMMC_BOOT1_VALUE;
constexpr uint8_t kBoot1Type[GPT_GUID_LEN] = GUID_EMMC_BOOT2_VALUE;
constexpr uint8_t kDummyType[GPT_GUID_LEN] = {0xaf, 0x3d, 0xc6, 0x0f, 0x83, 0x84, 0x72, 0x47,
0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4};
struct PartitionDescription {
std::string name;
uuid::Uuid type;
uint64_t start;
uint64_t length;
};
TEST(PartitionName, Bootloader) {
EXPECT_STREQ(PartitionName(paver::Partition::kBootloaderA, paver::PartitionScheme::kNew),
GPT_BOOTLOADER_A_NAME);
EXPECT_STREQ(PartitionName(paver::Partition::kBootloaderB, paver::PartitionScheme::kNew),
GPT_BOOTLOADER_B_NAME);
EXPECT_STREQ(PartitionName(paver::Partition::kBootloaderR, paver::PartitionScheme::kNew),
GPT_BOOTLOADER_R_NAME);
EXPECT_STREQ(PartitionName(paver::Partition::kBootloaderA, paver::PartitionScheme::kLegacy),
GUID_EFI_NAME);
EXPECT_STREQ(PartitionName(paver::Partition::kBootloaderB, paver::PartitionScheme::kLegacy),
GUID_EFI_NAME);
EXPECT_STREQ(PartitionName(paver::Partition::kBootloaderR, paver::PartitionScheme::kLegacy),
GUID_EFI_NAME);
}
TEST(PartitionName, AbrMetadata) {
EXPECT_STREQ(PartitionName(paver::Partition::kAbrMeta, paver::PartitionScheme::kNew),
GPT_DURABLE_BOOT_NAME);
EXPECT_STREQ(PartitionName(paver::Partition::kAbrMeta, paver::PartitionScheme::kLegacy),
GUID_ABR_META_NAME);
}
TEST(PartitionSpec, ToStringDefaultContentType) {
// This is a bit of a change-detector test since we don't actually care about
// the string value, but it's the cleanest way to check that the string is
// 1) non-empty and 2) doesn't contain a type suffix.
EXPECT_EQ(PartitionSpec(paver::Partition::kZirconA).ToString(), "Zircon A");
EXPECT_EQ(PartitionSpec(paver::Partition::kVbMetaB).ToString(), "VBMeta B");
}
TEST(PartitionSpec, ToStringWithContentType) {
EXPECT_EQ(PartitionSpec(paver::Partition::kZirconA, "foo").ToString(), "Zircon A (foo)");
EXPECT_EQ(PartitionSpec(paver::Partition::kVbMetaB, "a b c").ToString(), "VBMeta B (a b c)");
}
class GptDevicePartitionerTests : public PaverTest {
protected:
explicit GptDevicePartitionerTests(fbl::String board_name = fbl::String(),
uint32_t block_size = 512, std::string slot_suffix = "")
: board_name_(std::move(board_name)),
slot_suffix_(std::move(slot_suffix)),
block_size_(block_size) {}
void SetUp() override {
PaverTest::SetUp();
num_devices_ = 0;
paver::g_wipe_timeout = 0;
IsolatedDevmgr::Args args = BaseDevmgrArgs();
args.board_name = board_name_;
if (!slot_suffix_.empty()) {
args.fake_boot_args = std::make_unique<FakeBootArgs>(slot_suffix_);
}
ASSERT_OK(IsolatedDevmgr::Create(&args, &devmgr_));
ASSERT_OK(RecursiveWaitForFile(devmgr_.devfs_root().get(), "sys/platform/ram-disk/ramctl")
.status_value());
ASSERT_OK(RecursiveWaitForFile(devmgr_.devfs_root().get(), "sys/platform").status_value());
}
virtual IsolatedDevmgr::Args BaseDevmgrArgs() {
IsolatedDevmgr::Args args;
args.disable_block_watcher = false;
return args;
}
zx::result<paver::BlockDevices> CreateBlockDevices() {
return BaseDevmgrArgs().enable_storage_host
? paver::BlockDevices::CreateFromFshostBlockDir(BlockDirFd())
: paver::BlockDevices::CreateDevfs(devmgr_.devfs_root().duplicate());
}
fidl::ClientEnd<fuchsia_io::Directory> RealmExposedDir() { return devmgr_.RealmExposedDir(); }
fbl::unique_fd BlockDirFd() {
fbl::unique_fd fd;
auto [block, server] = fidl::Endpoints<fuchsia_io::Directory>::Create();
EXPECT_OK(fdio_open3_at(devmgr_.RealmExposedDir().handle()->get(), "block",
static_cast<uint64_t>(fuchsia_io::wire::kPermReadable),
server.TakeChannel().release()));
EXPECT_OK(fdio_fd_create(block.TakeChannel().release(), fd.reset_and_get_address()));
return fd;
}
// Create a disk with the default size for a BlockDevice.
void CreateDisk(std::unique_ptr<BlockDevice>* disk) {
ASSERT_NO_FATAL_FAILURE(CreateDisk(disk, kBlockCount * block_size_));
}
// Create a disk with the given size in bytes and the given type.
void CreateDisk(std::unique_ptr<BlockDevice>* disk, uint64_t bytes,
const uint8_t* type = kEmptyType) {
ASSERT_TRUE(bytes % block_size_ == 0);
uint64_t num_blocks = bytes / block_size_;
if (BaseDevmgrArgs().enable_storage_host) {
fidl::ClientEnd svc_root = RealmExposedDir();
fbl::unique_fd fd;
ASSERT_OK(fdio_fd_create(svc_root.TakeHandle().release(), fd.reset_and_get_address()));
ASSERT_NO_FATAL_FAILURE(BlockDevice::Create(disk, fd, type, num_blocks, block_size_));
} else {
ASSERT_NO_FATAL_FAILURE(
BlockDevice::CreateLegacy(disk, devmgr_.devfs_root(), type, num_blocks, block_size_));
}
}
// Create a disk with some initial contents.
void CreateDiskWithContents(std::unique_ptr<BlockDevice>* disk, zx::vmo contents,
const uint8_t* type_guid = kEmptyType) {
if (BaseDevmgrArgs().enable_storage_host) {
fidl::ClientEnd svc_root = RealmExposedDir();
fbl::unique_fd fd;
ASSERT_OK(fdio_fd_create(svc_root.TakeHandle().release(), fd.reset_and_get_address()));
ASSERT_NO_FATAL_FAILURE(
BlockDevice::CreateFromVmo(disk, fd, type_guid, std::move(contents), block_size_));
} else {
ASSERT_NO_FATAL_FAILURE(BlockDevice::CreateLegacyFromVmo(
disk, devmgr_.devfs_root(), type_guid, std::move(contents), block_size_));
}
}
// Creates a GPT-formatted device with `init_partitions`.
void CreateDiskWithGpt(std::unique_ptr<BlockDevice>* disk, size_t size = 0,
const std::vector<PartitionDescription>& init_partitions = {}) {
uint64_t num_blocks = std::max(size / block_size_, kGptBlockCount);
auto dev = std::make_unique<block_client::FakeBlockDevice>(num_blocks, block_size_);
zx::result result = dev->VmoChildReference();
ASSERT_OK(result);
zx::vmo contents = std::move(result).value();
zx::result gpt_result = gpt::GptDevice::Create(std::move(dev), block_size_, num_blocks);
ASSERT_OK(gpt_result);
std::unique_ptr<gpt::GptDevice> gpt = std::move(*gpt_result);
ASSERT_OK(gpt->Sync());
for (auto& part : init_partitions) {
ASSERT_OK(gpt->AddPartition(part.name.c_str(), part.type.bytes(), Uuid::Generate().bytes(),
part.start, part.length, 0),
"%s", part.name.c_str());
}
ASSERT_OK(gpt->Sync());
ASSERT_NO_FATAL_FAILURE(CreateDiskWithContents(disk, std::move(contents)));
}
// Creates a GPT-formatted device with EFI partition
void CreateDiskWithUefiGpt(std::unique_ptr<BlockDevice>* disk, size_t size = 0) {
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(disk, size,
{
PartitionDescription{GUID_EFI_NAME, Uuid(kEfiType), 0x8023, 0x8000},
}));
}
void ReadBlocks(const BlockDevice* blk_dev, size_t offset_in_blocks, size_t size_in_blocks,
uint8_t* out) const {
zx::vmo vmo;
const size_t vmo_size = size_in_blocks * block_size_;
ASSERT_OK(zx::vmo::create(vmo_size, 0, &vmo));
ASSERT_NO_FATAL_FAILURE(blk_dev->Read(vmo, vmo_size, offset_in_blocks, 0));
ASSERT_OK(vmo.read(out, 0, vmo_size));
}
void WriteBlocks(const BlockDevice* blk_dev, size_t offset_in_blocks, size_t size_in_blocks,
uint8_t* buffer) const {
zx::vmo vmo;
const size_t vmo_size = size_in_blocks * block_size_;
ASSERT_OK(zx::vmo::create(vmo_size, 0, &vmo));
ASSERT_OK(vmo.write(buffer, 0, vmo_size));
ASSERT_NO_FATAL_FAILURE(blk_dev->Write(vmo, vmo_size, offset_in_blocks, 0));
}
void ValidateBlockContent(const BlockDevice* blk_dev, size_t offset_in_blocks,
size_t size_in_blocks, uint8_t value) {
std::vector<uint8_t> buffer(size_in_blocks * block_size_);
ASSERT_NO_FATAL_FAILURE(ReadBlocks(blk_dev, offset_in_blocks, size_in_blocks, buffer.data()));
for (size_t i = 0; i < buffer.size(); i++) {
ASSERT_EQ(value, buffer[i], "at index: %zu", i);
}
}
// Ensure that the partitions published to fshost match the expected list.
void EnsurePartitionsMatch(std::span<const PartitionDescription> expected) {
std::vector<fidl::ClientEnd<fuchsia_hardware_block_partition::Partition>> devices;
ASSERT_NO_FATAL_FAILURE(FindAllBlockDevices(&devices));
std::vector<PartitionDescription> actual;
for (auto& device : devices) {
if (std::optional<PartitionDescription> desc = GetPartitionDescription(device); desc) {
actual.push_back(*desc);
}
}
for (const auto& part : expected) {
auto match = std::find_if(actual.cbegin(), actual.cend(),
[&part](const PartitionDescription& actual_part) {
return actual_part.name == part.name;
});
ASSERT_TRUE(match != actual.end(), "Partition %s not found", part.name.c_str());
EXPECT_EQ(part.type, match->type, "Partition %s wrong guid", part.name.c_str());
EXPECT_EQ(part.start, match->start, "Partition %s wrong start", part.name.c_str());
EXPECT_EQ(part.length, match->length, "Partition %s wrong length", part.name.c_str());
}
}
static std::optional<PartitionDescription> GetPartitionDescription(
fidl::UnownedClientEnd<fuchsia_hardware_block_partition::Partition> client) {
fidl::WireResult metadata = fidl::WireCall(client)->GetMetadata();
if (!metadata.ok() || !metadata->value()->has_name() || !metadata->value()->has_type_guid()) {
// Ignore non-Partition devices.
return std::nullopt;
}
const auto& value = metadata->value();
return PartitionDescription{
.name = std::string(value->name().cbegin(), value->name().cend()),
.type = uuid::Uuid(value->type_guid().value.data()),
.start = value->start_block_offset(),
.length = value->num_blocks(),
};
}
// The legacy devfs-based stack publishes drivers asynchronously, which means that some tests need
// to wait for block devices to be published to work properly.
void WaitForBlockDevices(size_t num_expected) {
if (BaseDevmgrArgs().enable_storage_host) {
// Storage-host publishes synchronously so we can stub this out.
return;
}
ASSERT_GT(num_expected, 0);
num_devices_ += num_expected;
std::string path = std::format("class/block/{:03d}", num_devices_ - 1);
ASSERT_OK(RecursiveWaitForFile(devmgr_.devfs_root().get(), path.c_str()).status_value());
}
void FindAllBlockDevices(
std::vector<fidl::ClientEnd<fuchsia_hardware_block_partition::Partition>>* out) {
if (BaseDevmgrArgs().enable_storage_host) {
ASSERT_NO_FATAL_FAILURE(FindAllBlockDevicesStorageHost(out));
return;
}
std::vector<std::string> block_devices;
ASSERT_TRUE(
files::ReadDirContentsAt(devmgr_.devfs_root().get(), "class/block", &block_devices));
for (const auto& device : block_devices) {
std::string path = std::format("class/block/{}/device_controller", device);
fdio_cpp::UnownedFdioCaller caller(devmgr_.devfs_root());
zx::result controller =
component::ConnectAt<fuchsia_device::Controller>(caller.directory(), path);
ASSERT_OK(controller);
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_block_partition::Partition>();
ASSERT_OK(endpoints);
auto [client, server] = std::move(*endpoints);
fidl::OneWayError response =
fidl::WireCall(*controller)->ConnectToDeviceFidl(server.TakeChannel());
ASSERT_TRUE(response.ok());
out->push_back(std::move(client));
}
}
void FindAllBlockDevicesStorageHost(
std::vector<fidl::ClientEnd<fuchsia_hardware_block_partition::Partition>>* out) {
fidl::ClientEnd svc_root = RealmExposedDir();
fbl::unique_fd fd;
ASSERT_OK(fdio_fd_create(svc_root.TakeHandle().release(), fd.reset_and_get_address()));
std::vector<std::string> entries;
ASSERT_TRUE(files::ReadDirContentsAt(fd.get(), "fuchsia.storage.partitions.PartitionService",
&entries));
for (const auto& entry : entries) {
std::string path =
std::format("fuchsia.storage.partitions.PartitionService/{}/volume", entry);
fdio_cpp::UnownedFdioCaller caller(fd.get());
zx::result partition = component::ConnectAt<fuchsia_hardware_block_partition::Partition>(
caller.directory(), path);
ASSERT_OK(partition);
out->push_back(std::move(*partition));
}
}
IsolatedDevmgr devmgr_;
fbl::String board_name_;
std::string slot_suffix_;
size_t num_devices_ = 0;
const uint32_t block_size_;
};
class FakeSystemStateTransition final : public fidl::WireServer<SystemStateTransition> {
public:
void GetTerminationSystemState(GetTerminationSystemStateCompleter::Sync& completer) override {
completer.Reply(state_);
}
void GetMexecZbis(GetMexecZbisCompleter::Sync& completer) override {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void SetTerminationSystemState(SystemPowerState state) { state_ = state; }
private:
fidl::ServerBindingGroup<SystemStateTransition> bindings_;
SystemPowerState state_ = SystemPowerState::kFullyOn;
};
class FakeSvc {
public:
explicit FakeSvc(async_dispatcher_t* dispatcher) {
zx::result server_end = fidl::CreateEndpoints(&root_);
ASSERT_OK(server_end);
async::PostTask(dispatcher, [dispatcher,
&fake_system_shutdown_state = fake_system_shutdown_state_,
server_end = std::move(server_end.value())]() mutable {
component::OutgoingDirectory outgoing{dispatcher};
ASSERT_OK(outgoing.AddUnmanagedProtocol<SystemStateTransition>(
[&fake_system_shutdown_state, dispatcher](fidl::ServerEnd<SystemStateTransition> server) {
fidl::BindServer(dispatcher, std::move(server), &fake_system_shutdown_state);
}));
ASSERT_OK(outgoing.Serve(std::move(server_end)));
// Stash the outgoing directory on the dispatcher so that the dtor runs on the dispatcher
// thread.
async::PostDelayedTask(
dispatcher, [outgoing = std::move(outgoing)]() {}, zx::duration::infinite());
});
}
FakeSystemStateTransition& fake_system_shutdown_state() { return fake_system_shutdown_state_; }
zx::result<fidl::ClientEnd<fuchsia_io::Directory>> svc() {
return component::OpenDirectoryAt(root_, component::OutgoingDirectory::kServiceDirectory);
}
private:
FakeSystemStateTransition fake_system_shutdown_state_;
fidl::ClientEnd<fuchsia_io::Directory> root_;
};
class EfiDevicePartitionerTests : public GptDevicePartitionerTests {
protected:
EfiDevicePartitionerTests() : GptDevicePartitionerTests(fbl::String()) {
EXPECT_OK(loop_.StartThread("efi-devicepartitioner-tests-loop"));
}
~EfiDevicePartitionerTests() { loop_.Shutdown(); }
// Create a DevicePartition for a device.
zx::result<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
BlockDevice* gpt = nullptr) {
return CreatePartitioner(gpt, RealmExposedDir());
}
virtual zx::result<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
BlockDevice* gpt, fidl::ClientEnd<fuchsia_io::Directory> svc_root) {
fidl::ClientEnd<fuchsia_device::Controller> controller;
if (gpt) {
controller = gpt->ConnectToLegacyController();
}
zx::result devices = CreateBlockDevices();
if (devices.is_error()) {
return devices.take_error();
}
std::shared_ptr<paver::Context> context;
return paver::EfiDevicePartitioner::Initialize(*devices, std::move(svc_root),
paver::PaverConfig{.arch = paver::Arch::kX64},
std::move(controller), context);
}
void ResetPartitionTablesTest();
async::Loop loop_{&kAsyncLoopConfigNeverAttachToThread};
};
TEST_F(EfiDevicePartitionerTests, InitializeWithoutGptFails) {
std::unique_ptr<BlockDevice> dev;
ASSERT_NO_FATAL_FAILURE(CreateDisk(&dev));
ASSERT_NOT_OK(CreatePartitioner({}));
}
TEST_F(EfiDevicePartitionerTests, InitializeTwoCandidatesWithoutFvmFails) {
std::unique_ptr<BlockDevice> gpt, gpt2;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithGpt(&gpt));
ASSERT_NO_FATAL_FAILURE(CreateDiskWithGpt(&gpt2));
ASSERT_NOT_OK(CreatePartitioner({}));
}
class EfiDevicePartitionerGptAllTests : public EfiDevicePartitionerTests {
protected:
IsolatedDevmgr::Args BaseDevmgrArgs() override {
IsolatedDevmgr::Args args;
args.disable_block_watcher = false;
args.fshost_config.emplace_back(component_testing::ConfigCapability{
.name = "fuchsia.fshost.GptAll", .value = component_testing::ConfigValue::Bool(true)});
return args;
}
};
TEST_F(EfiDevicePartitionerGptAllTests,
InitializeWithMultipleCandidateGPTsFailsWithoutExplicitDevice) {
// Set up a two valid GPTs.
std::unique_ptr<BlockDevice> gpt, gpt2;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithUefiGpt(&gpt, 64 * kGibibyte));
ASSERT_NO_FATAL_FAILURE(CreateDiskWithUefiGpt(&gpt2, 64 * kGibibyte));
ASSERT_OK(CreatePartitioner(gpt.get()));
ASSERT_OK(CreatePartitioner(gpt2.get()));
// Note that this time we don't pass in a block device fd.
ASSERT_NOT_OK(CreatePartitioner({}));
}
TEST_F(EfiDevicePartitionerTests, FindOldBootloaderPartitionName) {
std::unique_ptr<BlockDevice> gpt;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt, 64 * kGibibyte,
{
PartitionDescription{"efi-system", Uuid(kEfiType), 0x22, 0x8000},
}));
auto partitioner = CreatePartitioner(gpt.get());
ASSERT_OK(partitioner);
ASSERT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA)));
}
TEST_F(EfiDevicePartitionerTests, SupportsPartition) {
std::unique_ptr<BlockDevice> gpt;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithUefiGpt(&gpt, 64 * kGibibyte));
zx::result status = CreatePartitioner(gpt.get());
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_TRUE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
EXPECT_TRUE(partitioner->SupportsPartition(
PartitionSpec(paver::Partition::kFuchsiaVolumeManager, paver::kOpaqueVolumeContentType)));
// Unsupported partition type.
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kUnknown)));
// Unsupported content type.
EXPECT_FALSE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA, "foo_type")));
}
TEST_F(EfiDevicePartitionerTests, ValidatePayload) {
std::unique_ptr<BlockDevice> gpt;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithUefiGpt(&gpt, 64 * kGibibyte));
zx::result status = CreatePartitioner(gpt.get());
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
// Test invalid partitions.
ASSERT_NOT_OK(partitioner->ValidatePayload(PartitionSpec(paver::Partition::kZirconA),
std::span<uint8_t>()));
ASSERT_NOT_OK(partitioner->ValidatePayload(PartitionSpec(paver::Partition::kZirconB),
std::span<uint8_t>()));
ASSERT_NOT_OK(partitioner->ValidatePayload(PartitionSpec(paver::Partition::kZirconR),
std::span<uint8_t>()));
// Non-kernel partitions are not validated.
ASSERT_OK(partitioner->ValidatePayload(PartitionSpec(paver::Partition::kAbrMeta),
std::span<uint8_t>()));
}
TEST_F(EfiDevicePartitionerTests, OnStopRebootBootloader) {
std::unique_ptr<BlockDevice> gpt;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithGpt(
&gpt, 64 * kGibibyte,
{
PartitionDescription{GUID_EFI_NAME, Uuid(kEfiType), 0x8023, 0x8000},
PartitionDescription{GUID_ABR_META_NAME, Uuid(kAbrMetaType), 0x10023, 0x8},
}));
{
FakeSvc fake_svc(loop_.dispatcher());
zx::result svc = fake_svc.svc();
EXPECT_OK(svc);
zx::result partitioner_status = CreatePartitioner(gpt.get(), std::move(svc.value()));
ASSERT_OK(partitioner_status);
std::unique_ptr<paver::DevicePartitioner> partitioner = std::move(partitioner_status.value());
ASSERT_NO_FATAL_FAILURE(WaitForBlockDevices(2));
// Set Termination system state to "reboot to bootloader"
fake_svc.fake_system_shutdown_state().SetTerminationSystemState(
SystemPowerState::kRebootBootloader);
// Trigger OnStop event that should set one shot flag
ASSERT_OK(partitioner->OnStop());
// Verify ABR flags
auto partition = partitioner->FindPartition(paver::PartitionSpec(paver::Partition::kAbrMeta));
ASSERT_OK(partition);
auto abr_partition_client = abr::AbrPartitionClient::Create(std::move(partition.value()));
ASSERT_OK(abr_partition_client);
auto abr_flags_res = abr_partition_client.value()->GetAndClearOneShotFlags();
ASSERT_OK(abr_flags_res);
EXPECT_TRUE(AbrIsOneShotBootloaderBootSet(abr_flags_res.value()));
EXPECT_FALSE(AbrIsOneShotRecoveryBootSet(abr_flags_res.value()));
}
}
TEST_F(EfiDevicePartitionerTests, OnStopRebootRecovery) {
std::unique_ptr<BlockDevice> gpt;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithGpt(
&gpt, 64 * kGibibyte,
{
PartitionDescription{GUID_EFI_NAME, Uuid(kEfiType), 0x8023, 0x8000},
PartitionDescription{GUID_ABR_META_NAME, Uuid(kAbrMetaType), 0x10023, 0x8},
}));
{
FakeSvc fake_svc(loop_.dispatcher());
zx::result svc = fake_svc.svc();
EXPECT_OK(svc);
zx::result partitioner_status = CreatePartitioner(gpt.get(), std::move(svc.value()));
ASSERT_OK(partitioner_status);
std::unique_ptr<paver::DevicePartitioner> partitioner = std::move(partitioner_status.value());
ASSERT_NO_FATAL_FAILURE(WaitForBlockDevices(2));
// Set Termination system state to "reboot to bootloader"
fake_svc.fake_system_shutdown_state().SetTerminationSystemState(
SystemPowerState::kRebootRecovery);
// Trigger OnStop event that should set one shot flag
ASSERT_OK(partitioner->OnStop());
// Verify ABR flags
auto partition = partitioner->FindPartition(paver::PartitionSpec(paver::Partition::kAbrMeta));
ASSERT_OK(partition);
auto abr_partition_client = abr::AbrPartitionClient::Create(std::move(partition.value()));
ASSERT_OK(abr_partition_client);
auto abr_flags_res = abr_partition_client.value()->GetAndClearOneShotFlags();
ASSERT_OK(abr_flags_res);
EXPECT_FALSE(AbrIsOneShotBootloaderBootSet(abr_flags_res.value()));
EXPECT_TRUE(AbrIsOneShotRecoveryBootSet(abr_flags_res.value()));
}
}
class EfiDevicePartitionerWithStorageHostTests : public EfiDevicePartitionerTests {
protected:
IsolatedDevmgr::Args BaseDevmgrArgs() override {
IsolatedDevmgr::Args args;
args.enable_storage_host = true;
args.disable_block_watcher = true;
args.fshost_config.emplace_back(
component_testing::ConfigCapability{.name = "fuchsia.fshost.RamdiskImage",
.value = component_testing::ConfigValue::Bool(true)});
return args;
}
zx::result<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
BlockDevice* gpt, fidl::ClientEnd<fuchsia_io::Directory> svc_root) override {
zx::result devices = CreateBlockDevices();
if (devices.is_error()) {
return devices.take_error();
}
std::shared_ptr<paver::Context> context;
return paver::EfiDevicePartitioner::Initialize(
*devices, svc_root, paver::PaverConfig{.arch = paver::Arch::kX64}, {}, context);
}
};
void EfiDevicePartitionerTests::ResetPartitionTablesTest() {
const Uuid etc_guid = Uuid::Generate();
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, 64 * kGibibyte,
{
PartitionDescription{"efi", Uuid(kEfiType), 0x22, 0x1},
PartitionDescription{"efi-system", Uuid(kEfiType), 0x23, 0x8000},
PartitionDescription{GUID_EFI_NAME, Uuid(kEfiType), 0x8023, 0x8000},
PartitionDescription{"ZIRCON_A", Uuid(kZirconAType), 0x10023, 0x1},
PartitionDescription{"zircon_b", Uuid(kZirconBType), 0x10024, 0x1},
PartitionDescription{"zircon r", Uuid(kZirconRType), 0x10025, 0x1},
PartitionDescription{"vbmeta-a", Uuid(kVbMetaAType), 0x10026, 0x1},
PartitionDescription{"VBMETA_B", Uuid(kVbMetaBType), 0x10027, 0x1},
PartitionDescription{"VBMETA R", Uuid(kVbMetaRType), 0x10028, 0x1},
PartitionDescription{"abrmeta", Uuid(kAbrMetaType), 0x10029, 0x1},
PartitionDescription{"FVM", Uuid(kFvmType), 0x10030, 0x1},
PartitionDescription{"etc", etc_guid, 0x10031, 0x400},
}));
// Create EFI device partitioner and initialise partition tables.
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_NO_FATAL_FAILURE(WaitForBlockDevices(1 + 12));
ASSERT_OK(partitioner->ResetPartitionTables());
ASSERT_NO_FATAL_FAILURE(WaitForBlockDevices(12));
// Ensure the final partition layout looks like we expect it to.
// Non-Fuchsia partitions ought to have been preserved at their old offsets, and Fuchsia
// partitions should be dynamically allocated in a first-fit manner.
// For clarity they are listed in order of non-Fuchsia partitions followed by Fuchsia partitions,
// but the order is not necessarily representative of the GPT partition table entries.
const std::array<PartitionDescription, 12> partitions_at_end{
// Preserved Non-Fuchsia partitions
PartitionDescription{"efi", Uuid(kEfiType), 0x22, 0x1},
PartitionDescription{"efi-system", Uuid(kEfiType), 0x23, 0x8000},
PartitionDescription{"etc", etc_guid, 0x10031, 0x400},
// Reallocated Fuchsia partitions
PartitionDescription{GUID_BOOTLOADER_NAME, Uuid(kBootloaderType), 0x8023, 0x8000},
PartitionDescription{GPT_ZIRCON_A_NAME, Uuid(kZirconType), 0x10431, 0x40000},
PartitionDescription{GPT_ZIRCON_B_NAME, Uuid(kZirconType), 0x50431, 0x40000},
PartitionDescription{GPT_ZIRCON_R_NAME, Uuid(kZirconType), 0x90431, 0x60000},
PartitionDescription{GPT_VBMETA_A_NAME, Uuid(kVbMetaType), 0xf0431, 0x80},
PartitionDescription{GPT_VBMETA_B_NAME, Uuid(kVbMetaType), 0xf04b1, 0x80},
PartitionDescription{GPT_VBMETA_R_NAME, Uuid(kVbMetaType), 0xf0531, 0x80},
PartitionDescription{GPT_DURABLE_BOOT_NAME, Uuid(kDurableBootType), 0x10023, 0x8},
PartitionDescription{GPT_FVM_NAME, Uuid(kNewFvmType), 0xf05b1, 0x7000000},
};
ASSERT_NO_FATAL_FAILURE(EnsurePartitionsMatch(partitions_at_end));
// Make sure we can find the important partitions.
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
EXPECT_OK(partitioner->FindPartition(
PartitionSpec(paver::Partition::kFuchsiaVolumeManager, paver::kOpaqueVolumeContentType)));
// Check that we found the correct bootloader partition.
auto status2 = partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA));
EXPECT_OK(status2);
auto status3 = status2->GetPartitionSize();
EXPECT_OK(status3);
EXPECT_EQ(status3.value(), 0x8000 * block_size_);
}
TEST_F(EfiDevicePartitionerTests, ResetPartitionTables) {
ASSERT_NO_FATAL_FAILURE(ResetPartitionTablesTest());
}
TEST_F(EfiDevicePartitionerWithStorageHostTests, ResetPartitionTables) {
ASSERT_NO_FATAL_FAILURE(ResetPartitionTablesTest());
}
TEST_F(EfiDevicePartitionerWithStorageHostTests, InitializeWithoutGptFails) {
std::unique_ptr<BlockDevice> gpt;
ASSERT_NO_FATAL_FAILURE(CreateDisk(&gpt, 64 * kGibibyte));
ASSERT_NOT_OK(EfiDevicePartitionerTests::CreatePartitioner());
}
TEST_F(EfiDevicePartitionerWithStorageHostTests, InitializeWithoutFvmSucceeds) {
std::unique_ptr<BlockDevice> gpt;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithUefiGpt(&gpt, 64 * kGibibyte));
ASSERT_OK(EfiDevicePartitionerTests::CreatePartitioner());
}
class FixedDevicePartitionerTests : public PaverTest {
protected:
void SetUp() override {
PaverTest::SetUp();
IsolatedDevmgr::Args args;
args.disable_block_watcher = false;
ASSERT_OK(IsolatedDevmgr::Create(&args, &devmgr_));
ASSERT_OK(RecursiveWaitForFile(devmgr_.devfs_root().get(), "sys/platform/ram-disk/ramctl")
.status_value());
}
IsolatedDevmgr devmgr_;
};
TEST_F(FixedDevicePartitionerTests, UseBlockInterfaceTest) {
zx::result devices = paver::BlockDevices::CreateDevfs(devmgr_.devfs_root().duplicate());
ASSERT_OK(devices);
auto status = paver::FixedDevicePartitioner::Initialize(*devices, {});
ASSERT_OK(status);
ASSERT_FALSE(status->IsFvmWithinFtl());
}
TEST_F(FixedDevicePartitionerTests, WipeFvmTest) {
zx::result devices = paver::BlockDevices::CreateDevfs(devmgr_.devfs_root().duplicate());
ASSERT_OK(devices);
auto status = paver::FixedDevicePartitioner::Initialize(*devices, {});
ASSERT_OK(status);
ASSERT_OK(status->WipeFvm());
}
TEST_F(FixedDevicePartitionerTests, FindPartitionTest) {
std::unique_ptr<BlockDevice> fvm, bootloader, zircon_a, zircon_b, zircon_r, vbmeta_a, vbmeta_b,
vbmeta_r;
ASSERT_NO_FATAL_FAILURE(
BlockDevice::CreateLegacy(&bootloader, devmgr_.devfs_root(), kBootloaderType));
ASSERT_NO_FATAL_FAILURE(BlockDevice::CreateLegacy(&zircon_a, devmgr_.devfs_root(), kZirconAType));
ASSERT_NO_FATAL_FAILURE(BlockDevice::CreateLegacy(&zircon_b, devmgr_.devfs_root(), kZirconBType));
ASSERT_NO_FATAL_FAILURE(BlockDevice::CreateLegacy(&zircon_r, devmgr_.devfs_root(), kZirconRType));
ASSERT_NO_FATAL_FAILURE(BlockDevice::CreateLegacy(&vbmeta_a, devmgr_.devfs_root(), kVbMetaAType));
ASSERT_NO_FATAL_FAILURE(BlockDevice::CreateLegacy(&vbmeta_b, devmgr_.devfs_root(), kVbMetaBType));
ASSERT_NO_FATAL_FAILURE(BlockDevice::CreateLegacy(&vbmeta_r, devmgr_.devfs_root(), kVbMetaRType));
ASSERT_NO_FATAL_FAILURE(BlockDevice::CreateLegacy(&fvm, devmgr_.devfs_root(), kFvmType));
std::shared_ptr<paver::Context> context = std::make_shared<paver::Context>();
zx::result devices = paver::BlockDevices::CreateDevfs(devmgr_.devfs_root().duplicate());
ASSERT_OK(devices);
zx::result partitioner_result = paver::DevicePartitionerFactory::Create(
*devices, kInvalidSvcRoot, paver::PaverConfig{.arch = paver::Arch::kArm64}, context);
ASSERT_OK(partitioner_result);
std::unique_ptr partitioner = std::move(partitioner_result.value());
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
}
TEST_F(FixedDevicePartitionerTests, SupportsPartitionTest) {
zx::result devices = paver::BlockDevices::CreateDevfs(devmgr_.devfs_root().duplicate());
ASSERT_OK(devices);
auto status = paver::FixedDevicePartitioner::Initialize(*devices, {});
ASSERT_OK(status);
auto& partitioner = status.value();
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_TRUE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
// Unsupported partition type.
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kUnknown)));
// Unsupported content type.
EXPECT_FALSE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA, "foo_type")));
}
class SherlockPartitionerTests : public GptDevicePartitionerTests {
protected:
SherlockPartitionerTests() : GptDevicePartitionerTests("sherlock") {}
// Create a DevicePartition for a device.
zx::result<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
BlockDevice* gpt = nullptr) {
fidl::ClientEnd<fuchsia_io::Directory> svc_root = RealmExposedDir();
fidl::ClientEnd<fuchsia_device::Controller> controller;
if (gpt) {
controller = gpt->ConnectToLegacyController();
}
zx::result devices = CreateBlockDevices();
return paver::SherlockPartitioner::Initialize(
*devices, svc_root, paver::PaverConfig{.arch = paver::Arch::kArm64}, std::move(controller));
}
void FindPartitionNewGuidsTest() {
std::unique_ptr<BlockDevice> gpt_dev;
constexpr uint64_t kBlockCount = 0x748034;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, kBlockCount * block_size_,
// partition size / location is arbitrary
{
{GPT_DURABLE_BOOT_NAME, Uuid(kDurableBootType), 0x10400, 0x10000},
{GPT_VBMETA_A_NAME, Uuid(kVbMetaType), 0x20400, 0x10000},
{GPT_VBMETA_B_NAME, Uuid(kVbMetaType), 0x30400, 0x10000},
{GPT_VBMETA_R_NAME, Uuid(kVbMetaType), 0x40400, 0x10000},
{GPT_ZIRCON_A_NAME, Uuid(kZirconType), 0x50400, 0x10000},
{GPT_ZIRCON_B_NAME, Uuid(kZirconType), 0x60400, 0x10000},
{GPT_ZIRCON_R_NAME, Uuid(kZirconType), 0x70400, 0x10000},
{GPT_FVM_NAME, Uuid(kNewFvmType), 0x80400, 0x10000},
}));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
// Make sure we can find the important partitions.
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
}
void FindPartitionNewGuidsWithWrongTypeGUIDSTest() {
// Due to a bootloader bug (b/173801312), the type GUID's may be reset in certain conditions.
// This test verifies that the sherlock partitioner only looks at the partition name.
constexpr uint64_t kBlockCount = 0x748034;
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, kBlockCount * block_size_,
{
{GPT_DURABLE_BOOT_NAME, Uuid(kStateLinuxGuid), 0x10400, 0x10000},
{GPT_VBMETA_A_NAME, Uuid(kStateLinuxGuid), 0x20400, 0x10000},
{GPT_VBMETA_B_NAME, Uuid(kStateLinuxGuid), 0x30400, 0x10000},
{GPT_VBMETA_R_NAME, Uuid(kStateLinuxGuid), 0x40400, 0x10000},
{GPT_ZIRCON_A_NAME, Uuid(kStateLinuxGuid), 0x50400, 0x10000},
{GPT_ZIRCON_B_NAME, Uuid(kStateLinuxGuid), 0x60400, 0x10000},
{GPT_ZIRCON_R_NAME, Uuid(kStateLinuxGuid), 0x70400, 0x10000},
{GPT_FVM_NAME, Uuid(kStateLinuxGuid), 0x80400, 0x10000},
}));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
// Make sure we can find the important partitions.
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
}
void FindPartitionSecondaryTest() {
constexpr uint64_t kBlockCount = 0x748034;
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, kBlockCount * block_size_,
{
{GPT_DURABLE_BOOT_NAME, Uuid(kStateLinuxGuid), 0x10400, 0x10000},
{GPT_VBMETA_A_NAME, Uuid(kStateLinuxGuid), 0x20400, 0x10000},
{GPT_VBMETA_B_NAME, Uuid(kStateLinuxGuid), 0x30400, 0x10000},
// Removed vbmeta_r to validate that it is not found
{"boot", Uuid(kStateLinuxGuid), 0x50400, 0x10000},
{"system", Uuid(kStateLinuxGuid), 0x60400, 0x10000},
{"recovery", Uuid(kStateLinuxGuid), 0x70400, 0x10000},
{GPT_FVM_NAME, Uuid(kStateLinuxGuid), 0x80400, 0x10000},
}));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
// Make sure we can find the important partitions.
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_NOT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
}
void ShouldNotFindPartitionBootTest() {
constexpr uint64_t kBlockCount = 0x748034;
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, kBlockCount * block_size_,
{
{"bootloader", Uuid(kStateLinuxGuid), 0x10400, 0x10000},
}));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
// Make sure we can't find zircon_a, which is aliased to "boot". The GPT logic would
// previously only check prefixes, so "boot" would match with "bootloader".
EXPECT_NOT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
}
void FindBootloaderTest() {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithGpt(&gpt_dev));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
// No boot0/boot1 yet, we shouldn't be able to find the bootloader.
ASSERT_NOT_OK(
partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA, "skip_metadata")));
std::unique_ptr<BlockDevice> boot0_dev, boot1_dev;
ASSERT_NO_FATAL_FAILURE(CreateDisk(&boot0_dev, kBlockCount * kBlockSize, kBoot0Type));
ASSERT_NO_FATAL_FAILURE(CreateDisk(&boot1_dev, kBlockCount * kBlockSize, kBoot1Type));
// Now it should succeed.
ASSERT_OK(
partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA, "skip_metadata")));
}
void SupportsPartitionTest() {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithGpt(&gpt_dev, 64 * kMebibyte));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
EXPECT_TRUE(partitioner->SupportsPartition(
PartitionSpec(paver::Partition::kBootloaderA, "skip_metadata")));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_TRUE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
// Unsupported partition type.
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kUnknown)));
// Unsupported content type.
EXPECT_FALSE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA, "foo_type")));
}
};
TEST_F(SherlockPartitionerTests, InitializeWithoutGptFails) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateDisk(&gpt_dev));
ASSERT_NOT_OK(CreatePartitioner(nullptr));
}
TEST_F(SherlockPartitionerTests, FindPartitionNewGuids) {
ASSERT_NO_FATAL_FAILURE(FindPartitionNewGuidsTest());
}
TEST_F(SherlockPartitionerTests, FindPartitionNewGuidsWithWrongTypeGUIDS) {
ASSERT_NO_FATAL_FAILURE(FindPartitionNewGuidsWithWrongTypeGUIDSTest());
}
TEST_F(SherlockPartitionerTests, FindPartitionSecondary) {
ASSERT_NO_FATAL_FAILURE(FindPartitionSecondaryTest());
}
TEST_F(SherlockPartitionerTests, ShouldNotFindPartitionBoot) {
ASSERT_NO_FATAL_FAILURE(ShouldNotFindPartitionBootTest());
}
TEST_F(SherlockPartitionerTests, FindBootloader) { ASSERT_NO_FATAL_FAILURE(FindBootloaderTest()); }
TEST_F(SherlockPartitionerTests, SupportsPartition) {
ASSERT_NO_FATAL_FAILURE(SupportsPartitionTest());
}
class SherlockPartitionerWithStorageHostTests : public SherlockPartitionerTests {
protected:
IsolatedDevmgr::Args BaseDevmgrArgs() override {
IsolatedDevmgr::Args args;
args.enable_storage_host = true;
return args;
}
};
TEST_F(SherlockPartitionerWithStorageHostTests, FindPartitionNewGuids) {
ASSERT_NO_FATAL_FAILURE(FindPartitionNewGuidsTest());
}
TEST_F(SherlockPartitionerWithStorageHostTests, FindPartitionNewGuidsWithWrongTypeGUIDS) {
ASSERT_NO_FATAL_FAILURE(FindPartitionNewGuidsWithWrongTypeGUIDSTest());
}
TEST_F(SherlockPartitionerWithStorageHostTests, FindPartitionSecondary) {
ASSERT_NO_FATAL_FAILURE(FindPartitionSecondaryTest());
}
TEST_F(SherlockPartitionerWithStorageHostTests, ShouldNotFindPartitionBoot) {
ASSERT_NO_FATAL_FAILURE(ShouldNotFindPartitionBootTest());
}
TEST_F(SherlockPartitionerWithStorageHostTests, FindBootloader) {
ASSERT_NO_FATAL_FAILURE(FindBootloaderTest());
}
TEST_F(SherlockPartitionerWithStorageHostTests, SupportsPartition) {
ASSERT_NO_FATAL_FAILURE(SupportsPartitionTest());
}
class MoonflowerPartitionerTests : public GptDevicePartitionerTests {
protected:
MoonflowerPartitionerTests() : GptDevicePartitionerTests("sorrel") {}
IsolatedDevmgr::Args BaseDevmgrArgs() override {
IsolatedDevmgr::Args args;
args.disable_block_watcher = true;
args.enable_storage_host = true;
args.fshost_config.emplace_back(
component_testing::ConfigCapability{.name = "fuchsia.fshost.MergeSuperAndUserdata",
.value = component_testing::ConfigValue::Bool(true)});
return args;
}
// Create a DevicePartition for a device.
zx::result<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner() {
fidl::ClientEnd<fuchsia_io::Directory> svc_root = RealmExposedDir();
zx::result devices = CreateBlockDevices();
return paver::MoonflowerPartitioner::Initialize(
paver::PaverConfig{
.arch = paver::Arch::kArm64,
.system_partition_names = {"super_and_userdata"},
},
*devices, svc_root, {});
}
};
TEST_F(MoonflowerPartitionerTests, InitializeWithoutGptFails) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateDisk(&gpt_dev));
ASSERT_NOT_OK(CreatePartitioner());
}
TEST_F(MoonflowerPartitionerTests, InitializeWithoutFvmFails) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithGpt(&gpt_dev, 32 * kGibibyte));
ASSERT_NOT_OK(CreatePartitioner());
}
TEST_F(MoonflowerPartitionerTests, FindPartition) {
constexpr uint64_t kBlockCount = 0x748034;
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, kBlockCount * block_size_,
{
{GUID_ABR_META_NAME, Uuid(kAbrMetaType), 0x10400, 0x10000},
{"dtbo_a", Uuid(kDummyType), 0x30400, 0x10000},
{"dtbo_b", Uuid(kDummyType), 0x40400, 0x10000},
{"boot_a", Uuid(kZirconAType), 0x50400, 0x10000},
{"boot_b", Uuid(kZirconBType), 0x60400, 0x10000},
{"system_a", Uuid(kDummyType), 0x70400, 0x10000},
{"system_b", Uuid(kDummyType), 0x80400, 0x10000},
{GPT_VBMETA_A_NAME, Uuid(kVbMetaAType), 0x90400, 0x10000},
{GPT_VBMETA_B_NAME, Uuid(kVbMetaBType), 0xa0400, 0x10000},
{"reserved_a", Uuid(kDummyType), 0xc0400, 0x10000},
{"reserved_b", Uuid(kDummyType), 0xd0400, 0x10000},
{"reserved_c", Uuid(kVbMetaRType), 0xe0400, 0x10000},
{"cache", Uuid(kZirconRType), 0xf0400, 0x10000},
{"super", Uuid(kFvmType), 0x100400, 0x10000},
{"userdata", Uuid(kDummyType), 0x110400, 0x10000},
{"vendor_boot_a", Uuid(kDummyType), 0x120400, 0x10000},
{"vendor_boot_b", Uuid(kDummyType), 0x130400, 0x10000},
}));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
// Make sure we can find the important partitions.
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA, "dtbo")));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderB, "dtbo")));
EXPECT_OK(
partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA, "recovery_zbi")));
EXPECT_OK(
partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderB, "recovery_zbi")));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
// We should not be able to find a kBootloader partition with unknown `content_type`.
EXPECT_NOT_OK(
partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA, "foo_type")));
// We should not be able to find an unslotted kBootloader partition.
EXPECT_NOT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA, "cache")));
}
TEST_F(MoonflowerPartitionerTests, SupportsPartition) {
constexpr uint64_t kBlockCount = 0x748034;
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, kBlockCount * block_size_,
{
{GUID_ABR_META_NAME, Uuid(kAbrMetaType), 0x10400, 0x10000},
{"dtbo_a", Uuid(kDummyType), 0x30400, 0x10000},
{"dtbo_b", Uuid(kDummyType), 0x40400, 0x10000},
{"boot_a", Uuid(kZirconAType), 0x50400, 0x10000},
{"boot_b", Uuid(kZirconBType), 0x60400, 0x10000},
{"system_a", Uuid(kDummyType), 0x70400, 0x10000},
{"system_b", Uuid(kDummyType), 0x80400, 0x10000},
{GPT_VBMETA_A_NAME, Uuid(kVbMetaAType), 0x90400, 0x10000},
{GPT_VBMETA_B_NAME, Uuid(kVbMetaBType), 0xa0400, 0x10000},
{"reserved_a", Uuid(kDummyType), 0xc0400, 0x10000},
{"reserved_b", Uuid(kDummyType), 0xd0400, 0x10000},
{"reserved_c", Uuid(kVbMetaRType), 0xe0400, 0x10000},
{"cache", Uuid(kZirconRType), 0xf0400, 0x10000},
{"super", Uuid(kFvmType), 0x100400, 0x10000},
{"userdata", Uuid(kDummyType), 0x110400, 0x10000},
{"vendor_boot_a", Uuid(kDummyType), 0x120400, 0x10000},
{"vendor_boot_b", Uuid(kDummyType), 0x130400, 0x10000},
}));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
// We should support any kBootloader partitions with non-empty `content_type`.
EXPECT_TRUE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderA, "dtbo")));
EXPECT_TRUE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderB, "dtbo")));
EXPECT_TRUE(partitioner->SupportsPartition(
PartitionSpec(paver::Partition::kBootloaderA, "recovery_zbi")));
EXPECT_TRUE(partitioner->SupportsPartition(
PartitionSpec(paver::Partition::kBootloaderB, "recovery_zbi")));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_TRUE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
EXPECT_TRUE(partitioner->SupportsPartition(
PartitionSpec(paver::Partition::kFuchsiaVolumeManager, paver::kOpaqueVolumeContentType)));
// Unsupported partition type.
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kUnknown)));
// Unsupported content type.
EXPECT_FALSE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta, "foo_type")));
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderA, "")));
}
class LuisPartitionerTests : public GptDevicePartitionerTests {
protected:
LuisPartitionerTests() : GptDevicePartitionerTests("luis", 512, "_a") {}
// Create a DevicePartition for a device.
zx::result<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
fidl::ClientEnd<fuchsia_device::Controller> device) {
fidl::ClientEnd<fuchsia_io::Directory> svc_root = RealmExposedDir();
zx::result devices = CreateBlockDevices();
return paver::LuisPartitioner::Initialize(
*devices, svc_root, paver::PaverConfig{.arch = paver::Arch::kArm64}, std::move(device));
}
};
TEST_F(LuisPartitionerTests, InitializeWithoutGptFails) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateDisk(&gpt_dev));
ASSERT_NOT_OK(CreatePartitioner({}));
}
TEST_F(LuisPartitionerTests, InitializeWithoutFvmFails) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithGpt(&gpt_dev, 32 * kGibibyte));
ASSERT_NOT_OK(CreatePartitioner({}));
}
TEST_F(LuisPartitionerTests, FindPartition) {
// kBlockCount should be a value large enough to accommodate all partitions and blocks reserved
// by gpt. The current value is copied from the case of sherlock. As of now, we assume they
// have the same disk size requirement.
constexpr uint64_t kBlockCount = 0x748034;
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, kBlockCount * block_size_,
{
{GPT_DURABLE_BOOT_NAME, Uuid(kDummyType), 0x10400, 0x10000},
{GPT_BOOTLOADER_A_NAME, Uuid(kDummyType), 0x30400, 0x10000},
{GPT_BOOTLOADER_B_NAME, Uuid(kDummyType), 0x40400, 0x10000},
{GPT_BOOTLOADER_R_NAME, Uuid(kDummyType), 0x50400, 0x10000},
{GPT_VBMETA_A_NAME, Uuid(kDummyType), 0x60400, 0x10000},
{GPT_VBMETA_B_NAME, Uuid(kDummyType), 0x70400, 0x10000},
{GPT_VBMETA_R_NAME, Uuid(kDummyType), 0x80400, 0x10000},
{GPT_ZIRCON_A_NAME, Uuid(kDummyType), 0x90400, 0x10000},
{GPT_ZIRCON_B_NAME, Uuid(kDummyType), 0xa0400, 0x10000},
{GPT_ZIRCON_R_NAME, Uuid(kDummyType), 0xb0400, 0x10000},
{GPT_FACTORY_NAME, Uuid(kDummyType), 0xc0400, 0x10000},
{GPT_FVM_NAME, Uuid(kDummyType), 0xe0400, 0x10000},
}));
zx::result status = CreatePartitioner(gpt_dev->ConnectToLegacyController());
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
EXPECT_NOT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA)));
std::unique_ptr<BlockDevice> boot0_dev, boot1_dev;
ASSERT_NO_FATAL_FAILURE(CreateDisk(&boot0_dev, kBlockCount * kBlockSize, kBoot0Type));
ASSERT_NO_FATAL_FAILURE(CreateDisk(&boot1_dev, kBlockCount * kBlockSize, kBoot1Type));
// Make sure we can find the important partitions.
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
}
TEST_F(LuisPartitionerTests, CreateAbrClient) {
constexpr uint64_t kBlockCount = 0x748034;
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, kBlockCount * block_size_,
{
{GPT_DURABLE_BOOT_NAME, Uuid(kDurableBootType), 0x10400, 0x10000},
{GPT_FVM_NAME, Uuid(kNewFvmType), 0x20400, 0x10000},
}));
fidl::ClientEnd<fuchsia_io::Directory> svc_root = RealmExposedDir();
std::shared_ptr<paver::Context> context;
zx::result devices = CreateBlockDevices();
ASSERT_OK(devices);
zx::result partitioner = paver::LuisPartitionerFactory().New(
*devices, svc_root, paver::PaverConfig{.arch = paver::Arch::kArm64}, context, {});
ASSERT_OK(partitioner);
EXPECT_OK(partitioner->CreateAbrClient());
}
TEST_F(LuisPartitionerTests, SupportsPartition) {
constexpr uint64_t kBlockCount = 0x748034;
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, kBlockCount * block_size_,
{
{GPT_DURABLE_BOOT_NAME, Uuid(kDurableBootType), 0x10400, 0x10000},
{GPT_FVM_NAME, Uuid(kNewFvmType), 0x20400, 0x10000},
}));
zx::result status = CreatePartitioner(gpt_dev->ConnectToLegacyController());
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderB)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderR)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_TRUE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
// Unsupported partition type.
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kUnknown)));
// Unsupported content type.
EXPECT_FALSE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta, "foo_type")));
}
class NelsonPartitionerTests : public GptDevicePartitionerTests {
protected:
static constexpr size_t kNelsonBlockSize = 512;
static constexpr size_t kTplSize = 1024;
static constexpr size_t kBootloaderSize = paver::kNelsonBL2Size + kTplSize;
static constexpr uint8_t kBL2ImageValue = 0x01;
static constexpr uint8_t kTplImageValue = 0x02;
static constexpr size_t kTplSlotAOffset = 0x3000;
static constexpr size_t kTplSlotBOffset = 0x4000;
static constexpr size_t kUserTplBlockCount = 0x1000;
NelsonPartitionerTests() : GptDevicePartitionerTests("nelson", kNelsonBlockSize, "_a") {}
// Create a DevicePartition for a device.
zx::result<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
BlockDevice* gpt = nullptr) {
fidl::ClientEnd<fuchsia_io::Directory> svc_root = RealmExposedDir();
fidl::ClientEnd<fuchsia_device::Controller> controller;
if (gpt) {
controller = gpt->ConnectToLegacyController();
}
zx::result devices = CreateBlockDevices();
if (devices.is_error()) {
return devices.take_error();
}
return paver::NelsonPartitioner::Initialize(
*devices, svc_root, paver::PaverConfig{.arch = paver::Arch::kArm64}, std::move(controller));
}
static void CreateBootloaderPayload(zx::vmo* out) {
fzl::VmoMapper mapper;
ASSERT_OK(
mapper.CreateAndMap(kBootloaderSize, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, nullptr, out));
uint8_t* start = static_cast<uint8_t*>(mapper.start());
memset(start, kBL2ImageValue, paver::kNelsonBL2Size);
memset(start + paver::kNelsonBL2Size, kTplImageValue, kTplSize);
}
void TestBootloaderWrite(const PartitionSpec& spec, uint8_t tpl_a_expected,
uint8_t tpl_b_expected) {
std::unique_ptr<BlockDevice> gpt_dev, boot0, boot1;
ASSERT_NO_FATAL_FAILURE(InitializeBlockDeviceForBootloaderTest(&gpt_dev, &boot0, &boot1));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
{
auto partition_client = partitioner->FindPartition(spec);
ASSERT_OK(partition_client);
zx::vmo bootloader_payload;
ASSERT_NO_FATAL_FAILURE(CreateBootloaderPayload(&bootloader_payload));
ASSERT_OK(partition_client->Write(bootloader_payload, kBootloaderSize));
}
const size_t bl2_blocks = paver::kNelsonBL2Size / block_size_;
const size_t tpl_blocks = kTplSize / block_size_;
// info block stays unchanged. assume that storage data initialized as 0.
ASSERT_NO_FATAL_FAILURE(ValidateBlockContent(boot0.get(), 0, 1, 0));
ASSERT_NO_FATAL_FAILURE(ValidateBlockContent(boot0.get(), 1, bl2_blocks, kBL2ImageValue));
ASSERT_NO_FATAL_FAILURE(
ValidateBlockContent(boot0.get(), 1 + bl2_blocks, tpl_blocks, kTplImageValue));
// info block stays unchanged
ASSERT_NO_FATAL_FAILURE(ValidateBlockContent(boot1.get(), 0, 1, 0));
ASSERT_NO_FATAL_FAILURE(ValidateBlockContent(boot1.get(), 1, bl2_blocks, kBL2ImageValue));
ASSERT_NO_FATAL_FAILURE(
ValidateBlockContent(boot1.get(), 1 + bl2_blocks, tpl_blocks, kTplImageValue));
ASSERT_NO_FATAL_FAILURE(
ValidateBlockContent(gpt_dev.get(), kTplSlotAOffset, tpl_blocks, tpl_a_expected));
ASSERT_NO_FATAL_FAILURE(
ValidateBlockContent(gpt_dev.get(), kTplSlotBOffset, tpl_blocks, tpl_b_expected));
}
void TestBootloaderRead(const PartitionSpec& spec, uint8_t tpl_a_data, uint8_t tpl_b_data,
zx::result<>* out_status, uint8_t* out) {
std::unique_ptr<BlockDevice> gpt_dev, boot0, boot1;
ASSERT_NO_FATAL_FAILURE(InitializeBlockDeviceForBootloaderTest(&gpt_dev, &boot0, &boot1));
const size_t bl2_blocks = paver::kNelsonBL2Size / block_size_;
const size_t tpl_blocks = kTplSize / block_size_;
// Setup initial storage data
struct initial_storage_data {
const BlockDevice* blk_dev;
uint64_t start_block;
uint64_t size_in_blocks;
uint8_t data;
} initial_storage[] = {
{boot0.get(), 1, bl2_blocks, kBL2ImageValue}, // bl2 in boot0
{boot1.get(), 1, bl2_blocks, kBL2ImageValue}, // bl2 in boot1
{boot0.get(), 1 + bl2_blocks, tpl_blocks, kTplImageValue}, // tpl in boot0
{boot1.get(), 1 + bl2_blocks, tpl_blocks, kTplImageValue}, // tpl in boot1
{gpt_dev.get(), kTplSlotAOffset, tpl_blocks, tpl_a_data}, // tpl_a
{gpt_dev.get(), kTplSlotBOffset, tpl_blocks, tpl_b_data}, // tpl_b
};
for (auto& info : initial_storage) {
std::vector<uint8_t> data(info.size_in_blocks * block_size_, info.data);
ASSERT_NO_FATAL_FAILURE(
WriteBlocks(info.blk_dev, info.start_block, info.size_in_blocks, data.data()));
}
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
fzl::OwnedVmoMapper read_buf;
ASSERT_OK(read_buf.CreateAndMap(kBootloaderSize, "test-read-bootloader"));
auto partition_client = partitioner->FindPartition(spec);
ASSERT_OK(partition_client);
*out_status = partition_client->Read(read_buf.vmo(), kBootloaderSize);
memcpy(out, read_buf.start(), kBootloaderSize);
}
static void ValidateBootloaderRead(const uint8_t* buf, uint8_t expected_bl2,
uint8_t expected_tpl) {
for (size_t i = 0; i < paver::kNelsonBL2Size; i++) {
ASSERT_EQ(buf[i], expected_bl2, "bl2 mismatch at idx: %zu", i);
}
for (size_t i = 0; i < kTplSize; i++) {
ASSERT_EQ(buf[i + paver::kNelsonBL2Size], expected_tpl, "tpl mismatch at idx: %zu", i);
}
}
void InitializeBlockDeviceForBootloaderTest(std::unique_ptr<BlockDevice>* gpt_dev,
std::unique_ptr<BlockDevice>* boot0,
std::unique_ptr<BlockDevice>* boot1) {
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(gpt_dev, 64 * kMebibyte,
{
{"tpl_a", Uuid(kDummyType), kTplSlotAOffset, kUserTplBlockCount},
{"tpl_b", Uuid(kDummyType), kTplSlotBOffset, kUserTplBlockCount},
}));
ASSERT_NO_FATAL_FAILURE(CreateDisk(boot0, kUserTplBlockCount * kNelsonBlockSize, kBoot0Type));
ASSERT_NO_FATAL_FAILURE(CreateDisk(boot1, kUserTplBlockCount * kNelsonBlockSize, kBoot1Type));
}
void InitializeWithoutFvmSucceedsTest() {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithGpt(&gpt_dev, 32 * kGibibyte));
ASSERT_OK(CreatePartitioner());
}
void FindPartitionTest() {
// kBlockCount should be a value large enough to accommodate all partitions and blocks reserved
// by gpt. The current value is copied from the case of sherlock. The actual size of fvm
// partition on nelson is yet to be finalized.
constexpr uint64_t kBlockCount = 0x748034;
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, kBlockCount * block_size_,
{
// The initial gpt partitions are randomly chosen and does not
// necessarily reflect the actual gpt partition layout in product.
{GUID_ABR_META_NAME, Uuid(kAbrMetaType), 0x10400, 0x10000},
{"tpl_a", Uuid(kDummyType), 0x30400, 0x10000},
{"tpl_b", Uuid(kDummyType), 0x40400, 0x10000},
{"boot_a", Uuid(kZirconAType), 0x50400, 0x10000},
{"boot_b", Uuid(kZirconBType), 0x60400, 0x10000},
{"system_a", Uuid(kDummyType), 0x70400, 0x10000},
{"system_b", Uuid(kDummyType), 0x80400, 0x10000},
{GPT_VBMETA_A_NAME, Uuid(kVbMetaAType), 0x90400, 0x10000},
{GPT_VBMETA_B_NAME, Uuid(kVbMetaBType), 0xa0400, 0x10000},
{"reserved_a", Uuid(kDummyType), 0xc0400, 0x10000},
{"reserved_b", Uuid(kDummyType), 0xd0400, 0x10000},
{"reserved_c", Uuid(kVbMetaRType), 0xe0400, 0x10000},
{"cache", Uuid(kZirconRType), 0xf0400, 0x10000},
{"data", Uuid(kFvmType), 0x100400, 0x10000},
}));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
EXPECT_NOT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA)));
std::unique_ptr<BlockDevice> boot0_dev, boot1_dev;
ASSERT_NO_FATAL_FAILURE(CreateDisk(&boot0_dev, kBlockCount * kBlockSize, kBoot0Type));
ASSERT_NO_FATAL_FAILURE(CreateDisk(&boot1_dev, kBlockCount * kBlockSize, kBoot1Type));
// Make sure we can find the important partitions.
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA, "bl2")));
EXPECT_OK(
partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA, "bootloader")));
EXPECT_OK(
partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderB, "bootloader")));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA, "tpl")));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderB, "tpl")));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
}
void CreateAbrClientTest() {
constexpr uint64_t kBlockCount = 0x748034;
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, kBlockCount * block_size_,
{
{GUID_ABR_META_NAME, Uuid(kAbrMetaType), 0x10400, 0x10000},
{GPT_FVM_NAME, Uuid(kNewFvmType), 0x20400, 0x10000},
}));
fidl::ClientEnd<fuchsia_io::Directory> svc_root = RealmExposedDir();
std::shared_ptr<paver::Context> context;
zx::result devices = CreateBlockDevices();
ASSERT_OK(devices);
zx::result partitioner = paver::NelsonPartitionerFactory().New(
*devices, svc_root, paver::PaverConfig{.arch = paver::Arch::kArm64}, context, {});
ASSERT_OK(partitioner);
EXPECT_OK(partitioner->CreateAbrClient());
}
void SupportsPartitionTest() {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithGpt(&gpt_dev, 64 * kMebibyte));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
EXPECT_TRUE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderA, "bl2")));
EXPECT_TRUE(partitioner->SupportsPartition(
PartitionSpec(paver::Partition::kBootloaderA, "bootloader")));
EXPECT_TRUE(partitioner->SupportsPartition(
PartitionSpec(paver::Partition::kBootloaderB, "bootloader")));
EXPECT_TRUE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderA, "tpl")));
EXPECT_TRUE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderB, "tpl")));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_TRUE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
// Unsupported partition type.
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kUnknown)));
// Unsupported content type.
EXPECT_FALSE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta, "foo_type")));
}
void ValidatePayloadTest() {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateDiskWithGpt(&gpt_dev, 64 * kMebibyte));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
// Test invalid bootloader payload size.
std::vector<uint8_t> payload_bl2_size(paver::kNelsonBL2Size);
ASSERT_NOT_OK(
partitioner->ValidatePayload(PartitionSpec(paver::Partition::kBootloaderA, "bootloader"),
std::span<uint8_t>(payload_bl2_size)));
ASSERT_NOT_OK(
partitioner->ValidatePayload(PartitionSpec(paver::Partition::kBootloaderB, "bootloader"),
std::span<uint8_t>(payload_bl2_size)));
std::vector<uint8_t> payload_bl2_tpl_size(static_cast<size_t>(2) * 1024 * 1024);
ASSERT_OK(
partitioner->ValidatePayload(PartitionSpec(paver::Partition::kBootloaderA, "bootloader"),
std::span<uint8_t>(payload_bl2_tpl_size)));
ASSERT_OK(
partitioner->ValidatePayload(PartitionSpec(paver::Partition::kBootloaderB, "bootloader"),
std::span<uint8_t>(payload_bl2_tpl_size)));
}
void WriteBootloaderATest() {
TestBootloaderWrite(PartitionSpec(paver::Partition::kBootloaderA, "bootloader"), kTplImageValue,
0x00);
}
void WriteBootloaderBTest() {
TestBootloaderWrite(PartitionSpec(paver::Partition::kBootloaderB, "bootloader"), 0x00,
kTplImageValue);
}
void ReadBootloaderAFailTest() {
auto spec = PartitionSpec(paver::Partition::kBootloaderA, "bootloader");
std::vector<uint8_t> read_buf(kBootloaderSize);
zx::result<> status = zx::ok();
ASSERT_NO_FATAL_FAILURE(
TestBootloaderRead(spec, 0x03, kTplImageValue, &status, read_buf.data()));
ASSERT_NOT_OK(status);
}
void ReadBootloaderBFailTest() {
auto spec = PartitionSpec(paver::Partition::kBootloaderB, "bootloader");
std::vector<uint8_t> read_buf(kBootloaderSize);
zx::result<> status = zx::ok();
ASSERT_NO_FATAL_FAILURE(
TestBootloaderRead(spec, kTplImageValue, 0x03, &status, read_buf.data()));
ASSERT_NOT_OK(status);
}
void ReadBootloaderASucceedTest() {
auto spec = PartitionSpec(paver::Partition::kBootloaderA, "bootloader");
std::vector<uint8_t> read_buf(kBootloaderSize);
zx::result<> status = zx::ok();
ASSERT_NO_FATAL_FAILURE(
TestBootloaderRead(spec, kTplImageValue, 0x03, &status, read_buf.data()));
ASSERT_OK(status);
ASSERT_NO_FATAL_FAILURE(
ValidateBootloaderRead(read_buf.data(), kBL2ImageValue, kTplImageValue));
}
void ReadBootloaderBSucceedTest() {
std::vector<uint8_t> read_buf(kBootloaderSize);
auto spec = PartitionSpec(paver::Partition::kBootloaderB, "bootloader");
zx::result<> status = zx::ok();
ASSERT_NO_FATAL_FAILURE(
TestBootloaderRead(spec, 0x03, kTplImageValue, &status, read_buf.data()));
ASSERT_OK(status);
ASSERT_NO_FATAL_FAILURE(
ValidateBootloaderRead(read_buf.data(), kBL2ImageValue, kTplImageValue));
}
};
TEST_F(NelsonPartitionerTests, InitializeWithoutGptFails) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateDisk(&gpt_dev));
ASSERT_NOT_OK(CreatePartitioner());
}
TEST_F(NelsonPartitionerTests, InitializeWithoutFvmSucceeds) {
ASSERT_NO_FATAL_FAILURE(InitializeWithoutFvmSucceedsTest());
}
TEST_F(NelsonPartitionerTests, FindPartition) { ASSERT_NO_FATAL_FAILURE(FindPartitionTest()); }
TEST_F(NelsonPartitionerTests, CreateAbrClient) { ASSERT_NO_FATAL_FAILURE(CreateAbrClientTest()); }
TEST_F(NelsonPartitionerTests, SupportsPartition) {
ASSERT_NO_FATAL_FAILURE(SupportsPartitionTest());
}
TEST_F(NelsonPartitionerTests, ValidatePayload) { ASSERT_NO_FATAL_FAILURE(ValidatePayloadTest()); }
TEST_F(NelsonPartitionerTests, WriteBootloaderA) {
ASSERT_NO_FATAL_FAILURE(WriteBootloaderATest());
}
TEST_F(NelsonPartitionerTests, WriteBootloaderB) {
ASSERT_NO_FATAL_FAILURE(WriteBootloaderBTest());
}
TEST_F(NelsonPartitionerTests, ReadBootloaderAFail) {
ASSERT_NO_FATAL_FAILURE(ReadBootloaderAFailTest());
}
TEST_F(NelsonPartitionerTests, ReadBootloaderBFail) {
ASSERT_NO_FATAL_FAILURE(ReadBootloaderBFailTest());
}
TEST_F(NelsonPartitionerTests, ReadBootloaderASucceed) {
ASSERT_NO_FATAL_FAILURE(ReadBootloaderASucceedTest());
}
TEST_F(NelsonPartitionerTests, ReadBootloaderBSucceed) {
ASSERT_NO_FATAL_FAILURE(ReadBootloaderBSucceedTest());
}
class NelsonPartitionerWithStorageHostTests : public NelsonPartitionerTests {
protected:
IsolatedDevmgr::Args BaseDevmgrArgs() override {
IsolatedDevmgr::Args args;
args.enable_storage_host = true;
return args;
}
};
TEST_F(NelsonPartitionerWithStorageHostTests, InitializeWithoutFvmSucceeds) {
ASSERT_NO_FATAL_FAILURE(InitializeWithoutFvmSucceedsTest());
}
TEST_F(NelsonPartitionerWithStorageHostTests, FindPartition) {
ASSERT_NO_FATAL_FAILURE(FindPartitionTest());
}
TEST_F(NelsonPartitionerWithStorageHostTests, CreateAbrClient) {
ASSERT_NO_FATAL_FAILURE(CreateAbrClientTest());
}
TEST_F(NelsonPartitionerWithStorageHostTests, SupportsPartition) {
ASSERT_NO_FATAL_FAILURE(SupportsPartitionTest());
}
TEST_F(NelsonPartitionerWithStorageHostTests, ValidatePayload) {
ASSERT_NO_FATAL_FAILURE(ValidatePayloadTest());
}
TEST_F(NelsonPartitionerWithStorageHostTests, WriteBootloaderA) {
ASSERT_NO_FATAL_FAILURE(WriteBootloaderATest());
}
TEST_F(NelsonPartitionerWithStorageHostTests, WriteBootloaderB) {
ASSERT_NO_FATAL_FAILURE(WriteBootloaderBTest());
}
TEST_F(NelsonPartitionerWithStorageHostTests, ReadBootloaderAFail) {
ASSERT_NO_FATAL_FAILURE(ReadBootloaderAFailTest());
}
TEST_F(NelsonPartitionerWithStorageHostTests, ReadBootloaderBFail) {
ASSERT_NO_FATAL_FAILURE(ReadBootloaderBFailTest());
}
TEST_F(NelsonPartitionerWithStorageHostTests, ReadBootloaderASucceed) {
ASSERT_NO_FATAL_FAILURE(ReadBootloaderASucceedTest());
}
TEST_F(NelsonPartitionerWithStorageHostTests, ReadBootloaderBSucceed) {
ASSERT_NO_FATAL_FAILURE(ReadBootloaderBSucceedTest());
}
class Vim3PartitionerTests : public GptDevicePartitionerTests {
protected:
static constexpr const char kDummyBootloaderHeader[] = "bootloader";
Vim3PartitionerTests() : GptDevicePartitionerTests("vim3") {}
// Create a DevicePartition for a device.
zx::result<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
BlockDevice* gpt = nullptr) {
fidl::ClientEnd<fuchsia_io::Directory> svc_root = RealmExposedDir();
fidl::ClientEnd<fuchsia_device::Controller> controller;
if (gpt) {
controller = gpt->ConnectToLegacyController();
}
zx::result devices = CreateBlockDevices();
if (devices.is_error()) {
return devices.take_error();
}
return paver::Vim3Partitioner::Initialize(*devices, svc_root, paver::PaverConfig(),
std::move(controller));
}
void CreateBootloaderDevices(std::unique_ptr<BlockDevice>* boot0,
std::unique_ptr<BlockDevice>* boot1) {
zx::vmo vmo0;
ASSERT_OK(zx::vmo::create(32 * 1024 * 1024, 0, &vmo0));
// Write the first two blocks with a placeholder value we check later in VerifyBootloaderDevice.
ASSERT_OK(vmo0.write(kDummyBootloaderHeader, 0, strlen(kDummyBootloaderHeader)));
ASSERT_OK(vmo0.write(kDummyBootloaderHeader, block_size_, strlen(kDummyBootloaderHeader)));
ASSERT_NO_FATAL_FAILURE(CreateDiskWithContents(boot0, std::move(vmo0), kBoot0Type));
zx::vmo vmo1;
ASSERT_OK(zx::vmo::create(32 * 1024 * 1024, 0, &vmo1));
ASSERT_OK(vmo1.write(kDummyBootloaderHeader, 0, strlen(kDummyBootloaderHeader)));
ASSERT_OK(vmo1.write(kDummyBootloaderHeader, block_size_, strlen(kDummyBootloaderHeader)));
ASSERT_NO_FATAL_FAILURE(CreateDiskWithContents(boot1, std::move(vmo1), kBoot1Type));
}
void VerifyBootloaderDevice(const BlockDevice* device) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(block_size_, 0, &vmo));
for (size_t block = 0; block < 2; ++block) {
ASSERT_NO_FATAL_FAILURE(device->Read(vmo, block_size_, block, 0));
char data[block_size_];
ASSERT_OK(vmo.read(data, 0, block_size_));
EXPECT_EQ(std::string_view(data, strlen(kDummyBootloaderHeader)),
std::string_view(kDummyBootloaderHeader));
}
}
void InitializeWithoutGptFailsTest() {
std::unique_ptr<BlockDevice> boot0_dev;
std::unique_ptr<BlockDevice> boot1_dev;
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateBootloaderDevices(&boot0_dev, &boot1_dev));
ASSERT_NO_FATAL_FAILURE(CreateDisk(&gpt_dev));
ASSERT_NO_FATAL_FAILURE(WaitForBlockDevices(3));
ASSERT_NOT_OK(CreatePartitioner());
ASSERT_NO_FATAL_FAILURE(VerifyBootloaderDevice(boot0_dev.get()));
ASSERT_NO_FATAL_FAILURE(VerifyBootloaderDevice(boot1_dev.get()));
}
void InitializeTest() {
std::unique_ptr<BlockDevice> boot0_dev;
std::unique_ptr<BlockDevice> boot1_dev;
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateBootloaderDevices(&boot0_dev, &boot1_dev));
constexpr uint64_t kBlockCount = 0x748034;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&gpt_dev, kBlockCount * block_size_,
// partition size / location is arbitrary
{
{GPT_DURABLE_BOOT_NAME, Uuid(kDurableBootType), 0x10400, 0x10000},
{GPT_VBMETA_A_NAME, Uuid(kVbMetaType), 0x20400, 0x10000},
{GPT_VBMETA_B_NAME, Uuid(kVbMetaType), 0x30400, 0x10000},
{GPT_VBMETA_R_NAME, Uuid(kVbMetaType), 0x40400, 0x10000},
{GPT_ZIRCON_A_NAME, Uuid(kZirconType), 0x50400, 0x10000},
{GPT_ZIRCON_B_NAME, Uuid(kZirconType), 0x60400, 0x10000},
{GPT_ZIRCON_R_NAME, Uuid(kZirconType), 0x70400, 0x10000},
{GPT_FVM_NAME, Uuid(kNewFvmType), 0x80400, 0x10000},
}));
ASSERT_NO_FATAL_FAILURE(WaitForBlockDevices(3 + 8));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
// Make sure we can find the important partitions.
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
ASSERT_NO_FATAL_FAILURE(VerifyBootloaderDevice(boot0_dev.get()));
ASSERT_NO_FATAL_FAILURE(VerifyBootloaderDevice(boot1_dev.get()));
}
};
TEST_F(Vim3PartitionerTests, InitializeWithoutGptFails) {
ASSERT_NO_FATAL_FAILURE(InitializeWithoutGptFailsTest());
}
TEST_F(Vim3PartitionerTests, Initialize) { ASSERT_NO_FATAL_FAILURE(InitializeTest()); }
class Vim3PartitionerWithStorageHostTests : public Vim3PartitionerTests {
protected:
IsolatedDevmgr::Args BaseDevmgrArgs() override {
IsolatedDevmgr::Args args;
args.enable_storage_host = true;
return args;
}
};
TEST_F(Vim3PartitionerWithStorageHostTests, Initialize) {
ASSERT_NO_FATAL_FAILURE(InitializeTest());
}
class AndroidPartitionerTests : public GptDevicePartitionerTests {
protected:
AndroidPartitionerTests() = default;
IsolatedDevmgr::Args BaseDevmgrArgs() override {
IsolatedDevmgr::Args args;
args.disable_block_watcher = false;
args.fshost_config.emplace_back(component_testing::ConfigCapability{
.name = "fuchsia.fshost.GptAll", .value = component_testing::ConfigValue::Bool(true)});
return args;
}
// Create a DevicePartition for a device.
zx::result<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
BlockDevice* gpt = nullptr) {
fidl::ClientEnd<fuchsia_io::Directory> svc_root = RealmExposedDir();
fidl::ClientEnd<fuchsia_device::Controller> controller;
if (gpt) {
controller = gpt->ConnectToLegacyController();
}
zx::result devices = CreateBlockDevices();
if (devices.is_error()) {
return devices.take_error();
}
return paver::AndroidDevicePartitioner::Initialize(
*devices, svc_root, paver::PaverConfig{.arch = paver::Arch::kX64}, std::move(controller),
{});
}
void InitializeWithoutGptFailsTest() {
std::unique_ptr<BlockDevice> primary_gpt_dev;
ASSERT_NO_FATAL_FAILURE(CreateDisk(&primary_gpt_dev));
ASSERT_NO_FATAL_FAILURE(WaitForBlockDevices(1));
ASSERT_NOT_OK(CreatePartitioner());
}
void InitializeTest() {
std::unique_ptr<BlockDevice> primary_gpt_dev;
std::unique_ptr<BlockDevice> other_gpt_dev;
constexpr uint64_t kBlockCount = 0x748034;
ASSERT_NO_FATAL_FAILURE(
CreateDiskWithGpt(&primary_gpt_dev, kBlockCount * block_size_,
// partition size / location is arbitrary
{
{"misc", Uuid(kAbrMetaType), 0x10400, 0x10000},
{"vbmeta_a", Uuid(kVbMetaType), 0x20400, 0x10000},
{"vbmeta_b", Uuid(kVbMetaType), 0x30400, 0x10000},
{"boot_a", Uuid(kBootloaderType), 0x50400, 0x10000},
{"boot_b", Uuid(kBootloaderType), 0x60400, 0x10000},
{"vendor_boot_a", Uuid(kZirconType), 0x70400, 0x10000},
{"vendor_boot_b", Uuid(kZirconType), 0x80400, 0x10000},
{"super", Uuid(kNewFvmType), 0x90400, 0x10000},
}));
ASSERT_NO_FATAL_FAILURE(CreateDiskWithGpt(&other_gpt_dev, 512 * block_size_,
// partition size / location is arbitrary
{
{"vbmeta", Uuid(kVbMetaType), 0x30, 0x1},
{"frp", Uuid::Generate(), 0x31, 0x1},
}));
ASSERT_NO_FATAL_FAILURE(WaitForBlockDevices(2 + 2 + 8));
zx::result status = CreatePartitioner();
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
// Make sure we can find the important partitions.
EXPECT_OK(
partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA, "boot_shim")));
EXPECT_OK(
partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderB, "boot_shim")));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kAbrMeta)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
}
};
TEST_F(AndroidPartitionerTests, InitializeWithoutGptFails) {
ASSERT_NO_FATAL_FAILURE(InitializeWithoutGptFailsTest());
}
TEST_F(AndroidPartitionerTests, Initialize) { ASSERT_NO_FATAL_FAILURE(InitializeTest()); }
class AndroidPartitionerWithStorageHostTests : public AndroidPartitionerTests {
protected:
IsolatedDevmgr::Args BaseDevmgrArgs() override {
IsolatedDevmgr::Args args;
args.enable_storage_host = true;
args.fshost_config.emplace_back(component_testing::ConfigCapability{
.name = "fuchsia.fshost.GptAll", .value = component_testing::ConfigValue::Bool(true)});
return args;
}
};
TEST_F(AndroidPartitionerWithStorageHostTests, Initialize) {
ASSERT_NO_FATAL_FAILURE(InitializeTest());
}
} // namespace