blob: ba20b8acdf719ef3984a96369222f19976d5a9eb [file] [log] [blame]
// Copyright 2020 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/bringup/bin/bootsvc/bootfs-loader-service.h"
#include <lib/fit/defer.h>
#include <lib/zx/vmar.h>
#include <zircon/boot/bootfs.h>
#include <zircon/errors.h>
#include <cstddef>
#include <gtest/gtest.h>
#include "src/lib/loader_service/loader_service_test_fixture.h"
#define ASSERT_OK(expr) ASSERT_EQ(ZX_OK, expr)
#define EXPECT_OK(expr) EXPECT_EQ(ZX_OK, expr)
namespace bootsvc {
namespace {
using namespace loader::test;
namespace fldsvc = fuchsia_ldsvc;
struct BootfsDirectoryEntry {
std::string path;
std::string file_contents;
};
static zx::status<zx::vmo> GenerateBootfs(std::vector<BootfsDirectoryEntry> config) {
// Simplified VMO size calculation assuming each file's contents is no larger than a page (checked
// below) and dirents are max size.
uint64_t data_start =
ZBI_BOOTFS_PAGE_ALIGN(sizeof(zbi_bootfs_header_t) +
config.size() * ZBI_BOOTFS_DIRENT_SIZE(ZBI_BOOTFS_MAX_NAME_LEN));
uint64_t vmo_size = data_start + config.size() * ZBI_BOOTFS_PAGE_SIZE;
zx::vmo vmo;
zx_status_t status = zx::vmo::create(vmo_size, 0, &vmo);
if (status != ZX_OK) {
return zx::error(status);
}
uintptr_t mapped = 0;
status =
zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, vmo, 0, vmo_size, &mapped);
if (status != ZX_OK) {
return zx::error(status);
}
auto unmap = fit::defer([=] { zx::vmar::root_self()->unmap(mapped, vmo_size); });
// Write directory entries and data.
uintptr_t dirent_ptr = mapped + sizeof(zbi_bootfs_header_t);
uintptr_t data_ptr = mapped + data_start;
for (auto entry : config) {
auto dirent = reinterpret_cast<zbi_bootfs_dirent_t*>(dirent_ptr);
char* data = reinterpret_cast<char*>(data_ptr);
dirent->name_len = entry.path.size() + 1;
dirent->data_len = entry.file_contents.size() + 1;
if (dirent->data_len > ZBI_BOOTFS_PAGE_SIZE) {
// Check assumption made above when sizing VMO.
return zx::error(ZX_ERR_INTERNAL);
}
dirent->data_off = data_ptr - mapped;
memcpy(dirent->name, entry.path.c_str(), dirent->name_len);
memcpy(data, entry.file_contents.c_str(), dirent->data_len);
dirent_ptr += ZBI_BOOTFS_DIRENT_SIZE(dirent->name_len);
data_ptr += ZBI_BOOTFS_PAGE_SIZE;
}
// Write main header now that we know exact size of dirents.
auto hdr = reinterpret_cast<zbi_bootfs_header_t*>(mapped);
hdr->magic = ZBI_BOOTFS_MAGIC;
hdr->dirsize = dirent_ptr - mapped - sizeof(zbi_bootfs_header_t);
return zx::ok(std::move(vmo));
}
class BootfsLoaderServiceTest : public LoaderServiceTest {
public:
void CreateTestLoader(std::vector<BootfsDirectoryEntry> config,
std::shared_ptr<BootfsLoaderService>* loader) {
zx::resource vmex;
zx::status<zx::unowned_resource> unowned_vmex = GetVmexResource();
ASSERT_OK(unowned_vmex.status_value());
ASSERT_TRUE(unowned_vmex.value()->is_valid());
ASSERT_OK(unowned_vmex.value()->duplicate(ZX_RIGHT_SAME_RIGHTS, &vmex));
fbl::RefPtr<BootfsService> bootfs_svc;
ASSERT_OK(BootfsService::Create(fs_loop().dispatcher(), std::move(vmex), &bootfs_svc));
auto bootfs_vmo = GenerateBootfs(std::move(config));
ASSERT_OK(bootfs_vmo.status_value());
ASSERT_OK(bootfs_svc->AddBootfs(std::move(bootfs_vmo).value()));
*loader = BootfsLoaderService::Create(loader_loop().dispatcher(), std::move(bootfs_svc));
ASSERT_OK(fs_loop().StartThread("fs_loop"));
ASSERT_OK(loader_loop().StartThread("loader_loop"));
}
};
TEST_F(BootfsLoaderServiceTest, LoadObject) {
std::shared_ptr<BootfsLoaderService> loader;
std::vector<BootfsDirectoryEntry> config = {
{"lib/libfoo.so", "foo"},
{"lib/asan/libfoo.so", "asan foo"},
};
ASSERT_NO_FATAL_FAILURE(CreateTestLoader(std::move(config), &loader));
auto status = loader->Connect();
ASSERT_TRUE(status.is_ok());
fidl::WireSyncClient<fldsvc::Loader> client(std::move(status.value()));
EXPECT_NO_FATAL_FAILURE(LoadObject(client, "missing", zx::error(ZX_ERR_NOT_FOUND)));
EXPECT_NO_FATAL_FAILURE(LoadObject(client, "libfoo.so", zx::ok("foo")));
ASSERT_NO_FATAL_FAILURE(Config(client, "asan", zx::ok(ZX_OK)));
EXPECT_NO_FATAL_FAILURE(LoadObject(client, "libfoo.so", zx::ok("asan foo")));
}
} // namespace
} // namespace bootsvc