blob: 8375cee38e1b7f2209986ad62f694b1d5c525399 [file] [log] [blame]
// Copyright 2019 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.
#ifndef SRC_STORAGE_LIB_PAVER_TEST_TEST_UTILS_H_
#define SRC_STORAGE_LIB_PAVER_TEST_TEST_UTILS_H_
#include <fidl/fuchsia.boot/cpp/wire.h>
#include <fidl/fuchsia.hardware.block.volume/cpp/wire.h>
#include <fidl/fuchsia.sysinfo/cpp/wire.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/fdio/directory.h>
#include <lib/fzl/vmo-mapper.h>
#include <lib/zbi-format/zbi.h>
#include <memory>
#include <fbl/array.h>
#include <phys/zbi.h>
#include <ramdevice-client-test/ramnandctl.h>
#include <ramdevice-client/ramdisk.h>
#include <ramdevice-client/ramnand.h>
#include <zxtest/zxtest.h>
#include "src/storage/lib/paver/abr-client.h"
#include "src/storage/lib/paver/astro.h"
#include "src/storage/lib/paver/device-partitioner.h"
#include "src/storage/lib/paver/luis.h"
#include "src/storage/lib/paver/moonflower.h"
#include "src/storage/lib/paver/nelson.h"
#include "src/storage/lib/paver/sherlock.h"
#include "src/storage/lib/paver/uefi.h"
#include "src/storage/lib/paver/vim3.h"
constexpr uint64_t kBlockSize = 0x1000;
constexpr uint32_t kBlockCount = 0x100;
constexpr uint64_t kGptBlockCount = 2048;
constexpr uint32_t kOobSize = 8;
constexpr uint32_t kPageSize = 2048;
constexpr uint32_t kPagesPerBlock = 128;
constexpr uint32_t kSkipBlockSize = kPageSize * kPagesPerBlock;
constexpr uint32_t kNumBlocks = 400;
class PaverTest : public zxtest::Test {
protected:
void SetUp() override {
paver::DevicePartitionerFactory::Register(std::make_unique<paver::AstroPartitionerFactory>());
paver::DevicePartitionerFactory::Register(std::make_unique<paver::NelsonPartitionerFactory>());
paver::DevicePartitionerFactory::Register(
std::make_unique<paver::SherlockPartitionerFactory>());
paver::DevicePartitionerFactory::Register(
std::make_unique<paver::MoonflowerPartitionerFactory>());
paver::DevicePartitionerFactory::Register(std::make_unique<paver::LuisPartitionerFactory>());
paver::DevicePartitionerFactory::Register(std::make_unique<paver::Vim3PartitionerFactory>());
paver::DevicePartitionerFactory::Register(std::make_unique<paver::UefiPartitionerFactory>());
paver::DevicePartitionerFactory::Register(std::make_unique<paver::DefaultPartitionerFactory>());
}
};
struct DeviceAndController {
zx::channel device;
fidl::ClientEnd<fuchsia_device::Controller> controller;
};
zx::result<DeviceAndController> GetNewConnections(
fidl::UnownedClientEnd<fuchsia_device::Controller> controller);
struct PartitionDescription {
std::string name;
uuid::Uuid type;
uint64_t start;
uint64_t length;
// Instance is last since it is often elided. If unset, a generated instance is used.
std::optional<uuid::Uuid> instance;
};
class BlockDevice {
public:
static void Create(std::unique_ptr<BlockDevice>* device, const fbl::unique_fd& svc_root,
const uint8_t* guid, uint64_t block_count = kBlockCount,
uint32_t block_size = kBlockSize);
static void CreateFromVmo(std::unique_ptr<BlockDevice>* device, const fbl::unique_fd& svc_root,
const uint8_t* guid, zx::vmo vmo, uint32_t block_size = kBlockSize);
static void CreateWithGpt(const fbl::unique_fd& svc_root, uint64_t block_count,
uint32_t block_size,
const std::vector<PartitionDescription>& init_partitions,
std::unique_ptr<BlockDevice>* device);
static void CreateLegacy(std::unique_ptr<BlockDevice>* device, const fbl::unique_fd& devfs_root,
const uint8_t* guid, uint64_t block_count = kBlockCount,
uint32_t block_size = kBlockSize);
static void CreateLegacyFromVmo(std::unique_ptr<BlockDevice>* device,
const fbl::unique_fd& devfs_root, const uint8_t* guid,
zx::vmo vmo, uint32_t block_size = kBlockSize);
static void CreateLegacyWithGpt(const fbl::unique_fd& devfs_root, uint64_t block_count,
uint32_t block_size,
const std::vector<PartitionDescription>& init_partitions,
std::unique_ptr<BlockDevice>* device);
fidl::UnownedClientEnd<fuchsia_hardware_block::Block> block_interface() const {
return fidl::UnownedClientEnd<fuchsia_hardware_block::Block>(volume_.channel().get());
}
fidl::UnownedClientEnd<fuchsia_hardware_block_volume::Volume> volume_interface() const {
return volume_.borrow();
}
fidl::ClientEnd<fuchsia_hardware_block::Block> Connect() const {
zx::result result = ramdisk_.ConnectBlock();
ZX_ASSERT(result.status_value() == ZX_OK);
return std::move(*result);
}
fidl::ClientEnd<fuchsia_device::Controller> ConnectToLegacyController() const {
fidl::ClientEnd<fuchsia_device::Controller> controller;
zx::result controller_server = fidl::CreateEndpoints(&controller);
ZX_ASSERT(controller_server.is_ok());
fidl::OneWayStatus status = fidl::WireCall(ramdisk_.LegacyController())
->ConnectToController(std::move(*controller_server));
ZX_ASSERT(status.status() == ZX_OK);
return controller;
}
// Block count and block size of this device.
uint64_t block_count() const { return block_count_; }
uint32_t block_size() const { return block_size_; }
// Read `size` bytes from block offset `dev_offset` to *byte* offset `vmo_offset`.
void Read(const zx::vmo& vmo, size_t size, size_t dev_offset, size_t vmo_offset) const;
// Read `size` bytes from block offset `dev_offset` to *byte* offset `vmo_offset`.
void Write(const zx::vmo& vmo, size_t size, size_t dev_offset, size_t vmo_offset) const;
private:
BlockDevice(ramdevice_client::Ramdisk ramdisk,
fidl::ClientEnd<fuchsia_hardware_block_volume::Volume> volume, uint64_t block_count,
uint32_t block_size)
: ramdisk_(std::move(ramdisk)),
volume_(std::move(volume)),
block_count_(block_count),
block_size_(block_size) {}
ramdevice_client::Ramdisk ramdisk_;
fidl::ClientEnd<fuchsia_hardware_block_volume::Volume> volume_;
const uint64_t block_count_;
const uint32_t block_size_;
};
class SkipBlockDevice {
public:
static void Create(fbl::unique_fd devfs_root, fuchsia_hardware_nand::wire::RamNandInfo nand_info,
std::unique_ptr<SkipBlockDevice>* device);
fbl::unique_fd devfs_root() { return ctl_->devfs_root().duplicate(); }
fzl::VmoMapper& mapper() { return mapper_; }
~SkipBlockDevice() = default;
private:
SkipBlockDevice(std::unique_ptr<ramdevice_client_test::RamNandCtl> ctl,
ramdevice_client::RamNand ram_nand, fzl::VmoMapper mapper)
: ctl_(std::move(ctl)), ram_nand_(std::move(ram_nand)), mapper_(std::move(mapper)) {}
std::unique_ptr<ramdevice_client_test::RamNandCtl> ctl_;
ramdevice_client::RamNand ram_nand_;
fzl::VmoMapper mapper_;
};
// Dummy DevicePartition implementation meant to be used for testing. All functions are no-ops, i.e.
// they silently pass without doing anything. Tests can inherit from this class and override
// functions that are relevant for their test cases; this class provides an easy way to inherit from
// DevicePartitioner which is an abstract class.
class FakeDevicePartitioner : public paver::DevicePartitioner {
public:
zx::result<std::unique_ptr<abr::Client>> CreateAbrClient() const override { ZX_ASSERT(false); }
const paver::BlockDevices& Devices() const override { ZX_ASSERT(false); }
fidl::UnownedClientEnd<fuchsia_io::Directory> SvcRoot() const override { ZX_ASSERT(false); }
bool IsFvmWithinFtl() const override { return false; }
bool SupportsPartition(const paver::PartitionSpec& spec) const override { return true; }
zx::result<std::unique_ptr<paver::PartitionClient>> FindPartition(
const paver::PartitionSpec& spec) const override {
return zx::ok(nullptr);
}
zx::result<> WipeFvm() const override { return zx::ok(); }
zx::result<> ResetPartitionTables() const override { return zx::ok(); }
zx::result<> ValidatePayload(const paver::PartitionSpec& spec,
std::span<const uint8_t> data) const override {
return zx::ok();
}
};
// Defines a PartitionClient that reads and writes to a partition backed by a VMO in memory.
// Used for testing.
class FakePartitionClient : public paver::PartitionClient {
public:
FakePartitionClient(size_t block_count, size_t block_size);
explicit FakePartitionClient(size_t block_count);
zx::result<size_t> GetBlockSize() override;
zx::result<size_t> GetPartitionSize() override;
zx::result<> Read(const zx::vmo& vmo, size_t size) override;
zx::result<> Write(const zx::vmo& vmo, size_t vmo_size) override;
zx::result<> Trim() override;
zx::result<> Flush() override;
protected:
zx::vmo partition_;
size_t block_size_;
size_t partition_size_;
};
class FakeBootArgs : public fidl::WireServer<fuchsia_boot::Arguments> {
public:
explicit FakeBootArgs(std::string slot_suffix = "-a");
void GetString(GetStringRequestView request, GetStringCompleter::Sync& completer) override;
void GetStrings(GetStringsRequestView request, GetStringsCompleter::Sync& completer) override;
void GetBool(GetBoolRequestView request, GetBoolCompleter::Sync& completer) override;
void GetBools(GetBoolsRequestView request, GetBoolsCompleter::Sync& completer) override;
void Collect(CollectRequestView request, CollectCompleter::Sync& completer) override;
void SetAstroSysConfigAbrWearLeveling(bool value) { astro_sysconfig_abr_wear_leveling_ = value; }
void AddStringArgs(std::string key, std::string value) {
string_args_[std::move(key)] = std::move(value);
}
private:
fidl::ServerBindingGroup<fuchsia_boot::Arguments> bindings_;
bool astro_sysconfig_abr_wear_leveling_ = false;
std::unordered_map<std::string, std::string> string_args_;
};
// Allocates a valid-looking ZBI header, and give it some basic defaults.
//
// This is useful when attempting to pave a Kernel asset, in order to pass the
// verification checks.
//
// If "span" is non-null, it will be initialized with a span covering
// the allocated data.
//
// If "result_header" is non-null, it will point to the beginning of the
// uint8_t. It must not outlive the returned fbl::Array object.
fbl::Array<uint8_t> CreateZbiHeader(paver::Arch arch, size_t payload_size,
ZbiKernelImage** result_header,
std::span<uint8_t>* span = nullptr);
#endif // SRC_STORAGE_LIB_PAVER_TEST_TEST_UTILS_H_