blob: dc2fb41867e15adcbe7fcc070366f5548f9ca71c [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.
// clang-format off
#include <lib/sysconfig/sync-client.h>
// clang-format on
#include <lib/zx/channel.h>
#include <ramdevice-client/ramnand.h>
#include <zircon/hw/gpt.h>
#include <zxtest/zxtest.h>
namespace {
constexpr uint32_t kOobSize = 8;
constexpr uint32_t kPageSize = 4096;
constexpr uint32_t kPagesPerBlock = 64;
constexpr uint32_t kBlockSize = kPageSize * kPagesPerBlock;
constexpr uint32_t kNumBlocks = 8;
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 = 2,
.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_SYS_CONFIG_VALUE,
.unique_guid = {},
.first_block = 4,
.last_block = 7,
.copy_count = 4,
.copy_byte_offset = 0,
.name = {'s', 'y', 's', 'c', 'o', 'n', 'f', 'i', 'g'},
.hidden = false,
.bbt = false,
},
},
},
.export_nand_config = true,
.export_partition_map = true,
};
class SkipBlockDevice {
public:
static void Create(const fuchsia_hardware_nand_RamNandInfo& nand_info,
std::optional<SkipBlockDevice>* device);
fbl::unique_fd devfs_root() { return ctl_->devfs_root().duplicate(); }
fzl::VmoMapper& mapper() { return mapper_; }
~SkipBlockDevice() = default;
SkipBlockDevice(fbl::RefPtr<ramdevice_client::RamNandCtl> ctl, ramdevice_client::RamNand ram_nand,
fzl::VmoMapper mapper)
: ctl_(std::move(ctl)), ram_nand_(std::move(ram_nand)), mapper_(std::move(mapper)) {}
private:
fbl::RefPtr<ramdevice_client::RamNandCtl> ctl_;
ramdevice_client::RamNand ram_nand_;
fzl::VmoMapper mapper_;
};
void CreateBadBlockMap(void* buffer) {
// Set all entries in first BBT to be good blocks.
constexpr uint8_t kBlockGood = 0;
memset(buffer, kBlockGood, kPageSize);
struct OobMetadata {
uint32_t magic;
int16_t program_erase_cycles;
uint16_t generation;
};
const size_t oob_offset = kPageSize * kPagesPerBlock * kNumBlocks;
auto* oob = reinterpret_cast<OobMetadata*>(reinterpret_cast<uintptr_t>(buffer) + oob_offset);
oob->magic = 0x7462626E; // "nbbt"
oob->program_erase_cycles = 0;
oob->generation = 1;
}
void SkipBlockDevice::Create(const fuchsia_hardware_nand_RamNandInfo& nand_info,
std::optional<SkipBlockDevice>* device) {
fzl::VmoMapper mapper;
zx::vmo vmo;
ASSERT_OK(mapper.CreateAndMap((kPageSize + kOobSize) * kPagesPerBlock * kNumBlocks,
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, nullptr, &vmo));
memset(mapper.start(), 0xff, mapper.size());
CreateBadBlockMap(mapper.start());
zx::vmo dup;
ASSERT_OK(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup));
fuchsia_hardware_nand_RamNandInfo info = nand_info;
info.vmo = dup.release();
fbl::RefPtr<ramdevice_client::RamNandCtl> ctl;
ASSERT_OK(ramdevice_client::RamNandCtl::Create(&ctl));
std::optional<ramdevice_client::RamNand> ram_nand;
ASSERT_OK(ramdevice_client::RamNand::Create(ctl, &info, &ram_nand));
fbl::unique_fd fd;
ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(ctl->devfs_root(), "misc/sysinfo", &fd));
ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(ctl->devfs_root(), "sys/platform", &fd));
device->emplace(std::move(ctl), *std::move(ram_nand), std::move(mapper));
}
void CreatePayload(size_t size, zx::vmo* out) {
zx::vmo vmo;
fzl::VmoMapper mapper;
ASSERT_OK(mapper.CreateAndMap(fbl::round_up(size, ZX_PAGE_SIZE),
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, nullptr, &vmo));
memset(mapper.start(), 0x4a, mapper.size());
*out = std::move(vmo);
}
void ValidateBuffer(void* buffer, size_t size) {
const auto* start = static_cast<uint8_t*>(buffer);
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(start[i], 0x5c, "i = %zu", i);
}
}
class SyncClientTest : public zxtest::Test {
protected:
SyncClientTest() {
ASSERT_NO_FATAL_FAILURES(SkipBlockDevice::Create(kNandInfo, &device_));
}
void ValidateWritten(size_t offset, size_t size) {
for (size_t block = 4; block < 5; block++) {
const uint8_t* start =
static_cast<uint8_t*>(device_->mapper().start()) + (block * kBlockSize) + offset;
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(start[i], 0x4a, "block = %zu, i = %zu", block, i);
}
}
}
void ValidateUnwritten(size_t offset, size_t size) {
for (size_t block = 4; block < 5; block++) {
const uint8_t* start =
static_cast<uint8_t*>(device_->mapper().start()) + (block * kBlockSize) + offset;
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(start[i], 0xff, "block = %zu, offset: %zu i = %zu", block, offset, i);
}
}
}
void WriteData(size_t offset, size_t size) {
for (size_t block = 4; block < 7; block++) {
uint8_t* start =
static_cast<uint8_t*>(device_->mapper().start()) + (block * kBlockSize) + offset;
memset(start, 0x5c, size);
}
}
std::optional<SkipBlockDevice> device_;
};
TEST_F(SyncClientTest, CreateAstro) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
}
using PartitionType = sysconfig::SyncClient::PartitionType;
constexpr size_t kKilobyte = 1 << 10;
TEST_F(SyncClientTest, WritePartitionSysconfig) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
zx::vmo vmo;
ASSERT_NO_FATAL_FAILURES(CreatePayload(60 * kKilobyte, &vmo));
ASSERT_OK(client->WritePartition(PartitionType::kSysconfig, vmo, 0));
ASSERT_NO_FATAL_FAILURES(ValidateWritten(0, 60 * kKilobyte));
ASSERT_NO_FATAL_FAILURES(ValidateUnwritten(60 * kKilobyte, 196 * kKilobyte));
}
TEST_F(SyncClientTest, WritePartitionAbrMetadata) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
zx::vmo vmo;
ASSERT_NO_FATAL_FAILURES(CreatePayload(4 * kKilobyte, &vmo));
ASSERT_OK(client->WritePartition(PartitionType::kABRMetadata, vmo, 0));
ASSERT_NO_FATAL_FAILURES(ValidateUnwritten(0, 60 * kKilobyte));
ASSERT_NO_FATAL_FAILURES(ValidateWritten(60 * kKilobyte, 4 * kKilobyte));
ASSERT_NO_FATAL_FAILURES(ValidateUnwritten(64 * kKilobyte, 192 * kKilobyte));
}
TEST_F(SyncClientTest, WritePartitionVbMetaA) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
zx::vmo vmo;
ASSERT_NO_FATAL_FAILURES(CreatePayload(64 * kKilobyte, &vmo));
ASSERT_OK(client->WritePartition(PartitionType::kVerifiedBootMetadataA, vmo, 0));
ASSERT_NO_FATAL_FAILURES(ValidateUnwritten(0, 64 * kKilobyte));
ASSERT_NO_FATAL_FAILURES(ValidateWritten(64 * kKilobyte, 64 * kKilobyte));
ASSERT_NO_FATAL_FAILURES(ValidateUnwritten(128 * kKilobyte, 128 * kKilobyte));
}
TEST_F(SyncClientTest, WritePartitionVbMetaB) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
zx::vmo vmo;
ASSERT_NO_FATAL_FAILURES(CreatePayload(64 * kKilobyte, &vmo));
ASSERT_OK(client->WritePartition(PartitionType::kVerifiedBootMetadataB, vmo, 0));
ASSERT_NO_FATAL_FAILURES(ValidateUnwritten(0, 128 * kKilobyte));
ASSERT_NO_FATAL_FAILURES(ValidateWritten(128 * kKilobyte, 64 * kKilobyte));
ASSERT_NO_FATAL_FAILURES(ValidateUnwritten(192 * kKilobyte, 64 * kKilobyte));
}
TEST_F(SyncClientTest, WritePartitionVbMetaR) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
zx::vmo vmo;
ASSERT_NO_FATAL_FAILURES(CreatePayload(64 * kKilobyte, &vmo));
ASSERT_OK(client->WritePartition(PartitionType::kVerifiedBootMetadataR, vmo, 0));
ASSERT_NO_FATAL_FAILURES(ValidateUnwritten(0, 192 * kKilobyte));
ASSERT_NO_FATAL_FAILURES(ValidateWritten(192 * kKilobyte, 64 * kKilobyte));
}
TEST_F(SyncClientTest, ReadPartitionSysconfig) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
fzl::OwnedVmoMapper mapper;
ASSERT_OK(mapper.CreateAndMap(fbl::round_up(60 * kKilobyte, ZX_PAGE_SIZE), "test"));
ASSERT_NO_FATAL_FAILURES(WriteData(0, 60 * kKilobyte));
ASSERT_OK(client->ReadPartition(PartitionType::kSysconfig, mapper.vmo(), 0));
ASSERT_NO_FATAL_FAILURES(ValidateBuffer(mapper.start(), 60 * kKilobyte));
}
TEST_F(SyncClientTest, ReadPartitionAbrMetadata) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
fzl::OwnedVmoMapper mapper;
ASSERT_OK(mapper.CreateAndMap(fbl::round_up(4 * kKilobyte, ZX_PAGE_SIZE), "test"));
ASSERT_NO_FATAL_FAILURES(WriteData(60 * kKilobyte, 4 * kKilobyte));
ASSERT_OK(client->ReadPartition(PartitionType::kABRMetadata, mapper.vmo(), 0));
ASSERT_NO_FATAL_FAILURES(ValidateBuffer(mapper.start(), 4 * kKilobyte));
}
TEST_F(SyncClientTest, ReadPartitionVbMetaA) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
fzl::OwnedVmoMapper mapper;
ASSERT_OK(mapper.CreateAndMap(fbl::round_up(64 * kKilobyte, ZX_PAGE_SIZE), "test"));
ASSERT_NO_FATAL_FAILURES(WriteData(64 * kKilobyte, 64 * kKilobyte));
ASSERT_OK(client->ReadPartition(PartitionType::kVerifiedBootMetadataA, mapper.vmo(), 0));
ASSERT_NO_FATAL_FAILURES(ValidateBuffer(mapper.start(), 64 * kKilobyte));
}
TEST_F(SyncClientTest, ReadPartitionVbMetaB) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
fzl::OwnedVmoMapper mapper;
ASSERT_OK(mapper.CreateAndMap(fbl::round_up(64 * kKilobyte, ZX_PAGE_SIZE), "test"));
ASSERT_NO_FATAL_FAILURES(WriteData(128 * kKilobyte, 64 * kKilobyte));
ASSERT_OK(client->ReadPartition(PartitionType::kVerifiedBootMetadataB, mapper.vmo(), 0));
ASSERT_NO_FATAL_FAILURES(ValidateBuffer(mapper.start(), 64 * kKilobyte));
}
TEST_F(SyncClientTest, ReadPartitionVbMetaR) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
fzl::OwnedVmoMapper mapper;
ASSERT_OK(mapper.CreateAndMap(fbl::round_up(64 * kKilobyte, ZX_PAGE_SIZE), "test"));
ASSERT_NO_FATAL_FAILURES(WriteData(192 * kKilobyte, 64 * kKilobyte));
ASSERT_OK(client->ReadPartition(PartitionType::kVerifiedBootMetadataR, mapper.vmo(), 0));
ASSERT_NO_FATAL_FAILURES(ValidateBuffer(mapper.start(), 64 * kKilobyte));
}
TEST_F(SyncClientTest, GetPartitionSizeSysconfig) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
ASSERT_EQ(client->GetPartitionSize(PartitionType::kSysconfig), 60 * kKilobyte);
}
TEST_F(SyncClientTest, GetPartitionSizeAbrMetadata) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
ASSERT_EQ(client->GetPartitionSize(PartitionType::kABRMetadata), 4 * kKilobyte);
}
TEST_F(SyncClientTest, GetPartitionSizeVbMetaA) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
ASSERT_EQ(client->GetPartitionSize(PartitionType::kVerifiedBootMetadataA), 64 * kKilobyte);
}
TEST_F(SyncClientTest, GetPartitionSizeVbMetaB) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
ASSERT_EQ(client->GetPartitionSize(PartitionType::kVerifiedBootMetadataB), 64 * kKilobyte);
}
TEST_F(SyncClientTest, GetPartitionSizeVbMetaR) {
std::optional<sysconfig::SyncClient> client;
ASSERT_OK(sysconfig::SyncClient::Create(device_->devfs_root(), &client));
ASSERT_EQ(client->GetPartitionSize(PartitionType::kVerifiedBootMetadataR), 64 * kKilobyte);
}
} // namespace