| // 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 |