blob: e2c3206e704aaac4509c946f4aa63f011ee83a8b [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 <fuchsia/device/llcpp/fidl.h>
#include <fuchsia/hardware/block/llcpp/fidl.h>
#include <fuchsia/hardware/nand/c/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/devmgr-integration-test/fixture.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 <zircon/boot/image.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 <iostream>
#include <memory>
#include <string_view>
#include <utility>
#include <fbl/auto_call.h>
#include <fbl/span.h>
#include <gpt/cros.h>
#include <gpt/gpt.h>
#include <soc/aml-common/aml-guid.h>
#include <zxtest/zxtest.h>
#include "src/storage/lib/paver/as370.h"
#include "src/storage/lib/paver/astro.h"
#include "src/storage/lib/paver/chromebook-x64.h"
#include "src/storage/lib/paver/luis.h"
#include "src/storage/lib/paver/nelson.h"
#include "src/storage/lib/paver/sherlock.h"
#include "src/storage/lib/paver/test/test-utils.h"
#include "src/storage/lib/paver/utils.h"
#include "src/storage/lib/paver/vim3.h"
#include "src/storage/lib/paver/x64.h"
namespace paver {
extern zx_duration_t g_wipe_timeout;
}
namespace {
constexpr uint64_t kMebibyte = 1024 * 1024;
constexpr uint64_t kGibibyte = kMebibyte * 1024;
constexpr uint64_t kTebibyte = kGibibyte * 1024;
using devmgr_integration_test::RecursiveWaitForFile;
using driver_integration_test::IsolatedDevmgr;
using paver::BlockWatcherPauser;
using paver::PartitionSpec;
// 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 kCrosKernelType[GPT_GUID_LEN] = GUID_CROS_KERNEL_VALUE;
constexpr uint8_t kCrosRootfsType[GPT_GUID_LEN] = GUID_CROS_ROOTFS_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 kSysConfigType[GPT_GUID_LEN] = GUID_SYS_CONFIG_VALUE;
constexpr uint8_t kAbrMetaType[GPT_GUID_LEN] = GUID_ABR_META_VALUE;
// constexpr uint8_t kStateCrosGuid[GPT_GUID_LEN] = GUID_CROS_STATE_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};
const fbl::unique_fd kDummyDevice;
constexpr fuchsia_hardware_nand_RamNandInfo kNandInfo = {
.vmo = ZX_HANDLE_INVALID,
.nand_info =
{
.page_size = kPageSize,
.pages_per_block = kPagesPerBlock,
.num_blocks = kNumBlocks,
.ecc_bits = 8,
.oob_size = kOobSize,
.nand_class = fuchsia_hardware_nand_Class_PARTMAP,
.partition_guid = {},
},
.partition_map =
{
.device_guid = {},
.partition_count = 7,
.partitions =
{
{
.type_guid = {},
.unique_guid = {},
.first_block = 0,
.last_block = 3,
.copy_count = 0,
.copy_byte_offset = 0,
.name = {},
.hidden = true,
.bbt = true,
},
{
.type_guid = GUID_BOOTLOADER_VALUE,
.unique_guid = {},
.first_block = 4,
.last_block = 7,
.copy_count = 0,
.copy_byte_offset = 0,
.name = {'b', 'o', 'o', 't', 'l', 'o', 'a', 'd', 'e', 'r'},
.hidden = false,
.bbt = false,
},
{
.type_guid = GUID_ZIRCON_A_VALUE,
.unique_guid = {},
.first_block = 8,
.last_block = 9,
.copy_count = 0,
.copy_byte_offset = 0,
.name = {'z', 'i', 'r', 'c', 'o', 'n', '-', 'a'},
.hidden = false,
.bbt = false,
},
{
.type_guid = GUID_ZIRCON_B_VALUE,
.unique_guid = {},
.first_block = 10,
.last_block = 11,
.copy_count = 0,
.copy_byte_offset = 0,
.name = {'z', 'i', 'r', 'c', 'o', 'n', '-', 'b'},
.hidden = false,
.bbt = false,
},
{
.type_guid = GUID_ZIRCON_R_VALUE,
.unique_guid = {},
.first_block = 12,
.last_block = 13,
.copy_count = 0,
.copy_byte_offset = 0,
.name = {'z', 'i', 'r', 'c', 'o', 'n', '-', 'r'},
.hidden = false,
.bbt = false,
},
{
.type_guid = GUID_SYS_CONFIG_VALUE,
.unique_guid = {},
.first_block = 14,
.last_block = 17,
.copy_count = 0,
.copy_byte_offset = 0,
.name = {'s', 'y', 's', 'c', 'o', 'n', 'f', 'i', 'g'},
.hidden = false,
.bbt = false,
},
{
.type_guid = GUID_BL2_VALUE,
.unique_guid = {},
.first_block = 18,
.last_block = 22,
.copy_count = 0,
.copy_byte_offset = 0,
.name =
{
'b',
'l',
'2',
},
.hidden = false,
.bbt = false,
},
},
},
.export_nand_config = true,
.export_partition_map = true,
};
// Returns the start address of the given partition in |mapper|, or nullptr if
// the partition doesn't exist in |nand_info|.
uint8_t* PartitionStart(const fzl::VmoMapper& mapper,
const fuchsia_hardware_nand_RamNandInfo& nand_info,
const std::array<uint8_t, GPT_GUID_LEN> guid) {
const auto& map = nand_info.partition_map;
const auto* partitions_begin = map.partitions;
const auto* partitions_end = &map.partitions[map.partition_count];
const auto* part = std::find_if(partitions_begin, partitions_end,
[&guid](const fuchsia_hardware_nand_Partition& p) {
return memcmp(p.type_guid, guid.data(), guid.size()) == 0;
});
if (part == partitions_end) {
return nullptr;
}
return reinterpret_cast<uint8_t*>(mapper.start()) +
(part->first_block * kPageSize * kPagesPerBlock);
}
struct PartitionDescription {
const char* name;
const uint8_t* type;
uint64_t start;
uint64_t length;
};
uint8_t* GetRandomGuid() {
static uint8_t random_guid[GPT_GUID_LEN];
zx_cprng_draw(random_guid, GPT_GUID_LEN);
return random_guid;
}
void utf16_to_cstring(char* dst, const uint8_t* src, size_t charcount) {
while (charcount > 0) {
*dst++ = *src;
src += 2;
charcount -= 2;
}
}
// Find a partition with the given label.
//
// Returns nullptr if no partitions exist, or multiple partitions exist with
// the same label.
//
// Note: some care must be used with this function: the UEFI standard makes no guarantee
// that a GPT won't contain two partitions with the same label; for test data, using
// label names is convenient, however.
gpt_partition_t* FindPartitionWithLabel(const gpt::GptDevice* gpt, std::string_view name) {
gpt_partition_t* result = nullptr;
for (uint32_t i = 0; i < gpt->EntryCount(); i++) {
auto* gpt_part = gpt->GetPartition(i);
if (gpt_part == nullptr) {
continue;
}
// Convert UTF-16 partition label to ASCII.
char cstring_name[GPT_NAME_LEN + 1] = {};
utf16_to_cstring(cstring_name, gpt_part->name, GPT_NAME_LEN);
cstring_name[GPT_NAME_LEN] = 0;
auto partition_name = std::string_view(cstring_name, strlen(cstring_name));
// Ignore partitions with the incorrect name.
if (partition_name != name) {
continue;
}
// If we have already found a partition with the label, we've discovered
// multiple partitions with the same label. Return nullptr.
if (result != nullptr) {
printf("Found multiple partitions with label '%s'.\n", std::string(name).c_str());
return nullptr;
}
result = gpt_part;
}
return result;
}
// Ensure that the partitions on the device matches the given list.
void EnsurePartitionsMatch(const gpt::GptDevice* gpt,
fbl::Span<const PartitionDescription> expected) {
for (auto& part : expected) {
gpt_partition_t* gpt_part = FindPartitionWithLabel(gpt, part.name);
ASSERT_TRUE(gpt_part != nullptr, "Partition \"%s\" not found", part.name);
EXPECT_TRUE(memcmp(part.type, gpt_part->type, GPT_GUID_LEN) == 0);
EXPECT_EQ(part.start, gpt_part->first);
EXPECT_EQ(part.start + part.length - 1, gpt_part->last);
}
}
constexpr paver::Partition kUnknownPartition = static_cast<paver::Partition>(1000);
TEST(PartitionName, Bootloader) {
EXPECT_STR_EQ(PartitionName(paver::Partition::kBootloaderA, paver::PartitionScheme::kNew),
GPT_BOOTLOADER_A_NAME);
EXPECT_STR_EQ(PartitionName(paver::Partition::kBootloaderB, paver::PartitionScheme::kNew),
GPT_BOOTLOADER_B_NAME);
EXPECT_STR_EQ(PartitionName(paver::Partition::kBootloaderR, paver::PartitionScheme::kNew),
GPT_BOOTLOADER_R_NAME);
EXPECT_STR_EQ(PartitionName(paver::Partition::kBootloaderA, paver::PartitionScheme::kLegacy),
GUID_EFI_NAME);
EXPECT_STR_EQ(PartitionName(paver::Partition::kBootloaderB, paver::PartitionScheme::kLegacy),
GUID_EFI_NAME);
EXPECT_STR_EQ(PartitionName(paver::Partition::kBootloaderR, paver::PartitionScheme::kLegacy),
GUID_EFI_NAME);
}
TEST(PartitionName, AbrMetadata) {
EXPECT_STR_EQ(PartitionName(paver::Partition::kAbrMeta, paver::PartitionScheme::kNew),
GPT_DURABLE_BOOT_NAME);
EXPECT_STR_EQ(PartitionName(paver::Partition::kAbrMeta, paver::PartitionScheme::kLegacy),
GUID_ABR_META_NAME);
}
TEST(PartitionName, UnknownPartition) {
// We don't define what is returned in this case, but it shouldn't crash and
// it should be non-empty.
EXPECT_STR_NE(PartitionName(kUnknownPartition, paver::PartitionScheme::kNew), "");
EXPECT_STR_NE(PartitionName(kUnknownPartition, paver::PartitionScheme::kLegacy), "");
}
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)");
}
TEST(PartitionSpec, ToStringUnknownPartition) {
EXPECT_NE(PartitionSpec(kUnknownPartition).ToString(), "");
EXPECT_NE(PartitionSpec(kUnknownPartition, "foo").ToString(), "");
}
class GptDevicePartitionerTests : public zxtest::Test {
protected:
GptDevicePartitionerTests(fbl::String board_name = fbl::String(), uint32_t block_size = 512)
: block_size_(block_size) {
paver::g_wipe_timeout = 0;
IsolatedDevmgr::Args args;
args.driver_search_paths.push_back("/boot/driver");
args.disable_block_watcher = false;
args.board_name = board_name;
ASSERT_OK(IsolatedDevmgr::Create(&args, &devmgr_));
fbl::unique_fd fd;
ASSERT_OK(RecursiveWaitForFile(devmgr_.devfs_root(), "misc/ramctl", &fd));
ASSERT_OK(RecursiveWaitForFile(devmgr_.devfs_root(), "sys/platform", &fd));
}
zx::channel GetSvcRoot() {
const zx::channel& fshost_root = devmgr_.fshost_outgoing_dir();
zx::channel local, remote;
auto status = zx::channel::create(0, &local, &remote);
if (status != ZX_OK) {
return zx::channel();
}
status = fdio_service_connect_at(fshost_root.get(), "svc", remote.release());
if (status != ZX_OK) {
std::cout << "Failed to connect to fshost svc dir: " << zx_status_get_string(status)
<< std::endl;
return zx::channel();
}
return local;
}
// Create a disk with the default size for a BlockDevice.
void CreateDisk(std::unique_ptr<BlockDevice>* disk) {
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kEmptyType, disk));
}
// Create a disk with the given size in bytes.
void CreateDisk(uint64_t bytes, std::unique_ptr<BlockDevice>* disk) {
ASSERT_TRUE(bytes % block_size_ == 0);
uint64_t num_blocks = bytes / block_size_;
ASSERT_NO_FATAL_FAILURES(
BlockDevice::Create(devmgr_.devfs_root(), kEmptyType, num_blocks, block_size_, disk));
}
// Create a disk with the given size in bytes and the given type.
void CreateDisk(uint64_t bytes, const uint8_t* type, std::unique_ptr<BlockDevice>* disk) {
ASSERT_TRUE(bytes % block_size_ == 0);
uint64_t num_blocks = bytes / block_size_;
ASSERT_NO_FATAL_FAILURES(
BlockDevice::Create(devmgr_.devfs_root(), type, num_blocks, block_size_, disk));
}
// Create a disk with a given size, and allocate some extra room for the GPT
void CreateDiskWithGpt(uint64_t bytes, std::unique_ptr<BlockDevice>* disk) {
ASSERT_TRUE(bytes % block_size_ == 0);
uint64_t num_blocks = bytes / block_size_;
// Ensure there's always enough space for the GPT.
num_blocks += kGptBlockCount;
ASSERT_NO_FATAL_FAILURES(
BlockDevice::Create(devmgr_.devfs_root(), kEmptyType, num_blocks, block_size_, disk));
}
// Create GPT from a device.
void CreateGptDevice(BlockDevice* device, std::unique_ptr<gpt::GptDevice>* gpt) {
ASSERT_OK(gpt::GptDevice::Create(device->fd(), /*block_size=*/device->block_size(),
/*blocks=*/device->block_count(), gpt));
ASSERT_OK((*gpt)->Sync());
}
void InitializeStartingGPTPartitions(BlockDevice* gpt_dev,
const std::vector<PartitionDescription>& init_partitions) {
// Pause the block watcher while we write partitions to the disk.
// This is to avoid the block watcher seeing an intermediate state of the partition table
// and incorrectly treating it as an MBR.
// The watcher is automatically resumed when this goes out of scope.
auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_OK(
gpt::GptDevice::Create(gpt_dev->fd(), gpt_dev->block_size(), gpt_dev->block_count(), &gpt));
ASSERT_OK(gpt->Sync());
for (const auto& part : init_partitions) {
ASSERT_OK(
gpt->AddPartition(part.name, part.type, GetRandomGuid(), part.start, part.length, 0),
"%s", part.name);
}
ASSERT_OK(gpt->Sync());
fdio_cpp::UnownedFdioCaller caller(gpt_dev->fd());
auto result = ::llcpp::fuchsia::device::Controller::Call::Rebind(
caller.channel(), fidl::StringView("/boot/driver/gpt.so"));
ASSERT_TRUE(result.ok());
ASSERT_FALSE(result->result.is_err());
}
void ReadBlocks(const BlockDevice* blk_dev, size_t offset_in_blocks, size_t size_in_blocks,
uint8_t* out) {
zx::channel gpt_chan;
ASSERT_OK(fdio_fd_clone(blk_dev->fd(), gpt_chan.reset_and_get_address()));
auto block_client = std::make_unique<paver::BlockPartitionClient>(std::move(gpt_chan));
zx::vmo vmo;
const size_t vmo_size = size_in_blocks * block_size_;
ASSERT_OK(zx::vmo::create(vmo_size, 0, &vmo));
ASSERT_OK(block_client->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) {
zx::channel gpt_chan;
ASSERT_OK(fdio_fd_clone(blk_dev->fd(), gpt_chan.reset_and_get_address()));
auto block_client = std::make_unique<paver::BlockPartitionClient>(std::move(gpt_chan));
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_OK(block_client->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_FAILURES(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);
}
}
IsolatedDevmgr devmgr_;
const uint32_t block_size_;
};
TEST_F(GptDevicePartitionerTests, AddPartitionAtLargeOffset) {
std::unique_ptr<BlockDevice> gpt_dev;
// Create 2TB disk
ASSERT_NO_FATAL_FAILURES(CreateDisk(2 * kTebibyte, &gpt_dev));
{
// Pause the block watcher while we write partitions to the disk.
// This is to avoid the block watcher seeing an intermediate state of the partition table
// and incorrectly treating it as an MBR.
// The watcher is automatically resumed when this goes out of scope.
auto pauser = BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
// Add a dummy partition of large size (~1.9TB)
ASSERT_OK(
gpt->AddPartition("dummy-partition", kEfiType, GetRandomGuid(), 0x1000, 0xF0000000, 0),
"%s", "dummy-partition");
ASSERT_OK(gpt->Sync());
}
// Iniitialize paver gpt device partitioner
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = paver::GptDevicePartitioner::InitializeGpt(devmgr_.devfs_root().duplicate(),
GetSvcRoot(), std::move(gpt_fd));
ASSERT_OK(status);
// Check if a partition can be added after the "dummy-partition"
ASSERT_OK(status->gpt->AddPartition("test", uuid::Uuid(GUID_FVM_VALUE), 15LU * kGibibyte, 0));
}
class EfiDevicePartitionerTests : public GptDevicePartitionerTests {
protected:
EfiDevicePartitionerTests() : GptDevicePartitionerTests(fbl::String(), 512) {}
// Create a DevicePartition for a device.
zx::status<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
const fbl::unique_fd& device) {
zx::channel svc_root = GetSvcRoot();
return paver::EfiDevicePartitioner::Initialize(devmgr_.devfs_root().duplicate(),
std::move(svc_root), paver::Arch::kX64,
std::move(device));
}
};
TEST_F(EfiDevicePartitionerTests, InitializeWithoutGptFails) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(&gpt_dev));
ASSERT_NOT_OK(CreatePartitioner(kDummyDevice));
}
TEST_F(EfiDevicePartitionerTests, InitializeWithoutFvmSucceeds) {
std::unique_ptr<BlockDevice> gpt_dev;
// 32GiB disk.
constexpr uint64_t kBlockCount = (32LU << 30) / kBlockSize;
ASSERT_NO_FATAL_FAILURES(
BlockDevice::Create(devmgr_.devfs_root(), kEmptyType, kBlockCount, &gpt_dev));
// Set up a valid GPT.
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_OK(gpt::GptDevice::Create(gpt_dev->fd(), kBlockSize, kBlockCount, &gpt));
ASSERT_OK(gpt->Sync());
ASSERT_OK(CreatePartitioner(kDummyDevice));
}
TEST_F(EfiDevicePartitionerTests, InitializeTwoCandidatesWithoutFvmFails) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(&gpt_dev));
// Set up a valid GPT.
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
std::unique_ptr<BlockDevice> gpt_dev2;
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kEmptyType, &gpt_dev2));
// Set up a valid GPT.
std::unique_ptr<gpt::GptDevice> gpt2;
ASSERT_OK(gpt::GptDevice::Create(gpt_dev->fd(), kBlockSize, kBlockCount, &gpt2));
ASSERT_OK(gpt2->Sync());
ASSERT_NOT_OK(CreatePartitioner(kDummyDevice));
}
TEST_F(EfiDevicePartitionerTests, AddPartitionZirconB) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDiskWithGpt(128 * kMebibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
ASSERT_OK(status->AddPartition(PartitionSpec(paver::Partition::kZirconB)));
}
TEST_F(EfiDevicePartitionerTests, AddPartitionFvm) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDiskWithGpt(16 * kGibibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
ASSERT_OK(status->AddPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
}
TEST_F(EfiDevicePartitionerTests, AddPartitionTooSmall) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(&gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
ASSERT_NOT_OK(status->AddPartition(PartitionSpec(paver::Partition::kZirconB)));
}
TEST_F(EfiDevicePartitionerTests, AddedPartitionIsFindable) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDiskWithGpt(128 * kMebibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
ASSERT_OK(status->AddPartition(PartitionSpec(paver::Partition::kZirconB)));
ASSERT_OK(status->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
ASSERT_NOT_OK(status->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
}
TEST_F(EfiDevicePartitionerTests, InitializePartitionsWithoutExplicitDevice) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDiskWithGpt(16 * kGibibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
ASSERT_OK(status->AddPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
status.value().reset();
// Note that this time we don't pass in a block device fd.
ASSERT_OK(CreatePartitioner(kDummyDevice));
}
TEST_F(EfiDevicePartitionerTests, InitializeWithMultipleCandidateGPTsFailsWithoutExplicitDevice) {
std::unique_ptr<BlockDevice> gpt_dev1, gpt_dev2;
ASSERT_NO_FATAL_FAILURES(CreateDiskWithGpt(16 * kGibibyte, &gpt_dev1));
fbl::unique_fd gpt_fd(dup(gpt_dev1->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
ASSERT_OK(status->AddPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
status.value().reset();
ASSERT_NO_FATAL_FAILURES(CreateDiskWithGpt(16 * kGibibyte, &gpt_dev2));
gpt_fd.reset(dup(gpt_dev2->fd()));
auto status2 = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status2);
ASSERT_OK(status2->AddPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
status2.value().reset();
// Note that this time we don't pass in a block device fd.
ASSERT_NOT_OK(CreatePartitioner(kDummyDevice));
}
TEST_F(EfiDevicePartitionerTests, InitializeWithTwoCandidateGPTsSucceedsAfterWipingOne) {
std::unique_ptr<BlockDevice> gpt_dev1, gpt_dev2;
ASSERT_NO_FATAL_FAILURES(CreateDiskWithGpt(16 * kGibibyte, &gpt_dev1));
fbl::unique_fd gpt_fd(dup(gpt_dev1->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
ASSERT_OK(status->AddPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
status.value().reset();
ASSERT_NO_FATAL_FAILURES(CreateDiskWithGpt(16 * kGibibyte, &gpt_dev2));
gpt_fd.reset(dup(gpt_dev2->fd()));
auto status2 = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status2);
ASSERT_OK(status2->AddPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
ASSERT_OK(status2->WipeFvm());
status2.value().reset();
// Note that this time we don't pass in a block device fd.
ASSERT_OK(CreatePartitioner(kDummyDevice));
}
TEST_F(EfiDevicePartitionerTests, AddedPartitionRemovedAfterWipePartitions) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDiskWithGpt(128 * kMebibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
ASSERT_OK(status->AddPartition(PartitionSpec(paver::Partition::kZirconB)));
ASSERT_OK(status->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
ASSERT_OK(status->WipePartitionTables());
ASSERT_NOT_OK(status->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
}
TEST_F(EfiDevicePartitionerTests, FindOldBootloaderPartitionName) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(32 * kGibibyte, &gpt_dev));
{
// Pause the block watcher while we write partitions to the disk.
// This is to avoid the block watcher seeing an intermediate state of the partition table
// and incorrectly treating it as an MBR.
// The watcher is automatically resumed when this goes out of scope.
auto pauser = BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
ASSERT_OK(gpt->AddPartition("efi-system", kEfiType, GetRandomGuid(), 0x22, 0x8000, 0));
ASSERT_OK(gpt->Sync());
}
fdio_cpp::UnownedFdioCaller caller(gpt_dev->fd());
auto result = ::llcpp::fuchsia::device::Controller::Call::Rebind(
caller.channel(), fidl::StringView("/boot/driver/gpt.so"));
ASSERT_TRUE(result.ok());
ASSERT_FALSE(result->result.is_err());
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto partitioner = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(partitioner);
ASSERT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA)));
}
TEST_F(EfiDevicePartitionerTests, InitPartitionTables) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(32 * kGibibyte, &gpt_dev));
{
// Pause the block watcher while we write partitions to the disk.
// This is to avoid the block watcher seeing an intermediate state of the partition table
// and incorrectly treating it as an MBR.
// The watcher is automatically resumed when this goes out of scope.
auto pauser = BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
// Write initial partitions to disk.
const std::array<PartitionDescription, 11> partitions_at_start{
PartitionDescription{"efi", kEfiType, 0x22, 0x1},
PartitionDescription{"efi-system", kEfiType, 0x23, 0x8000},
PartitionDescription{GUID_EFI_NAME, kEfiType, 0x8023, 0x8000},
PartitionDescription{"ZIRCON-A", kZirconAType, 0x10023, 0x1},
PartitionDescription{"zircon_b", kZirconBType, 0x10024, 0x1},
PartitionDescription{"zircon r", kZirconRType, 0x10025, 0x1},
PartitionDescription{"vbmeta-a", kVbMetaAType, 0x10026, 0x1},
PartitionDescription{"VBMETA_B", kVbMetaBType, 0x10027, 0x1},
PartitionDescription{"VBMETA R", kVbMetaRType, 0x10028, 0x1},
PartitionDescription{"abrmeta", kAbrMetaType, 0x10029, 0x1},
PartitionDescription{"FVM", kFvmType, 0x10030, 0x1},
};
for (auto& part : partitions_at_start) {
ASSERT_OK(
gpt->AddPartition(part.name, part.type, GetRandomGuid(), part.start, part.length, 0),
"%s", part.name);
}
ASSERT_OK(gpt->Sync());
}
// Create EFI device partitioner and initialise partition tables.
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_OK(partitioner->InitPartitionTables());
// Ensure the final partition layout looks like we expect it to.
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
const std::array<PartitionDescription, 10> partitions_at_end{
PartitionDescription{"efi", kEfiType, 0x22, 0x1},
PartitionDescription{GUID_EFI_NAME, kEfiType, 0x23, 0x8000},
PartitionDescription{GUID_ZIRCON_A_NAME, kZirconAType, 0x8023, 0x40000},
PartitionDescription{GUID_ZIRCON_B_NAME, kZirconBType, 0x48023, 0x40000},
PartitionDescription{GUID_ZIRCON_R_NAME, kZirconRType, 0x88023, 0x60000},
PartitionDescription{GUID_VBMETA_A_NAME, kVbMetaAType, 0xe8023, 0x80},
PartitionDescription{GUID_VBMETA_B_NAME, kVbMetaBType, 0xe80a3, 0x80},
PartitionDescription{GUID_VBMETA_R_NAME, kVbMetaRType, 0xe8123, 0x80},
PartitionDescription{GUID_ABR_META_NAME, kAbrMetaType, 0xe81a3, 0x8},
PartitionDescription{GUID_FVM_NAME, kFvmType, 0xe81ab, 0x2000000},
};
ASSERT_NO_FATAL_FAILURES(EnsurePartitionsMatch(gpt.get(), 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)));
// 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, SupportsPartition) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(1 * kGibibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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)));
// 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_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(1 * kGibibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
// Test invalid partitions.
ASSERT_NOT_OK(partitioner->ValidatePayload(PartitionSpec(paver::Partition::kZirconA),
fbl::Span<uint8_t>()));
ASSERT_NOT_OK(partitioner->ValidatePayload(PartitionSpec(paver::Partition::kZirconB),
fbl::Span<uint8_t>()));
ASSERT_NOT_OK(partitioner->ValidatePayload(PartitionSpec(paver::Partition::kZirconR),
fbl::Span<uint8_t>()));
// Non-kernel partitions are not validated.
ASSERT_OK(partitioner->ValidatePayload(PartitionSpec(paver::Partition::kAbrMeta),
fbl::Span<uint8_t>()));
}
class CrosDevicePartitionerTests : public GptDevicePartitionerTests {
protected:
CrosDevicePartitionerTests() : GptDevicePartitionerTests(fbl::String(), 512) {}
// Create a DevicePartition for a device.
void CreatePartitioner(BlockDevice* device,
std::unique_ptr<paver::DevicePartitioner>* partitioner) {
zx::channel svc_root = GetSvcRoot();
auto status = paver::CrosDevicePartitioner::Initialize(devmgr_.devfs_root().duplicate(),
std::move(svc_root), paver::Arch::kX64,
fbl::unique_fd{dup(device->fd())});
ASSERT_OK(status);
*partitioner = std::move(status.value());
}
};
TEST_F(CrosDevicePartitionerTests, InitPartitionTables) {
std::unique_ptr<BlockDevice> disk;
ASSERT_NO_FATAL_FAILURES(CreateDisk(32 * kGibibyte, &disk));
{
// Pause the block watcher while we write partitions to the disk.
// This is to avoid the block watcher seeing an intermediate state of the partition table
// and incorrectly treating it as an MBR.
// The watcher is automatically resumed when this goes out of scope.
auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
// Write initial partitions to disk.
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(disk.get(), &gpt));
const std::array<PartitionDescription, 5> partitions_at_start{
PartitionDescription{"SYSCFG", kSysConfigType, 0x22, 0x800},
PartitionDescription{"ZIRCON-A", kCrosKernelType, 0x822, 0x20000},
PartitionDescription{"ZIRCON-B", kCrosKernelType, 0x20822, 0x20000},
PartitionDescription{"ZIRCON-R", kCrosKernelType, 0x40822, 0x20000},
PartitionDescription{"fvm", kFvmType, 0x60822, 0x1000000},
};
for (auto& part : partitions_at_start) {
ASSERT_OK(
gpt->AddPartition(part.name, part.type, GetRandomGuid(), part.start, part.length, 0),
"%s", part.name);
}
ASSERT_OK(gpt->Sync());
}
// Create CrOS device partitioner and initialise partition tables.
std::unique_ptr<paver::DevicePartitioner> partitioner;
ASSERT_NO_FATAL_FAILURES(CreatePartitioner(disk.get(), &partitioner));
ASSERT_OK(partitioner->InitPartitionTables());
// Ensure the final partition layout looks like we expect it to.
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(disk.get(), &gpt));
const std::array<PartitionDescription, 4> partitions_at_end{
PartitionDescription{GUID_ZIRCON_A_NAME, kCrosKernelType, 0x822, 0x20000},
PartitionDescription{GUID_ZIRCON_B_NAME, kCrosKernelType, 0x20822, 0x20000},
PartitionDescription{GUID_ZIRCON_R_NAME, kCrosKernelType, 0x40822, 0x20000},
PartitionDescription{GUID_FVM_NAME, kFvmType, 0x60822, 0x2000000},
};
ASSERT_NO_FATAL_FAILURES(EnsurePartitionsMatch(gpt.get(), 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::kFuchsiaVolumeManager)));
}
TEST_F(CrosDevicePartitionerTests, SupportsPartition) {
// Create a 32 GiB disk.
std::unique_ptr<BlockDevice> disk;
ASSERT_NO_FATAL_FAILURES(CreateDisk(32 * kGibibyte, &disk));
// Create EFI device partitioner and initialise partition tables.
std::unique_ptr<paver::DevicePartitioner> partitioner;
ASSERT_NO_FATAL_FAILURES(CreatePartitioner(disk.get(), &partitioner));
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::kFuchsiaVolumeManager)));
// Unsupported partition type.
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kUnknown)));
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderA)));
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta)));
// Unsupported content type.
EXPECT_FALSE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA, "foo_type")));
}
TEST_F(CrosDevicePartitionerTests, ValidatePayload) {
// Create a 32 GiB disk.
std::unique_ptr<BlockDevice> disk;
ASSERT_NO_FATAL_FAILURES(CreateDisk(32 * kGibibyte, &disk));
// Create EFI device partitioner and initialise partition tables.
std::unique_ptr<paver::DevicePartitioner> partitioner;
ASSERT_NO_FATAL_FAILURES(CreatePartitioner(disk.get(), &partitioner));
// Test invalid partitions.
ASSERT_NOT_OK(partitioner->ValidatePayload(PartitionSpec(paver::Partition::kZirconA),
fbl::Span<uint8_t>()));
ASSERT_NOT_OK(partitioner->ValidatePayload(PartitionSpec(paver::Partition::kZirconB),
fbl::Span<uint8_t>()));
ASSERT_NOT_OK(partitioner->ValidatePayload(PartitionSpec(paver::Partition::kZirconR),
fbl::Span<uint8_t>()));
// Test valid partition.
constexpr std::string_view kChromeOsMagicHeader = "CHROMEOS";
ASSERT_OK(partitioner->ValidatePayload(
PartitionSpec(paver::Partition::kZirconA),
fbl::Span<const uint8_t>(reinterpret_cast<const uint8_t*>(kChromeOsMagicHeader.data()),
kChromeOsMagicHeader.size())));
// Non-kernel partitions are not validated.
ASSERT_OK(partitioner->ValidatePayload(PartitionSpec(paver::Partition::kFuchsiaVolumeManager),
fbl::Span<uint8_t>()));
}
TEST_F(CrosDevicePartitionerTests, InitPartitionTablesForRecoveredDevice) {
std::unique_ptr<BlockDevice> disk;
ASSERT_NO_FATAL_FAILURES(CreateDisk(32 * kGibibyte, &disk));
{
// Pause the block watcher while we write partitions to the disk.
// This is to avoid the block watcher seeing an intermediate state of the partition table
// and incorrectly treating it as an MBR.
// The watcher is automatically resumed when this goes out of scope.
auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
// Write initial partitions to disk.
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(disk.get(), &gpt));
// Write initial partitions to disk. (reflective of state resulting
// from CrOS recovery)
const std::array<PartitionDescription, 9> partitions_at_start{
PartitionDescription{"efi-system", kEfiType, 0x22, 0x1},
PartitionDescription{"KERN-A", kCrosKernelType, 0x23, 0x1},
PartitionDescription{"KERN_B", kCrosKernelType, 0x24, 0x1},
PartitionDescription{"KERN_C", kCrosKernelType, 0x25, 0x1},
PartitionDescription{"ROOT_A", kCrosRootfsType, 0x26, 0x1},
PartitionDescription{"ROOT_B", kCrosRootfsType, 0x27, 0x1},
PartitionDescription{"ROOT_C", kCrosRootfsType, 0x28, 0x1},
PartitionDescription{"STATE", kStateLinuxGuid, 0x29, 0x1},
PartitionDescription{"sys-config", kSysConfigType, 0x2A, 0x1},
};
for (auto& part : partitions_at_start) {
ASSERT_OK(
gpt->AddPartition(part.name, part.type, GetRandomGuid(), part.start, part.length, 0),
"%s", part.name);
}
ASSERT_OK(gpt->Sync());
}
// Create CrOS device partitioner and initialise partition tables.
std::unique_ptr<paver::DevicePartitioner> partitioner;
ASSERT_NO_FATAL_FAILURES(CreatePartitioner(disk.get(), &partitioner));
ASSERT_OK(partitioner->InitPartitionTables());
// Ensure the final partition layout looks like we expect it to.
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(disk.get(), &gpt));
const std::array<PartitionDescription, 4> partitions_at_end{
PartitionDescription{GUID_ZIRCON_A_NAME, kCrosKernelType, 0x82B, 0x20000},
PartitionDescription{GUID_ZIRCON_B_NAME, kCrosKernelType, 0x2082B, 0x20000},
PartitionDescription{GUID_ZIRCON_R_NAME, kCrosKernelType, 0x4082B, 0x20000},
PartitionDescription{GUID_FVM_NAME, kFvmType, 0x6082B, 0x2000000},
};
ASSERT_NO_FATAL_FAILURES(EnsurePartitionsMatch(gpt.get(), 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::kFuchsiaVolumeManager)));
}
// Get Cros GPT flags for a kernel with the given priority.
uint64_t CrosGptPriorityFlags(uint8_t priority) {
uint64_t flags = 0;
ZX_ASSERT(gpt_cros_attr_set_priority(&flags, priority) >= 0);
return flags;
}
TEST_F(CrosDevicePartitionerTests, KernelPriority) {
// Create a 32 GiB disk.
std::unique_ptr<BlockDevice> disk;
ASSERT_NO_FATAL_FAILURES(CreateDisk(32 * kGibibyte, &disk));
{
// Pause the block watcher while we write partitions to the disk.
// This is to avoid the block watcher seeing an intermediate state of the partition table
// and incorrectly treating it as an MBR.
// The watcher is automatically resumed when this goes out of scope.
auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
// Set up partition table for test.
// Add non-ChromeOS partitions.
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(disk.get(), &gpt));
ASSERT_OK(gpt->AddPartition("CROS_KERNEL", kCrosKernelType, GetRandomGuid(), 0x1000, 0x1000,
CrosGptPriorityFlags(3)));
ASSERT_OK(gpt->AddPartition("NOT_KERNEL", GetRandomGuid(), GetRandomGuid(), 0x2000, 0x10,
CrosGptPriorityFlags(7)));
ASSERT_OK(gpt->Sync());
}
// Partition the disk, which will add ChromeOS partitions and adjust priorities.
{
std::unique_ptr<paver::DevicePartitioner> partitioner;
ASSERT_NO_FATAL_FAILURES(CreatePartitioner(disk.get(), &partitioner));
ASSERT_OK(partitioner->InitPartitionTables());
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kZirconA)));
}
// Ensure that the "zircon-a" kernel was created with priority 4 (priority of CROS_KERNEL + 1).
{
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(disk.get(), &gpt));
gpt_partition_t* partition = FindPartitionWithLabel(gpt.get(), GUID_ZIRCON_A_NAME);
ASSERT_TRUE(partition != nullptr);
EXPECT_EQ(gpt_cros_attr_get_priority(partition->flags), 4);
}
// Partition the disk again.
{
std::unique_ptr<paver::DevicePartitioner> partitioner;
ASSERT_NO_FATAL_FAILURES(CreatePartitioner(disk.get(), &partitioner));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kZirconA)));
}
// Ensure that the "zircon-a" kernel still has priority 4.
{
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(disk.get(), &gpt));
gpt_partition_t* partition = FindPartitionWithLabel(gpt.get(), GUID_ZIRCON_A_NAME);
ASSERT_TRUE(partition != nullptr);
EXPECT_EQ(gpt_cros_attr_get_priority(partition->flags), 4);
}
}
class FixedDevicePartitionerTests : public zxtest::Test {
protected:
FixedDevicePartitionerTests() {
IsolatedDevmgr::Args args;
args.driver_search_paths.push_back("/boot/driver");
args.disable_block_watcher = false;
ASSERT_OK(IsolatedDevmgr::Create(&args, &devmgr_));
fbl::unique_fd fd;
ASSERT_OK(RecursiveWaitForFile(devmgr_.devfs_root(), "misc/ramctl", &fd));
}
IsolatedDevmgr devmgr_;
};
TEST_F(FixedDevicePartitionerTests, UseBlockInterfaceTest) {
auto status = paver::FixedDevicePartitioner::Initialize(devmgr_.devfs_root().duplicate());
ASSERT_OK(status);
ASSERT_FALSE(status->IsFvmWithinFtl());
}
TEST_F(FixedDevicePartitionerTests, AddPartitionTest) {
auto status = paver::FixedDevicePartitioner::Initialize(devmgr_.devfs_root().duplicate());
ASSERT_OK(status);
ASSERT_STATUS(status->AddPartition(PartitionSpec(paver::Partition::kZirconB)),
ZX_ERR_NOT_SUPPORTED);
}
TEST_F(FixedDevicePartitionerTests, WipeFvmTest) {
auto status = paver::FixedDevicePartitioner::Initialize(devmgr_.devfs_root().duplicate());
ASSERT_OK(status);
ASSERT_OK(status->WipeFvm());
}
TEST_F(FixedDevicePartitionerTests, FinalizePartitionTest) {
auto status = paver::FixedDevicePartitioner::Initialize(devmgr_.devfs_root().duplicate());
ASSERT_OK(status);
auto& partitioner = status.value();
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kBootloaderA)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kZirconA)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kZirconB)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kZirconR)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kVbMetaA)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kVbMetaB)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kVbMetaR)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
}
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_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kBootloaderType, &bootloader));
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kZirconAType, &zircon_a));
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kZirconBType, &zircon_b));
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kZirconRType, &zircon_r));
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kVbMetaAType, &vbmeta_a));
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kVbMetaBType, &vbmeta_b));
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kVbMetaRType, &vbmeta_r));
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kFvmType, &fvm));
std::shared_ptr<paver::Context> context = std::make_shared<paver::Context>();
auto partitioner = paver::DevicePartitionerFactory::Create(
devmgr_.devfs_root().duplicate(), zx::channel(), paver::Arch::kArm64, context);
ASSERT_NE(partitioner.get(), nullptr);
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) {
auto status = paver::FixedDevicePartitioner::Initialize(devmgr_.devfs_root().duplicate());
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", 512) {}
// Create a DevicePartition for a device.
zx::status<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
const fbl::unique_fd& device) {
zx::channel svc_root = GetSvcRoot();
return paver::SherlockPartitioner::Initialize(devmgr_.devfs_root().duplicate(),
std::move(svc_root), device);
}
};
TEST_F(SherlockPartitionerTests, InitializeWithoutGptFails) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(&gpt_dev));
ASSERT_NOT_OK(CreatePartitioner(kDummyDevice));
}
TEST_F(SherlockPartitionerTests, InitializeWithoutFvmSucceeds) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(32 * kGibibyte, &gpt_dev));
{
// Pause the block watcher while we write partitions to the disk.
// This is to avoid the block watcher seeing an intermediate state of the partition table
// and incorrectly treating it as an MBR.
// The watcher is automatically resumed when this goes out of scope.
auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
// Set up a valid GPT.
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
ASSERT_OK(CreatePartitioner(kDummyDevice));
}
}
TEST_F(SherlockPartitionerTests, AddPartitionNotSupported) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(64 * kMebibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
ASSERT_STATUS(status->AddPartition(PartitionSpec(paver::Partition::kZirconB)),
ZX_ERR_NOT_SUPPORTED);
}
TEST_F(SherlockPartitionerTests, InitializePartitionTable) {
std::unique_ptr<BlockDevice> gpt_dev;
constexpr uint64_t kBlockCount = 0x748034;
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * block_size_, &gpt_dev));
{
// Pause the block watcher while we write partitions to the disk.
// This is to avoid the block watcher seeing an intermediate state of the partition table
// and incorrectly treating it as an MBR.
// The watcher is automatically resumed when this goes out of scope.
auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
const PartitionDescription kStartingPartitions[] = {
{"bootloader", kDummyType, 0x22, 0x2000}, {"reserved", kDummyType, 0x12000, 0x20000},
{"env", kDummyType, 0x36000, 0x4000}, {"fts", kDummyType, 0x3E000, 0x2000},
{"factory", kDummyType, 0x44000, 0x10000}, {"recovery", kDummyType, 0x58000, 0x10000},
{"boot", kDummyType, 0x6C000, 0x10000}, {"system", kDummyType, 0x80000, 0x278000},
{"cache", kDummyType, 0x2FC000, 0x400000}, {"fct", kDummyType, 0x700000, 0x20000},
{"sysconfig", kDummyType, 0x724000, 0x800}, {"migration", kDummyType, 0x728800, 0x3800},
{"buf", kDummyType, 0x730000, 0x18000},
};
for (const auto& part : fbl::Span(kStartingPartitions)) {
ASSERT_OK(
gpt->AddPartition(part.name, part.type, GetRandomGuid(), part.start, part.length, 0),
"%s", part.name);
}
ASSERT_OK(gpt->Sync());
}
fdio_cpp::UnownedFdioCaller caller(gpt_dev->fd());
auto result = ::llcpp::fuchsia::device::Controller::Call::Rebind(
caller.channel(), fidl::StringView("/boot/driver/gpt.so"));
ASSERT_TRUE(result.ok());
ASSERT_FALSE(result->result.is_err());
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_OK(partitioner->InitPartitionTables());
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
// Ensure the final partition layout looks like we expect it to.
const PartitionDescription kFinalPartitions[] = {
{"bootloader", kDummyType, 0x22, 0x2000},
{GUID_SYS_CONFIG_NAME, kSysConfigType, 0x2022, 0x678},
{GUID_ABR_META_NAME, kAbrMetaType, 0x269A, 0x8},
{GUID_VBMETA_A_NAME, kVbMetaAType, 0x26A2, 0x80},
{GUID_VBMETA_B_NAME, kVbMetaBType, 0x2722, 0x80},
{GUID_VBMETA_R_NAME, kVbMetaRType, 0x27A2, 0x80},
{"migration", kDummyType, 0x2822, 0x3800},
{"reserved", kDummyType, 0x12000, 0x20000},
{"env", kDummyType, 0x36000, 0x4000},
{"fts", kDummyType, 0x3E000, 0x2000},
{"factory", kDummyType, 0x44000, 0x10000},
{"recovery", kZirconRType, 0x54000, 0x10000},
{"boot", kZirconAType, 0x64000, 0x10000},
{"system", kZirconBType, 0x74000, 0x10000},
{GUID_FVM_NAME, kFvmType, 0x84000, 0x668000},
{"fct", kDummyType, 0x6EC000, 0x20000},
{"buffer", kDummyType, 0x70C000, 0x18000},
};
ASSERT_NO_FATAL_FAILURES(EnsurePartitionsMatch(gpt.get(), kFinalPartitions));
// 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)));
}
TEST_F(SherlockPartitionerTests, FindPartitionNewGuids) {
std::unique_ptr<BlockDevice> gpt_dev;
constexpr uint64_t kBlockCount = 0x748034;
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * block_size_, &gpt_dev));
// partition size / location is arbitrary
const std::vector<PartitionDescription> kSherlockNewPartitions = {
{GPT_DURABLE_BOOT_NAME, kDurableBootType, 0x10400, 0x10000},
{GPT_VBMETA_A_NAME, kVbMetaType, 0x20400, 0x10000},
{GPT_VBMETA_B_NAME, kVbMetaType, 0x30400, 0x10000},
{GPT_VBMETA_R_NAME, kVbMetaType, 0x40400, 0x10000},
{GPT_ZIRCON_A_NAME, kZirconType, 0x50400, 0x10000},
{GPT_ZIRCON_B_NAME, kZirconType, 0x60400, 0x10000},
{GPT_ZIRCON_R_NAME, kZirconType, 0x70400, 0x10000},
{GPT_FVM_NAME, kNewFvmType, 0x80400, 0x10000},
};
ASSERT_NO_FATAL_FAILURES(InitializeStartingGPTPartitions(gpt_dev.get(), kSherlockNewPartitions));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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)));
}
TEST_F(SherlockPartitionerTests, FindPartitionNewGuidsWithWrongTypeGUIDS) {
// 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.
std::unique_ptr<BlockDevice> gpt_dev;
constexpr uint64_t kBlockCount = 0x748034;
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * block_size_, &gpt_dev));
const std::vector<PartitionDescription> kSherlockNewPartitions = {
{GPT_DURABLE_BOOT_NAME, kStateLinuxGuid, 0x10400, 0x10000},
{GPT_VBMETA_A_NAME, kStateLinuxGuid, 0x20400, 0x10000},
{GPT_VBMETA_B_NAME, kStateLinuxGuid, 0x30400, 0x10000},
{GPT_VBMETA_R_NAME, kStateLinuxGuid, 0x40400, 0x10000},
{GPT_ZIRCON_A_NAME, kStateLinuxGuid, 0x50400, 0x10000},
{GPT_ZIRCON_B_NAME, kStateLinuxGuid, 0x60400, 0x10000},
{GPT_ZIRCON_R_NAME, kStateLinuxGuid, 0x70400, 0x10000},
{GPT_FVM_NAME, kStateLinuxGuid, 0x80400, 0x10000},
};
ASSERT_NO_FATAL_FAILURES(InitializeStartingGPTPartitions(gpt_dev.get(), kSherlockNewPartitions));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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)));
}
TEST_F(SherlockPartitionerTests, FindBootloader) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(&gpt_dev));
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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_FAILURES(CreateDisk(kBlockCount * kBlockSize, kBoot0Type, &boot0_dev));
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * kBlockSize, kBoot1Type, &boot1_dev));
// Now it should succeed.
ASSERT_OK(
partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA, "skip_metadata")));
}
TEST_F(SherlockPartitionerTests, SupportsPartition) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(64 * kMebibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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")));
}
class LuisPartitionerTests : public GptDevicePartitionerTests {
protected:
LuisPartitionerTests() : GptDevicePartitionerTests("luis", 512) {}
// Create a DevicePartition for a device.
zx::status<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
const fbl::unique_fd& device) {
zx::channel svc_root = GetSvcRoot();
return paver::LuisPartitioner::Initialize(devmgr_.devfs_root().duplicate(), std::move(svc_root),
device);
}
};
TEST_F(LuisPartitionerTests, InitializeWithoutGptFails) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(&gpt_dev));
ASSERT_NOT_OK(CreatePartitioner(kDummyDevice));
}
TEST_F(LuisPartitionerTests, InitializeWithoutFvmSucceeds) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(32 * kGibibyte, &gpt_dev));
{
// Pause the block watcher while we write partitions to the disk.
// This is to avoid the block watcher seeing an intermediate state of the partition table
// and incorrectly treating it as an MBR.
// The watcher is automatically resumed when this goes out of scope.
auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
// Set up a valid GPT.
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
ASSERT_OK(CreatePartitioner(kDummyDevice));
}
}
TEST_F(LuisPartitionerTests, AddPartitionNotSupported) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(64 * kMebibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
ASSERT_STATUS(status->AddPartition(PartitionSpec(paver::Partition::kZirconB)),
ZX_ERR_NOT_SUPPORTED);
}
TEST_F(LuisPartitionerTests, FindPartition) {
std::unique_ptr<BlockDevice> gpt_dev;
// kBlockCount should be a value large enough to accomodate 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;
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * block_size_, &gpt_dev));
// The initial gpt partitions are randomly chosen and does not necessarily reflect the
// actual gpt partition layout in product.
const std::vector<PartitionDescription> kLuisStartingPartitions = {
{GPT_DURABLE_BOOT_NAME, kDummyType, 0x10400, 0x10000},
{GPT_BOOTLOADER_A_NAME, kDummyType, 0x30400, 0x10000},
{GPT_BOOTLOADER_B_NAME, kDummyType, 0x40400, 0x10000},
{GPT_BOOTLOADER_R_NAME, kDummyType, 0x50400, 0x10000},
{GPT_VBMETA_A_NAME, kDummyType, 0x60400, 0x10000},
{GPT_VBMETA_B_NAME, kDummyType, 0x70400, 0x10000},
{GPT_VBMETA_R_NAME, kDummyType, 0x80400, 0x10000},
{GPT_ZIRCON_A_NAME, kDummyType, 0x90400, 0x10000},
{GPT_ZIRCON_B_NAME, kDummyType, 0xa0400, 0x10000},
{GPT_ZIRCON_R_NAME, kDummyType, 0xb0400, 0x10000},
{GPT_FACTORY_NAME, kDummyType, 0xc0400, 0x10000},
{GPT_DURABLE_NAME, kDummyType, 0xd0400, 0x10000},
{GPT_FVM_NAME, kDummyType, 0xe0400, 0x10000},
};
ASSERT_NO_FATAL_FAILURES(InitializeStartingGPTPartitions(gpt_dev.get(), kLuisStartingPartitions));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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_FAILURES(CreateDisk(kBlockCount * kBlockSize, kBoot0Type, &boot0_dev));
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * kBlockSize, kBoot1Type, &boot1_dev));
// 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) {
std::unique_ptr<BlockDevice> gpt_dev;
constexpr uint64_t kBlockCount = 0x748034;
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * block_size_, &gpt_dev));
const std::vector<PartitionDescription> kStartingPartitions = {
{GPT_DURABLE_BOOT_NAME, kDummyType, 0x10400, 0x10000},
};
ASSERT_NO_FATAL_FAILURES(InitializeStartingGPTPartitions(gpt_dev.get(), kStartingPartitions));
zx::channel svc_root = GetSvcRoot();
std::shared_ptr<paver::Context> context;
EXPECT_OK(paver::LuisAbrClientFactory().New(devmgr_.devfs_root().duplicate(), std::move(svc_root),
context));
}
TEST_F(LuisPartitionerTests, SupportsPartition) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(64 * kMebibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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) {}
// Create a DevicePartition for a device.
zx::status<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
const fbl::unique_fd& device) {
zx::channel svc_root = GetSvcRoot();
return paver::NelsonPartitioner::Initialize(devmgr_.devfs_root().duplicate(),
std::move(svc_root), device);
}
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) {
auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
std::unique_ptr<BlockDevice> gpt_dev, boot0, boot1;
ASSERT_NO_FATAL_FAILURES(InitializeBlockDeviceForBootloaderTest(&gpt_dev, &boot0, &boot1));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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_FAILURES(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_FAILURES(ValidateBlockContent(boot0.get(), 0, 1, 0));
ASSERT_NO_FATAL_FAILURES(ValidateBlockContent(boot0.get(), 1, bl2_blocks, kBL2ImageValue));
ASSERT_NO_FATAL_FAILURES(
ValidateBlockContent(boot0.get(), 1 + bl2_blocks, tpl_blocks, kTplImageValue));
// info block stays unchanged
ASSERT_NO_FATAL_FAILURES(ValidateBlockContent(boot1.get(), 0, 1, 0));
ASSERT_NO_FATAL_FAILURES(ValidateBlockContent(boot1.get(), 1, bl2_blocks, kBL2ImageValue));
ASSERT_NO_FATAL_FAILURES(
ValidateBlockContent(boot1.get(), 1 + bl2_blocks, tpl_blocks, kTplImageValue));
ASSERT_NO_FATAL_FAILURES(
ValidateBlockContent(gpt_dev.get(), kTplSlotAOffset, tpl_blocks, tpl_a_expected));
ASSERT_NO_FATAL_FAILURES(
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::status<>* out_status, uint8_t* out) {
auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
std::unique_ptr<BlockDevice> gpt_dev, boot0, boot1;
ASSERT_NO_FATAL_FAILURES(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_FAILURES(
WriteBlocks(info.blk_dev, info.start_block, info.size_in_blocks, data.data()));
}
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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);
}
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) {
auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
ASSERT_NO_FATAL_FAILURES(CreateDisk(64 * kMebibyte, gpt_dev));
static const std::vector<PartitionDescription> kNelsonBootloaderTestPartitions = {
{"tpl_a", kDummyType, kTplSlotAOffset, kUserTplBlockCount},
{"tpl_b", kDummyType, kTplSlotBOffset, kUserTplBlockCount},
};
ASSERT_NO_FATAL_FAILURES(
InitializeStartingGPTPartitions(gpt_dev->get(), kNelsonBootloaderTestPartitions));
ASSERT_NO_FATAL_FAILURES(CreateDisk(kUserTplBlockCount * kNelsonBlockSize, kBoot0Type, boot0));
ASSERT_NO_FATAL_FAILURES(CreateDisk(kUserTplBlockCount * kNelsonBlockSize, kBoot1Type, boot1));
}
};
TEST_F(NelsonPartitionerTests, InitializeWithoutGptFails) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(&gpt_dev));
ASSERT_NOT_OK(CreatePartitioner(kDummyDevice));
}
TEST_F(NelsonPartitionerTests, InitializeWithoutFvmSucceeds) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(32 * kGibibyte, &gpt_dev));
{
// Pause the block watcher while we write partitions to the disk.
// This is to avoid the block watcher seeing an intermediate state of the partition table
// and incorrectly treating it as an MBR.
// The watcher is automatically resumed when this goes out of scope.
auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
// Set up a valid GPT.
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
ASSERT_OK(CreatePartitioner(kDummyDevice));
}
}
TEST_F(NelsonPartitionerTests, AddPartitionNotSupported) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(64 * kMebibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
ASSERT_STATUS(status->AddPartition(PartitionSpec(paver::Partition::kZirconB)),
ZX_ERR_NOT_SUPPORTED);
}
TEST_F(NelsonPartitionerTests, FindPartition) {
std::unique_ptr<BlockDevice> gpt_dev;
// kBlockCount should be a value large enough to accomodate 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;
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * block_size_, &gpt_dev));
// The initial gpt partitions are randomly chosen and does not necessarily reflect the
// actual gpt partition layout in product.
const std::vector<PartitionDescription> kNelsonStartingPartitions = {
{GUID_ABR_META_NAME, kAbrMetaType, 0x10400, 0x10000},
{"tpl_a", kDummyType, 0x30400, 0x10000},
{"tpl_b", kDummyType, 0x40400, 0x10000},
{"boot_a", kZirconAType, 0x50400, 0x10000},
{"boot_b", kZirconBType, 0x60400, 0x10000},
{"system_a", kDummyType, 0x70400, 0x10000},
{"system_b", kDummyType, 0x80400, 0x10000},
{GPT_VBMETA_A_NAME, kVbMetaAType, 0x90400, 0x10000},
{GPT_VBMETA_B_NAME, kVbMetaBType, 0xa0400, 0x10000},
{"reserved_a", kDummyType, 0xc0400, 0x10000},
{"reserved_b", kDummyType, 0xd0400, 0x10000},
{"reserved_c", kVbMetaRType, 0xe0400, 0x10000},
{"cache", kZirconRType, 0xf0400, 0x10000},
{"data", kFvmType, 0x100400, 0x10000},
};
ASSERT_NO_FATAL_FAILURES(
InitializeStartingGPTPartitions(gpt_dev.get(), kNelsonStartingPartitions));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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_FAILURES(CreateDisk(kBlockCount * kBlockSize, kBoot0Type, &boot0_dev));
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * kBlockSize, kBoot1Type, &boot1_dev));
// 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)));
}
TEST_F(NelsonPartitionerTests, CreateAbrClient) {
std::unique_ptr<BlockDevice> gpt_dev;
constexpr uint64_t kBlockCount = 0x748034;
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * block_size_, &gpt_dev));
const std::vector<PartitionDescription> kStartingPartitions = {
{GUID_ABR_META_NAME, kAbrMetaType, 0x10400, 0x10000},
};
ASSERT_NO_FATAL_FAILURES(InitializeStartingGPTPartitions(gpt_dev.get(), kStartingPartitions));
zx::channel svc_root = GetSvcRoot();
std::shared_ptr<paver::Context> context;
EXPECT_OK(paver::NelsonAbrClientFactory().New(devmgr_.devfs_root().duplicate(),
std::move(svc_root), context));
}
TEST_F(NelsonPartitionerTests, SupportsPartition) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(64 * kMebibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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")));
}
TEST_F(NelsonPartitionerTests, ValidatePayload) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(64 * kMebibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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"),
fbl::Span<uint8_t>(payload_bl2_size)));
ASSERT_NOT_OK(
partitioner->ValidatePayload(PartitionSpec(paver::Partition::kBootloaderB, "bootloader"),
fbl::Span<uint8_t>(payload_bl2_size)));
std::vector<uint8_t> payload_bl2_tpl_size(2 * 1024 * 1024);
ASSERT_OK(
partitioner->ValidatePayload(PartitionSpec(paver::Partition::kBootloaderA, "bootloader"),
fbl::Span<uint8_t>(payload_bl2_tpl_size)));
ASSERT_OK(
partitioner->ValidatePayload(PartitionSpec(paver::Partition::kBootloaderB, "bootloader"),
fbl::Span<uint8_t>(payload_bl2_tpl_size)));
}
TEST_F(NelsonPartitionerTests, WriteBootloaderA) {
TestBootloaderWrite(PartitionSpec(paver::Partition::kBootloaderA, "bootloader"), kTplImageValue,
0x00);
}
TEST_F(NelsonPartitionerTests, WriteBootloaderB) {
TestBootloaderWrite(PartitionSpec(paver::Partition::kBootloaderB, "bootloader"), 0x00,
kTplImageValue);
}
TEST_F(NelsonPartitionerTests, ReadBootloaderAFail) {
auto spec = PartitionSpec(paver::Partition::kBootloaderA, "bootloader");
std::vector<uint8_t> read_buf(kBootloaderSize);
zx::status<> status = zx::ok();
ASSERT_NO_FATAL_FAILURES(
TestBootloaderRead(spec, 0x03, kTplImageValue, &status, read_buf.data()));
ASSERT_NOT_OK(status);
}
TEST_F(NelsonPartitionerTests, ReadBootloaderBFail) {
auto spec = PartitionSpec(paver::Partition::kBootloaderB, "bootloader");
std::vector<uint8_t> read_buf(kBootloaderSize);
zx::status<> status = zx::ok();
ASSERT_NO_FATAL_FAILURES(
TestBootloaderRead(spec, kTplImageValue, 0x03, &status, read_buf.data()));
ASSERT_NOT_OK(status);
}
TEST_F(NelsonPartitionerTests, ReadBootloaderASucceed) {
auto spec = PartitionSpec(paver::Partition::kBootloaderA, "bootloader");
std::vector<uint8_t> read_buf(kBootloaderSize);
zx::status<> status = zx::ok();
ASSERT_NO_FATAL_FAILURES(
TestBootloaderRead(spec, kTplImageValue, 0x03, &status, read_buf.data()));
ASSERT_OK(status);
ASSERT_NO_FATAL_FAILURES(ValidateBootloaderRead(read_buf.data(), kBL2ImageValue, kTplImageValue));
}
TEST_F(NelsonPartitionerTests, ReadBootloaderBSucceed) {
std::vector<uint8_t> read_buf(kBootloaderSize);
auto spec = PartitionSpec(paver::Partition::kBootloaderB, "bootloader");
zx::status<> status = zx::ok();
ASSERT_NO_FATAL_FAILURES(
TestBootloaderRead(spec, 0x03, kTplImageValue, &status, read_buf.data()));
ASSERT_OK(status);
ASSERT_NO_FATAL_FAILURES(ValidateBootloaderRead(read_buf.data(), kBL2ImageValue, kTplImageValue));
}
class Vim3PartitionerTests : public GptDevicePartitionerTests {
protected:
static constexpr size_t kVim3BlockSize = 512;
Vim3PartitionerTests() : GptDevicePartitionerTests("vim3", kVim3BlockSize) {}
// Create a DevicePartition for a device.
zx::status<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
const fbl::unique_fd& device) {
zx::channel svc_root = GetSvcRoot();
return paver::Vim3Partitioner::Initialize(devmgr_.devfs_root().duplicate(), std::move(svc_root),
device);
}
};
TEST_F(Vim3PartitionerTests, InitializeWithoutGptFails) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(&gpt_dev));
ASSERT_NOT_OK(CreatePartitioner(kDummyDevice));
}
TEST_F(Vim3PartitionerTests, InitializeWithoutFvmSucceeds) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(32 * kGibibyte, &gpt_dev));
{
// Pause the block watcher while we write partitions to the disk.
// This is to avoid the block watcher seeing an intermediate state of the partition table
// and incorrectly treating it as an MBR.
// The watcher is automatically resumed when this goes out of scope.
auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
ASSERT_OK(pauser);
// Set up a valid GPT.
std::unique_ptr<gpt::GptDevice> gpt;
ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
ASSERT_OK(CreatePartitioner(kDummyDevice));
}
}
TEST_F(Vim3PartitionerTests, AddPartitionNotSupported) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(64 * kMebibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
ASSERT_OK(status);
ASSERT_STATUS(status->AddPartition(PartitionSpec(paver::Partition::kZirconB)),
ZX_ERR_NOT_SUPPORTED);
}
TEST_F(Vim3PartitionerTests, FindPartition) {
std::unique_ptr<BlockDevice> gpt_dev;
constexpr uint64_t kBlockCount = 0x800000;
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * block_size_, &gpt_dev));
// The initial gpt partitions are randomly chosen and does not necessarily reflect the
// actual gpt partition layout in product.
std::vector<PartitionDescription> vim3_partitions = {
{GPT_DURABLE_BOOT_NAME, kDummyType, 0, 0x10000}, {GPT_VBMETA_A_NAME, kDummyType, 0, 0x10000},
{GPT_VBMETA_B_NAME, kDummyType, 0, 0x10000}, {GPT_VBMETA_R_NAME, kDummyType, 0, 0x10000},
{GPT_ZIRCON_A_NAME, kDummyType, 0, 0x10000}, {GPT_ZIRCON_B_NAME, kDummyType, 0, 0x10000},
{GPT_ZIRCON_R_NAME, kDummyType, 0, 0x10000}, {GPT_DURABLE_NAME, kDummyType, 0, 0x10000},
{GPT_FVM_NAME, kDummyType, 0, 0x10000},
};
vim3_partitions[0].start = 0x10400;
for (size_t i = 1; i < vim3_partitions.size(); i++) {
vim3_partitions[i].start = vim3_partitions[i - 1].start + vim3_partitions[i - 1].length;
}
ASSERT_NO_FATAL_FAILURES(InitializeStartingGPTPartitions(gpt_dev.get(), vim3_partitions));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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_FAILURES(CreateDisk(kBlockCount * kBlockSize, kBoot0Type, &boot0_dev));
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * kBlockSize, kBoot1Type, &boot1_dev));
// 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::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)));
}
TEST_F(Vim3PartitionerTests, CreateAbrClient) {
std::unique_ptr<BlockDevice> gpt_dev;
constexpr uint64_t kBlockCount = 0x748034;
ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * block_size_, &gpt_dev));
const std::vector<PartitionDescription> kStartingPartitions = {
{GPT_DURABLE_BOOT_NAME, kDummyType, 0x10400, 0x10000},
};
ASSERT_NO_FATAL_FAILURES(InitializeStartingGPTPartitions(gpt_dev.get(), kStartingPartitions));
zx::channel svc_root = GetSvcRoot();
std::shared_ptr<paver::Context> context;
EXPECT_OK(paver::Vim3AbrClientFactory().New(devmgr_.devfs_root().duplicate(), std::move(svc_root),
context));
}
TEST_F(Vim3PartitionerTests, SupportsPartition) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(64 * kMebibyte, &gpt_dev));
fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
auto status = CreatePartitioner(std::move(gpt_fd));
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)));
// Unsupported partition type.
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kUnknown)));
// Unsupported content type.
EXPECT_FALSE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta, "foo_type")));
}
TEST(AstroPartitionerTests, IsFvmWithinFtl) {
std::unique_ptr<SkipBlockDevice> device;
ASSERT_NO_FATAL_FAILURES(SkipBlockDevice::Create(kNandInfo, &device));
zx::channel svc_root;
std::shared_ptr<paver::Context> context = std::make_shared<paver::Context>();
auto status = paver::AstroPartitioner::Initialize(device->devfs_root(), svc_root, context);
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_TRUE(partitioner->IsFvmWithinFtl());
}
TEST(AstroPartitionerTests, ChooseAstroPartitioner) {
std::unique_ptr<SkipBlockDevice> device;
ASSERT_NO_FATAL_FAILURES(SkipBlockDevice::Create(kNandInfo, &device));
auto devfs_root = device->devfs_root();
std::unique_ptr<BlockDevice> zircon_a;
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devfs_root, kZirconAType, &zircon_a));
std::shared_ptr<paver::Context> context = std::make_shared<paver::Context>();
auto partitioner = paver::DevicePartitionerFactory::Create(std::move(devfs_root), zx::channel(),
paver::Arch::kArm64, context);
ASSERT_NE(partitioner.get(), nullptr);
ASSERT_TRUE(partitioner->IsFvmWithinFtl());
}
TEST(AstroPartitionerTests, AddPartitionTest) {
std::unique_ptr<SkipBlockDevice> device;
ASSERT_NO_FATAL_FAILURES(SkipBlockDevice::Create(kNandInfo, &device));
zx::channel svc_root;
std::shared_ptr<paver::Context> context = std::make_shared<paver::Context>();
auto status = paver::AstroPartitioner::Initialize(device->devfs_root(), svc_root, context);
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_STATUS(partitioner->AddPartition(PartitionSpec(paver::Partition::kZirconB)),
ZX_ERR_NOT_SUPPORTED);
}
TEST(AstroPartitionerTests, WipeFvmTest) {
std::unique_ptr<SkipBlockDevice> device;
ASSERT_NO_FATAL_FAILURES(SkipBlockDevice::Create(kNandInfo, &device));
zx::channel svc_root;
std::shared_ptr<paver::Context> context = std::make_shared<paver::Context>();
auto status = paver::AstroPartitioner::Initialize(device->devfs_root(), svc_root, context);
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_OK(partitioner->WipeFvm());
}
TEST(AstroPartitionerTests, FinalizePartitionTest) {
std::unique_ptr<SkipBlockDevice> device;
ASSERT_NO_FATAL_FAILURES(SkipBlockDevice::Create(kNandInfo, &device));
zx::channel svc_root;
std::shared_ptr<paver::Context> context = std::make_shared<paver::Context>();
auto status = paver::AstroPartitioner::Initialize(device->devfs_root(), svc_root, context);
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kBootloaderA)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kZirconA)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kZirconB)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kZirconR)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kVbMetaA)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kVbMetaB)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kVbMetaR)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kSysconfig)));
}
TEST(AstroPartitionerTests, FindPartitionTest) {
std::unique_ptr<SkipBlockDevice> device;
ASSERT_NO_FATAL_FAILURES(SkipBlockDevice::Create(kNandInfo, &device));
auto devfs_root = device->devfs_root();
std::unique_ptr<BlockDevice> fvm;
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devfs_root, kFvmType, &fvm));
zx::channel svc_root;
std::shared_ptr<paver::Context> context = std::make_shared<paver::Context>();
auto status = paver::AstroPartitioner::Initialize(device->devfs_root(), svc_root, context);
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA, "bl2")));
ASSERT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
ASSERT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
ASSERT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconR)));
ASSERT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaA)));
ASSERT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaB)));
ASSERT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaR)));
ASSERT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kSysconfig)));
ASSERT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
}
TEST(AstroPartitionerTests, SupportsPartition) {
std::unique_ptr<SkipBlockDevice> device;
ASSERT_NO_FATAL_FAILURES(SkipBlockDevice::Create(kNandInfo, &device));
zx::channel svc_root;
std::shared_ptr<paver::Context> context = std::make_shared<paver::Context>();
auto status = paver::AstroPartitioner::Initialize(device->devfs_root(), svc_root, context);
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::kBootloaderA, "bl2")));
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::kSysconfig)));
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::kBootloaderA, "unknown")));
EXPECT_FALSE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA, "foo_type")));
}
// Gets a PartitionClient for the given |spec| and writes |contents| padded to
// the partition's block size.
//
// Call with ASSERT_NO_FATAL_FAILURES.
void WritePartition(const paver::DevicePartitioner* partitioner, const PartitionSpec& spec,
std::string_view contents) {
auto status = partitioner->FindPartition(spec);
ASSERT_OK(status);
std::unique_ptr<paver::PartitionClient>& partition = status.value();
auto status2 = partition->GetBlockSize();
ASSERT_OK(status2);
size_t& block_size = status2.value();
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(block_size, ZX_VMO_RESIZABLE, &vmo));
ASSERT_OK(vmo.write(contents.data(), 0, contents.size()));
ASSERT_OK(partition->Write(vmo, block_size));
}
TEST(AstroPartitionerTests, BootloaderTplTest) {
std::unique_ptr<SkipBlockDevice> device;
ASSERT_NO_FATAL_FAILURES(SkipBlockDevice::Create(kNandInfo, &device));
zx::channel svc_root;
std::shared_ptr<paver::Context> context = std::make_shared<paver::Context>();
auto status = paver::AstroPartitioner::Initialize(device->devfs_root(), svc_root, context);
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_NO_FATAL_FAILURES(
WritePartition(partitioner.get(), PartitionSpec(paver::Partition::kBootloaderA), "abcd1234"));
const uint8_t* tpl_partition = PartitionStart(device->mapper(), kNandInfo, GUID_BOOTLOADER_VALUE);
ASSERT_NOT_NULL(tpl_partition);
ASSERT_EQ(0, memcmp("abcd1234", tpl_partition, 8));
}
TEST(AstroPartitionerTests, BootloaderBl2Test) {
std::unique_ptr<SkipBlockDevice> device;
ASSERT_NO_FATAL_FAILURES(SkipBlockDevice::Create(kNandInfo, &device));
zx::channel svc_root;
std::shared_ptr<paver::Context> context = std::make_shared<paver::Context>();
auto status = paver::AstroPartitioner::Initialize(device->devfs_root(), svc_root, context);
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_NO_FATAL_FAILURES(WritePartition(
partitioner.get(), PartitionSpec(paver::Partition::kBootloaderA, "bl2"), "123xyz"));
const uint8_t* bl2_partition = PartitionStart(device->mapper(), kNandInfo, GUID_BL2_VALUE);
ASSERT_NOT_NULL(bl2_partition);
// Special BL2 handling - image contents start at offset 4096 (page 1 on Astro).
ASSERT_EQ(0, memcmp("123xyz", bl2_partition + 4096, 6));
}
class As370PartitionerTests : public zxtest::Test {
protected:
As370PartitionerTests() {
IsolatedDevmgr::Args args;
args.driver_search_paths.push_back("/boot/driver");
args.disable_block_watcher = false;
args.board_name = "visalia";
ASSERT_OK(IsolatedDevmgr::Create(&args, &devmgr_));
fbl::unique_fd fd;
ASSERT_OK(RecursiveWaitForFile(devmgr_.devfs_root(), "sys/platform", &fd));
ASSERT_OK(RecursiveWaitForFile(devmgr_.devfs_root(), "misc/ramctl", &fd));
}
IsolatedDevmgr devmgr_;
};
TEST_F(As370PartitionerTests, IsFvmWithinFtl) {
auto status = paver::As370Partitioner::Initialize(devmgr_.devfs_root().duplicate());
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_TRUE(partitioner->IsFvmWithinFtl());
}
TEST_F(As370PartitionerTests, ChooseAs370Partitioner) {
std::shared_ptr<paver::Context> context = std::make_shared<paver::Context>();
auto partitioner = paver::DevicePartitionerFactory::Create(
devmgr_.devfs_root().duplicate(), zx::channel(), paver::Arch::kArm64, context);
ASSERT_NE(partitioner.get(), nullptr);
ASSERT_TRUE(partitioner->IsFvmWithinFtl());
}
TEST_F(As370PartitionerTests, AddPartitionTest) {
auto status = paver::As370Partitioner::Initialize(devmgr_.devfs_root().duplicate());
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_STATUS(partitioner->AddPartition(PartitionSpec(paver::Partition::kZirconB)),
ZX_ERR_NOT_SUPPORTED);
}
TEST_F(As370PartitionerTests, WipeFvmTest) {
auto status = paver::As370Partitioner::Initialize(devmgr_.devfs_root().duplicate());
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_OK(partitioner->WipeFvm());
}
TEST_F(As370PartitionerTests, FinalizePartitionTest) {
auto status = paver::As370Partitioner::Initialize(devmgr_.devfs_root().duplicate());
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kBootloaderA)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kZirconA)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kZirconB)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kZirconR)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kVbMetaA)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kVbMetaB)));
ASSERT_OK(partitioner->FinalizePartition(PartitionSpec(paver::Partition::kVbMetaR)));
}
TEST_F(As370PartitionerTests, FindPartitionTest) {
std::unique_ptr<BlockDevice> fvm;
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kFvmType, &fvm));
auto status = paver::As370Partitioner::Initialize(devmgr_.devfs_root().duplicate());
ASSERT_OK(status);
std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
ASSERT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
}
TEST_F(As370PartitionerTests, SupportsPartition) {
auto status = paver::As370Partitioner::Initialize(devmgr_.devfs_root().duplicate());
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::kFuchsiaVolumeManager)));
// Unsupported partition type.
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kUnknown)));
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaA)));
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaB)));
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaR)));
EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta)));
// Unsupported content type.
EXPECT_FALSE(
partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA, "foo_type")));
}
} // namespace