blob: 344cbbb6fbff36884ba1923cbc6801253c542e45 [file] [log] [blame]
// Copyright 2018 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 <dirent.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <utility>
#include <fbl/algorithm.h>
#include <fbl/string.h>
#include <fbl/string_piece.h>
#include <fbl/unique_fd.h>
#include <fbl/unique_ptr.h>
#include <lib/bootsvc-protocol/processargs.h>
#include <lib/fdio/namespace.h>
#include <lib/zx/channel.h>
#include <lib/zx/job.h>
#include <lib/zx/vmo.h>
#include <unittest/unittest.h>
#include <zircon/boot/bootdata.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include "util.h"
namespace {
// Check that bootsvc put the boot cmdline in env
bool TestBootCmdline() {
BEGIN_TEST;
ASSERT_STR_EQ(getenv("bootsvc.next"), "bin/bootsvc-tests");
END_TEST;
}
// Make sure that bootsvc passed the bootdata here, and check if it published
// a crashlog if one of the bootdata had one.
bool TestBootdata() {
BEGIN_TEST;
fbl::Vector<zx::vmo> bootdata_vmos = bootsvc::RetrieveBootdata();
ASSERT_GT(bootdata_vmos.size(), 0);
for (const zx::vmo& vmo : bootdata_vmos) {
bootdata_t bootdata;
zx_status_t status = vmo.read(&bootdata, 0, sizeof(bootdata));
if (status < 0) {
continue;
}
if ((bootdata.type != BOOTDATA_CONTAINER) || (bootdata.extra != BOOTDATA_MAGIC)) {
continue;
}
if (!(bootdata.flags & BOOTDATA_FLAG_V2)) {
continue;
}
size_t len = bootdata.length;
size_t off = sizeof(bootdata);
while (len > sizeof(bootdata)) {
zx_status_t status = vmo.read(&bootdata, off, sizeof(bootdata));
if (status < 0) {
break;
}
size_t itemlen = BOOTDATA_ALIGN(sizeof(bootdata_t) + bootdata.length);
if (itemlen > len) {
break;
}
switch (bootdata.type) {
case BOOTDATA_LAST_CRASHLOG: {
// If we see a LAST_CRASHLOG entry, then the kernel should have
// translated it into a VMO file, and bootsvc should have put it
// at the path below.
char path[strlen(bootsvc::kLastPanicFilePath) + 7];
snprintf(path, sizeof(path), "/boot/%s", bootsvc::kLastPanicFilePath);
auto file_buffer = fbl::make_unique<uint8_t[]>(bootdata.length);
auto vmo_buffer = fbl::make_unique<uint8_t[]>(bootdata.length);
fbl::unique_fd fd(open(path, O_RDONLY));
ASSERT_TRUE(fd.is_valid());
ASSERT_EQ(read(fd.get(), file_buffer.get(), bootdata.length), bootdata.length);
ASSERT_EQ(vmo.read(vmo_buffer.get(), off + sizeof(bootdata_t), bootdata.length), ZX_OK);
ASSERT_BYTES_EQ(file_buffer.get(), vmo_buffer.get(), bootdata.length, "");
break;
}
}
off += itemlen;
len -= itemlen;
}
}
END_TEST;
}
// Make sure the loader works
bool TestLoader() {
BEGIN_TEST;
// Request loading a library we don't use
void* ptr = dlopen("libdriver.so", RTLD_LAZY | RTLD_LOCAL);
ASSERT_NOT_NULL(ptr);
dlclose(ptr);
END_TEST;
}
// Make sure that bootsvc gave us a namespace with only /boot
bool TestNamespace() {
BEGIN_TEST;
fdio_flat_namespace_t* ns;
ASSERT_EQ(fdio_ns_export_root(&ns), ZX_OK);
// Close the cloned handles, since we don't need them
for (size_t i = 0; i < ns->count; ++i) {
zx_handle_close(ns->handle[i]);
}
ASSERT_EQ(ns->count, 1);
ASSERT_STR_EQ(ns->path[0], "/boot");
free(ns);
END_TEST;
}
// Check that bootsvc gave us the expected handles
bool TestStartupHandles() {
BEGIN_TEST;
// Check we were given a channel that when read produces a resource handle (should be the
// root one)
zx::channel resource_channel(zx_take_startup_handle(BOOTSVC_ROOT_RESOURCE_CHANNEL_HND));
ASSERT_TRUE(resource_channel.is_valid());
zx::handle root_resource;
uint32_t actual = 0;
ASSERT_EQ(resource_channel.read(0, nullptr, 0, nullptr, root_resource.reset_and_get_address(),
1, &actual), ZX_OK);
ASSERT_EQ(actual, 1);
ASSERT_TRUE(root_resource.is_valid());
zx_info_handle_basic_t basic;
ASSERT_EQ(root_resource.get_info(ZX_INFO_HANDLE_BASIC, &basic, sizeof(basic), nullptr, nullptr),
ZX_OK);
ASSERT_EQ(basic.type, ZX_OBJ_TYPE_RESOURCE);
// Check we were given a job handle (should be the root job)
ASSERT_TRUE(zx::job::default_job()->is_valid());
END_TEST;
}
// Check that the kernel-provided VDSOs were added to /boot/kernel/vdso
bool TestVdsosPresent() {
BEGIN_TEST;
DIR* dir = opendir("/boot/kernel/vdso");
ASSERT_NOT_NULL(dir);
size_t count = 0;
dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
if (!strcmp(entry->d_name, ".")) {
continue;
}
ASSERT_EQ(entry->d_type, DT_REG);
++count;
}
ASSERT_GT(count, 0);
closedir(dir);
END_TEST;
}
} // namespace
BEGIN_TEST_CASE(bootsvc_integration_tests)
RUN_TEST(TestBootCmdline)
RUN_TEST(TestBootdata)
RUN_TEST(TestLoader)
RUN_TEST(TestNamespace)
RUN_TEST(TestStartupHandles)
RUN_TEST(TestVdsosPresent)
END_TEST_CASE(bootsvc_integration_tests)
int main(int argc, char** argv) {
return unittest_run_all_tests(argc, argv) ? 0 : -1;
}