blob: 864ef687c34da0a6faf5bd13accb94ef0e0ce86a [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.
#include <endian.h>
#include <fcntl.h>
#include <fuchsia/boot/llcpp/fidl.h>
#include <fuchsia/hardware/nand/c/fidl.h>
#include <fuchsia/paver/llcpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/cksum.h>
#include <lib/fdio/directory.h>
#include <lib/fidl-async/cpp/bind.h>
#include <lib/fzl/fdio.h>
#include <lib/fzl/vmo-mapper.h>
#include <lib/paver/provider.h>
#include <lib/zx/vmo.h>
#include <zircon/hw/gpt.h>
#include <optional>
#include <fbl/algorithm.h>
#include <fbl/unique_fd.h>
#include <fbl/unique_ptr.h>
#include <fs/pseudo_dir.h>
#include <fs/service.h>
#include <fs/synchronous_vfs.h>
#include <zxtest/zxtest.h>
#include "device-partitioner.h"
#include "paver.h"
#include "test/test-utils.h"
namespace {
using devmgr_integration_test::IsolatedDevmgr;
using devmgr_integration_test::RecursiveWaitForFile;
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 = 6,
.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,
},
},
},
.export_nand_config = true,
.export_partition_map = true,
};
class FakeBootArgs : public ::llcpp::fuchsia::boot::Arguments::Interface {
public:
zx_status_t Connect(async_dispatcher_t* dispatcher, zx::channel request) {
return fidl::Bind(dispatcher, std::move(request), this);
}
void Get(GetCompleter::Sync completer) {
zx::vmo vmo;
zx::vmo::create(fbl::round_up(sizeof(kArgs), ZX_PAGE_SIZE), 0, &vmo);
vmo.write(kArgs, 0, sizeof(kArgs));
completer.Reply(std::move(vmo), sizeof(kArgs));
}
private:
static constexpr char kArgs[] = "zvb.current_slot=-a";
};
class FakeSvc {
public:
explicit FakeSvc(async_dispatcher_t* dispatcher) : dispatcher_(dispatcher), vfs_(dispatcher) {
auto root_dir = fbl::MakeRefCounted<fs::PseudoDir>();
root_dir->AddEntry(::llcpp::fuchsia::boot::Arguments::Name,
fbl::MakeRefCounted<fs::Service>([this](zx::channel request) {
return fake_boot_args_.Connect(dispatcher_, std::move(request));
}));
zx::channel svc_remote;
ASSERT_OK(zx::channel::create(0, &svc_local_, &svc_remote));
vfs_.ServeDirectory(root_dir, std::move(svc_remote));
}
FakeBootArgs& fake_boot_args() { return fake_boot_args_; }
zx::channel& svc_chan() { return svc_local_; }
private:
async_dispatcher_t* dispatcher_;
fs::SynchronousVfs vfs_;
FakeBootArgs fake_boot_args_;
zx::channel svc_local_;
};
class PaverServiceTest : public zxtest::Test {
public:
PaverServiceTest();
~PaverServiceTest();
protected:
void CreatePayload(size_t num_pages, ::llcpp::fuchsia::mem::Buffer* out);
static constexpr size_t kKilobyte = 1 << 10;
void ValidateWritten(const ::llcpp::fuchsia::mem::Buffer& buf, size_t num_pages) {
ASSERT_GE(buf.size, num_pages * kPageSize);
fzl::VmoMapper mapper;
ASSERT_OK(mapper.Map(buf.vmo, 0, fbl::round_up(num_pages * kPageSize, ZX_PAGE_SIZE),
ZX_VM_PERM_READ));
const uint8_t* start = reinterpret_cast<uint8_t*>(mapper.start());
for (size_t i = 0; i < num_pages * kPageSize; i++) {
ASSERT_EQ(start[i], 0x4a, "i = %zu", i);
}
}
void* provider_ctx_ = nullptr;
std::optional<::llcpp::fuchsia::paver::Paver::SyncClient> client_;
async::Loop loop_;
// The paver makes synchronous calls into /svc, so it must run in a seperate loop to not
// deadlock.
async::Loop loop2_;
FakeSvc fake_svc_;
};
PaverServiceTest::PaverServiceTest()
: loop_(&kAsyncLoopConfigAttachToCurrentThread),
loop2_(&kAsyncLoopConfigNoAttachToCurrentThread),
fake_svc_(loop2_.dispatcher()) {
zx::channel client, server;
ASSERT_OK(zx::channel::create(0, &client, &server));
client_.emplace(std::move(client));
ASSERT_OK(paver_get_service_provider()->ops->init(&provider_ctx_));
ASSERT_OK(paver_get_service_provider()->ops->connect(
provider_ctx_, loop_.dispatcher(), ::llcpp::fuchsia::paver::Paver::Name, server.release()));
loop_.StartThread("paver-svc-test-loop");
loop2_.StartThread("paver-svc-test-loop-2");
}
PaverServiceTest::~PaverServiceTest() {
loop_.Shutdown();
loop2_.Shutdown();
paver_get_service_provider()->ops->release(provider_ctx_);
provider_ctx_ = nullptr;
}
void PaverServiceTest::CreatePayload(size_t num_pages, ::llcpp::fuchsia::mem::Buffer* out) {
zx::vmo vmo;
fzl::VmoMapper mapper;
const size_t size = kPageSize * num_pages;
ASSERT_OK(mapper.CreateAndMap(size, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, nullptr, &vmo));
memset(mapper.start(), 0x4a, mapper.size());
out->vmo = std::move(vmo);
out->size = size;
}
class PaverServiceSkipBlockTest : public PaverServiceTest {
public:
PaverServiceSkipBlockTest() {
ASSERT_NO_FATAL_FAILURES(SpawnIsolatedDevmgr());
ASSERT_NO_FATAL_FAILURES(WaitForSysconfig());
}
protected:
void SpawnIsolatedDevmgr() {
ASSERT_EQ(device_.get(), nullptr);
SkipBlockDevice::Create(kNandInfo, &device_);
static_cast<paver::Paver*>(provider_ctx_)->set_devfs_root(device_->devfs_root());
static_cast<paver::Paver*>(provider_ctx_)->set_svc_root(std::move(fake_svc_.svc_chan()));
}
void WaitForSysconfig() {
fbl::unique_fd fd;
ASSERT_OK(RecursiveWaitForFile(device_->devfs_root(),
"misc/nand-ctl/ram-nand-0/sysconfig/skip-block", &fd));
}
void SetAbr(const abr::Data& data) {
auto* buf = reinterpret_cast<uint8_t*>(device_->mapper().start()) + (14 * kSkipBlockSize) +
(60 * kKilobyte);
*reinterpret_cast<abr::Data*>(buf) = data;
}
abr::Data GetAbr() {
auto* buf = reinterpret_cast<uint8_t*>(device_->mapper().start()) + (14 * kSkipBlockSize) +
(60 * kKilobyte);
return *reinterpret_cast<abr::Data*>(buf);
}
using PaverServiceTest::ValidateWritten;
void ValidateWritten(uint32_t block, size_t num_blocks) {
const uint8_t* start =
static_cast<uint8_t*>(device_->mapper().start()) + (block * kSkipBlockSize);
for (size_t i = 0; i < kSkipBlockSize * num_blocks; i++) {
ASSERT_EQ(start[i], 0x4a, "i = %zu", i);
}
}
void ValidateUnwritten(uint32_t block, size_t num_blocks) {
const uint8_t* start =
static_cast<uint8_t*>(device_->mapper().start()) + (block * kSkipBlockSize);
for (size_t i = 0; i < kSkipBlockSize * num_blocks; i++) {
ASSERT_EQ(start[i], 0xff, "i = %zu", i);
}
}
void ValidateWrittenPages(uint32_t page, size_t num_pages) {
const uint8_t* start =
static_cast<uint8_t*>(device_->mapper().start()) + (page * kPageSize);
for (size_t i = 0; i < kPageSize * num_pages; i++) {
ASSERT_EQ(start[i], 0x4a, "i = %zu", i);
}
}
void ValidateUnwrittenPages(uint32_t page, size_t num_pages) {
const uint8_t* start =
static_cast<uint8_t*>(device_->mapper().start()) + (page * kPageSize);
for (size_t i = 0; i < kPageSize * num_pages; i++) {
ASSERT_EQ(start[i], 0xff, "i = %zu", i);
}
}
void WriteData(uint32_t page, size_t num_pages, uint8_t data) {
uint8_t* start =
static_cast<uint8_t*>(device_->mapper().start()) + (page * kPageSize);
memset(start, data, kPageSize * num_pages);
}
fbl::unique_ptr<SkipBlockDevice> device_;
};
constexpr abr::Data kAbrData = {
.magic = {'\0', 'A', 'B', '0'},
.version_major = abr::kMajorVersion,
.version_minor = abr::kMinorVersion,
.reserved1 = {},
.slots =
{
{
.priority = 0,
.tries_remaining = 0,
.successful_boot = 0,
.reserved = {},
},
{
.priority = 1,
.tries_remaining = 0,
.successful_boot = 1,
.reserved = {},
},
},
.oneshot_recovery_boot = 0,
.reserved2 = {},
.crc32 = {},
};
void ComputeCrc(abr::Data* data) {
data->crc32 = htobe32(
crc32(0, reinterpret_cast<const uint8_t*>(data), offsetof(abr::Data, crc32)));
}
TEST_F(PaverServiceSkipBlockTest, InitializeAbr) {
abr::Data abr_data = {};
memset(&abr_data, 0x3d, sizeof(abr_data));
SetAbr(abr_data);
auto result = client_->InitializeAbr();
ASSERT_OK(result.status());
ASSERT_OK(result->status);
}
TEST_F(PaverServiceSkipBlockTest, InitializeAbrAlreadyValid) {
abr::Data abr_data = kAbrData;
ComputeCrc(&abr_data);
SetAbr(abr_data);
auto result = client_->InitializeAbr();
ASSERT_OK(result.status());
ASSERT_OK(result->status);
}
TEST_F(PaverServiceSkipBlockTest, QueryActiveConfigurationInvalidAbr) {
abr::Data abr_data = {};
memset(&abr_data, 0x3d, sizeof(abr_data));
SetAbr(abr_data);
auto result = client_->QueryActiveConfiguration();
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_err());
ASSERT_STATUS(result->result.err(), ZX_ERR_NOT_SUPPORTED);
}
TEST_F(PaverServiceSkipBlockTest, QueryActiveConfigurationBothPriority0) {
abr::Data abr_data = kAbrData;
abr_data.slots[0].priority = 0;
abr_data.slots[1].priority = 0;
ComputeCrc(&abr_data);
SetAbr(abr_data);
auto result = client_->QueryActiveConfiguration();
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_err());
ASSERT_STATUS(result->result.err(), ZX_ERR_NOT_SUPPORTED);
}
TEST_F(PaverServiceSkipBlockTest, QueryActiveConfigurationSlotB) {
abr::Data abr_data = kAbrData;
ComputeCrc(&abr_data);
SetAbr(abr_data);
auto result = client_->QueryActiveConfiguration();
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_response());
ASSERT_EQ(result->result.response().configuration, ::llcpp::fuchsia::paver::Configuration::B);
}
TEST_F(PaverServiceSkipBlockTest, QueryActiveConfigurationSlotA) {
abr::Data abr_data = kAbrData;
abr_data.slots[0].priority = 2;
abr_data.slots[0].successful_boot = 1;
ComputeCrc(&abr_data);
SetAbr(abr_data);
auto result = client_->QueryActiveConfiguration();
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_response());
ASSERT_EQ(result->result.response().configuration, ::llcpp::fuchsia::paver::Configuration::A);
}
TEST_F(PaverServiceSkipBlockTest, QueryConfigurationStatusHealthy) {
abr::Data abr_data = kAbrData;
ComputeCrc(&abr_data);
SetAbr(abr_data);
auto result = client_->QueryConfigurationStatus(::llcpp::fuchsia::paver::Configuration::B);
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_response());
ASSERT_EQ(result->result.response().status,
::llcpp::fuchsia::paver::ConfigurationStatus::HEALTHY);
}
TEST_F(PaverServiceSkipBlockTest, QueryConfigurationStatusPending) {
abr::Data abr_data = kAbrData;
abr_data.slots[1].successful_boot = 0;
abr_data.slots[1].tries_remaining = 1;
ComputeCrc(&abr_data);
SetAbr(abr_data);
auto result = client_->QueryConfigurationStatus(::llcpp::fuchsia::paver::Configuration::B);
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_response());
ASSERT_EQ(result->result.response().status,
::llcpp::fuchsia::paver::ConfigurationStatus::PENDING);
}
TEST_F(PaverServiceSkipBlockTest, QueryConfigurationStatusUnbootable) {
abr::Data abr_data = kAbrData;
ComputeCrc(&abr_data);
SetAbr(abr_data);
auto result = client_->QueryConfigurationStatus(::llcpp::fuchsia::paver::Configuration::A);
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_response());
ASSERT_EQ(result->result.response().status,
::llcpp::fuchsia::paver::ConfigurationStatus::UNBOOTABLE);
}
TEST_F(PaverServiceSkipBlockTest, SetConfigurationActive) {
abr::Data abr_data = kAbrData;
ComputeCrc(&abr_data);
SetAbr(abr_data);
abr_data.slots[0].priority = 2;
abr_data.slots[0].tries_remaining = abr::kMaxTriesRemaining;
abr_data.slots[0].successful_boot = 0;
ComputeCrc(&abr_data);
auto result = client_->SetConfigurationActive(::llcpp::fuchsia::paver::Configuration::A);
ASSERT_OK(result.status());
ASSERT_OK(result->status);
auto actual = GetAbr();
ASSERT_BYTES_EQ(&abr_data, &actual, sizeof(abr_data));
}
TEST_F(PaverServiceSkipBlockTest, SetConfigurationActiveRollover) {
abr::Data abr_data = kAbrData;
abr_data.slots[1].priority = abr::kMaxPriority;
ComputeCrc(&abr_data);
SetAbr(abr_data);
abr_data.slots[1].priority = 1;
abr_data.slots[0].priority = 2;
abr_data.slots[0].tries_remaining = abr::kMaxTriesRemaining;
abr_data.slots[0].successful_boot = 0;
ComputeCrc(&abr_data);
auto result = client_->SetConfigurationActive(::llcpp::fuchsia::paver::Configuration::A);
ASSERT_OK(result.status());
ASSERT_OK(result->status);
auto actual = GetAbr();
ASSERT_BYTES_EQ(&abr_data, &actual, sizeof(abr_data));
}
TEST_F(PaverServiceSkipBlockTest, SetConfigurationUnbootableSlotA) {
abr::Data abr_data = kAbrData;
abr_data.slots[0].priority = 2;
abr_data.slots[0].tries_remaining = 3;
abr_data.slots[0].successful_boot = 0;
ComputeCrc(&abr_data);
SetAbr(abr_data);
abr_data.slots[0].priority = 0;
abr_data.slots[0].tries_remaining = 0;
abr_data.slots[0].successful_boot = 0;
ComputeCrc(&abr_data);
auto result = client_->SetConfigurationUnbootable(::llcpp::fuchsia::paver::Configuration::A);
ASSERT_OK(result.status());
ASSERT_OK(result->status);
auto actual = GetAbr();
ASSERT_BYTES_EQ(&abr_data, &actual, sizeof(abr_data));
}
TEST_F(PaverServiceSkipBlockTest, SetConfigurationUnbootableSlotB) {
abr::Data abr_data = kAbrData;
abr_data.slots[1].tries_remaining = 3;
abr_data.slots[1].successful_boot = 0;
ComputeCrc(&abr_data);
SetAbr(abr_data);
abr_data.slots[1].priority = 0;
abr_data.slots[1].tries_remaining = 0;
abr_data.slots[1].successful_boot = 0;
ComputeCrc(&abr_data);
auto result = client_->SetConfigurationUnbootable(::llcpp::fuchsia::paver::Configuration::B);
ASSERT_OK(result.status());
ASSERT_OK(result->status);
auto actual = GetAbr();
ASSERT_BYTES_EQ(&abr_data, &actual, sizeof(abr_data));
}
TEST_F(PaverServiceSkipBlockTest, SetActiveConfigurationHealthy) {
abr::Data abr_data = kAbrData;
abr_data.slots[1].tries_remaining = 3;
abr_data.slots[1].successful_boot = 0;
ComputeCrc(&abr_data);
SetAbr(abr_data);
abr_data.slots[1].tries_remaining = 0;
abr_data.slots[1].successful_boot = 1;
ComputeCrc(&abr_data);
auto result = client_->SetActiveConfigurationHealthy();
ASSERT_OK(result.status());
ASSERT_OK(result->status);
auto actual = GetAbr();
ASSERT_BYTES_EQ(&abr_data, &actual, sizeof(abr_data));
}
TEST_F(PaverServiceSkipBlockTest, SetActiveConfigurationHealthyBothPriorityZero) {
abr::Data abr_data = kAbrData;
abr_data.slots[1].tries_remaining = 3;
abr_data.slots[1].successful_boot = 0;
abr_data.slots[1].priority = 0;
ComputeCrc(&abr_data);
SetAbr(abr_data);
auto result = client_->SetActiveConfigurationHealthy();
ASSERT_OK(result.status());
ASSERT_NE(result->status, ZX_OK);
}
TEST_F(PaverServiceSkipBlockTest, WriteAssetKernelConfigA) {
::llcpp::fuchsia::mem::Buffer payload;
CreatePayload(2 * kPagesPerBlock, &payload);
auto result = client_->WriteAsset(::llcpp::fuchsia::paver::Configuration::A,
::llcpp::fuchsia::paver::Asset::KERNEL, std::move(payload));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
ValidateWritten(8, 2);
ValidateUnwritten(10, 4);
}
TEST_F(PaverServiceSkipBlockTest, WriteAssetKernelConfigB) {
::llcpp::fuchsia::mem::Buffer payload;
CreatePayload(2 * kPagesPerBlock, &payload);
auto result = client_->WriteAsset(::llcpp::fuchsia::paver::Configuration::B,
::llcpp::fuchsia::paver::Asset::KERNEL, std::move(payload));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
ValidateUnwritten(8, 2);
ValidateWritten(10, 2);
ValidateUnwritten(12, 2);
}
TEST_F(PaverServiceSkipBlockTest, WriteAssetKernelConfigRecovery) {
::llcpp::fuchsia::mem::Buffer payload;
CreatePayload(2 * kPagesPerBlock, &payload);
auto result = client_->WriteAsset(::llcpp::fuchsia::paver::Configuration::RECOVERY,
::llcpp::fuchsia::paver::Asset::KERNEL, std::move(payload));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
ValidateUnwritten(8, 4);
ValidateWritten(12, 2);
}
TEST_F(PaverServiceSkipBlockTest, WriteAssetVbMetaConfigA) {
::llcpp::fuchsia::mem::Buffer payload;
CreatePayload(32, &payload);
auto result = client_->WriteAsset(::llcpp::fuchsia::paver::Configuration::A,
::llcpp::fuchsia::paver::Asset::VERIFIED_BOOT_METADATA,
std::move(payload));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
ValidateWrittenPages(14 * kPagesPerBlock + 32, 32);
}
TEST_F(PaverServiceSkipBlockTest, WriteAssetVbMetaConfigB) {
::llcpp::fuchsia::mem::Buffer payload;
CreatePayload(32, &payload);
auto result = client_->WriteAsset(::llcpp::fuchsia::paver::Configuration::B,
::llcpp::fuchsia::paver::Asset::VERIFIED_BOOT_METADATA,
std::move(payload));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
ValidateWrittenPages(14 * kPagesPerBlock + 64, 32);
}
TEST_F(PaverServiceSkipBlockTest, WriteAssetVbMetaConfigRecovery) {
::llcpp::fuchsia::mem::Buffer payload;
CreatePayload(32, &payload);
auto result = client_->WriteAsset(::llcpp::fuchsia::paver::Configuration::RECOVERY,
::llcpp::fuchsia::paver::Asset::VERIFIED_BOOT_METADATA,
std::move(payload));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
ValidateWrittenPages(14 * kPagesPerBlock + 96, 32);
}
TEST_F(PaverServiceSkipBlockTest, WriteAssetTwice) {
::llcpp::fuchsia::mem::Buffer payload;
CreatePayload(2 * kPagesPerBlock, &payload);
auto result = client_->WriteAsset(::llcpp::fuchsia::paver::Configuration::A,
::llcpp::fuchsia::paver::Asset::KERNEL, std::move(payload));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
CreatePayload(2 * kPagesPerBlock, &payload);
ValidateWritten(8, 2);
ValidateUnwritten(10, 4);
result = client_->WriteAsset(::llcpp::fuchsia::paver::Configuration::A,
::llcpp::fuchsia::paver::Asset::KERNEL, std::move(payload));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
ValidateWritten(8, 2);
ValidateUnwritten(10, 4);
}
TEST_F(PaverServiceSkipBlockTest, ReadAssetKernelConfigA) {
WriteData(8 * kPagesPerBlock, 2 * kPagesPerBlock, 0x4a);
auto result = client_->ReadAsset(::llcpp::fuchsia::paver::Configuration::A,
::llcpp::fuchsia::paver::Asset::KERNEL);
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_response());
ValidateWritten(result->result.response().asset, 2 * kPagesPerBlock);
}
TEST_F(PaverServiceSkipBlockTest, ReadAssetKernelConfigB) {
WriteData(10 * kPagesPerBlock, 2 * kPagesPerBlock, 0x4a);
auto result = client_->ReadAsset(::llcpp::fuchsia::paver::Configuration::B,
::llcpp::fuchsia::paver::Asset::KERNEL);
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_response());
ValidateWritten(result->result.response().asset, 2 * kPagesPerBlock);
}
TEST_F(PaverServiceSkipBlockTest, ReadAssetKernelConfigRecovery) {
WriteData(12 * kPagesPerBlock, 2 * kPagesPerBlock, 0x4a);
auto result = client_->ReadAsset(::llcpp::fuchsia::paver::Configuration::RECOVERY,
::llcpp::fuchsia::paver::Asset::KERNEL);
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_response());
ValidateWritten(result->result.response().asset, 2 * kPagesPerBlock);
}
TEST_F(PaverServiceSkipBlockTest, ReadAssetVbMetaConfigA) {
WriteData(14 * kPagesPerBlock + 32, 32, 0x4a);
auto result = client_->ReadAsset(::llcpp::fuchsia::paver::Configuration::A,
::llcpp::fuchsia::paver::Asset::VERIFIED_BOOT_METADATA);
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_response());
ValidateWritten(result->result.response().asset, 32);
}
TEST_F(PaverServiceSkipBlockTest, ReadAssetVbMetaConfigB) {
WriteData(14 * kPagesPerBlock + 64, 32, 0x4a);
auto result = client_->ReadAsset(::llcpp::fuchsia::paver::Configuration::B,
::llcpp::fuchsia::paver::Asset::VERIFIED_BOOT_METADATA);
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_response());
ValidateWritten(result->result.response().asset, 32);
}
TEST_F(PaverServiceSkipBlockTest, ReadAssetVbMetaConfigRecovery) {
WriteData(14 * kPagesPerBlock + 96, 32, 0x4a);
auto result = client_->ReadAsset(::llcpp::fuchsia::paver::Configuration::RECOVERY,
::llcpp::fuchsia::paver::Asset::VERIFIED_BOOT_METADATA);
ASSERT_OK(result.status());
ASSERT_TRUE(result->result.is_response());
ValidateWritten(result->result.response().asset, 32);
}
TEST_F(PaverServiceSkipBlockTest, WriteBootloader) {
::llcpp::fuchsia::mem::Buffer payload;
CreatePayload(4 * kPagesPerBlock, &payload);
auto result = client_->WriteBootloader(std::move(payload));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
ValidateWritten(4, 4);
}
// We prefill the bootloader partition with the expected data, leaving the last block as 0xFF.
// Normally the last page would be overwritten with 0s, but because the actual payload is identical,
// we don't actually pave the image, so the extra page stays as 0xFF.
TEST_F(PaverServiceSkipBlockTest, WriteBootloaderNotAligned) {
::llcpp::fuchsia::mem::Buffer payload;
CreatePayload(4 * kPagesPerBlock, &payload);
payload.size = 4 * kPagesPerBlock - 1;
WriteData(4 * kPagesPerBlock, 4 * kPagesPerBlock - 1, 0x4a);
WriteData(8 * kPagesPerBlock - 1 , 1, 0xff);
auto result = client_->WriteBootloader(std::move(payload));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
ValidateWrittenPages(4 * kPagesPerBlock, 4 * kPagesPerBlock - 1);
ValidateUnwrittenPages(8 * kPagesPerBlock - 1, 1);
}
TEST_F(PaverServiceSkipBlockTest, WriteDataFile) {
// TODO(ZX-4007): Figure out a way to test this.
}
TEST_F(PaverServiceSkipBlockTest, WriteVolumes) {
// TODO(ZX-4007): Figure out a way to test this.
}
TEST_F(PaverServiceSkipBlockTest, WipeVolumes) {
// TODO(ZX-4007): Figure out a way to test this.
}
// TODO(34771): Re-enable once bug in GPT is fixed.
#if 0
class PaverServiceBlockTest : public PaverServiceTest {
public:
PaverServiceBlockTest() {
ASSERT_NO_FATAL_FAILURES(SpawnIsolatedDevmgr());
}
protected:
void SpawnIsolatedDevmgr() {
devmgr_launcher::Args args;
args.sys_device_driver = IsolatedDevmgr::kSysdevDriver;
args.driver_search_paths.push_back("/boot/driver");
args.disable_block_watcher = true;
ASSERT_OK(IsolatedDevmgr::Create(std::move(args), &devmgr_));
fbl::unique_fd fd;
ASSERT_OK(RecursiveWaitForFile(devmgr_.devfs_root(), "misc/ramctl", &fd));
static_cast<paver::Paver*>(provider_ctx_)->set_devfs_root(devmgr_.devfs_root().duplicate());
static_cast<paver::Paver*>(provider_ctx_)->set_svc_root(std::move(fake_svc_.svc_chan()));
}
IsolatedDevmgr devmgr_;
};
#if defined(__x86_64__)
constexpr uint8_t kEmptyType[GPT_GUID_LEN] = GUID_EMPTY_VALUE;
TEST_F(PaverServiceBlockTest, InitializePartitionTables) {
fbl::unique_ptr<BlockDevice> gpt_dev;
constexpr uint64_t block_count = (1LU << 34) / kBlockSize;
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kEmptyType, block_count,
&gpt_dev));
zx::channel gpt_chan;
ASSERT_OK(fdio_fd_clone(gpt_dev->fd(), gpt_chan.reset_and_get_address()));
auto result = client_->InitializePartitionTables(std::move(gpt_chan));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
}
TEST_F(PaverServiceBlockTest, InitializePartitionTablesMultipleDevices) {
fbl::unique_ptr<BlockDevice> gpt_dev1, gpt_dev2;
constexpr uint64_t block_count = (1LU << 34) / kBlockSize;
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kEmptyType, block_count,
&gpt_dev1));
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kEmptyType, block_count,
&gpt_dev2));
zx::channel gpt_chan;
ASSERT_OK(fdio_fd_clone(gpt_dev1->fd(), gpt_chan.reset_and_get_address()));
auto result = client_->InitializePartitionTables(std::move(gpt_chan));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
}
TEST_F(PaverServiceBlockTest, WipePartitionTables) {
fbl::unique_ptr<BlockDevice> gpt_dev;
constexpr uint64_t block_count = (1LU << 34) / kBlockSize;
ASSERT_NO_FATAL_FAILURES(BlockDevice::Create(devmgr_.devfs_root(), kEmptyType, block_count,
&gpt_dev));
zx::channel gpt_chan;
ASSERT_OK(fdio_fd_clone(gpt_dev->fd(), gpt_chan.reset_and_get_address()));
auto result = client_->InitializePartitionTables(std::move(gpt_chan));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
ASSERT_OK(fdio_fd_clone(gpt_dev->fd(), gpt_chan.reset_and_get_address()));
auto wipe_result = client_->WipePartitionTables(std::move(gpt_chan));
ASSERT_OK(wipe_result.status());
ASSERT_OK(wipe_result.value().status);
}
#endif
#endif
} // namespace