blob: 7b96cb4e7621c2a414bacfd2956f8c21dc259560 [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 <ctype.h>
#include <fbl/string.h>
#include <fbl/vector.h>
#include <fuchsia/boot/c/fidl.h>
#include <launchpad/launchpad.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/fdio/fdio.h>
#include <lib/zx/debuglog.h>
#include <lib/zx/job.h>
#include <sstream>
#include <stdio.h>
#include <thread>
#include <utility>
#include <zircon/boot/image.h>
#include <zircon/dlfcn.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include "bootfs-loader-service.h"
#include "bootfs-service.h"
#include "svcfs-service.h"
#include "util.h"
namespace {
// Wire up stdout so that printf() and friends work.
void SetupStdout() {
zx::debuglog h;
if (zx::debuglog::create(zx::resource(), 0, &h) < 0) {
return;
}
fdio_t* logger = nullptr;
zx_status_t status = fdio_create(h.release(), &logger);
if (status != ZX_OK) {
return;
}
close(1);
fdio_bind_to_fd(logger, 1, 0);
}
// Load the boot arguments from bootfs and environment variables.
zx_status_t LoadBootArgs(const fbl::RefPtr<bootsvc::BootfsService>& bootfs, zx::vmo* out,
uint64_t* size) {
// TODO(teisenbe): Rename this file
const char* config_path = "/config/devmgr";
fbl::Vector<char> buf;
zx::vmo config_vmo;
uint64_t file_size;
zx_status_t status = bootfs->Open(config_path, &config_vmo, &file_size);
if (status == ZX_OK) {
auto config = std::make_unique<char[]>(file_size);
status = config_vmo.read(config.get(), 0, file_size);
if (status != ZX_OK) {
return status;
}
// Parse boot arguments file from bootfs.
std::string_view str(config.get(), file_size);
status = bootsvc::ParseBootArgs(std::move(str), &buf);
if (status != ZX_OK) {
return status;
}
}
// Add boot arguments from environment variables.
for (char** e = environ; *e != nullptr; e++) {
for (const char* x = *e; *x != 0; x++) {
buf.push_back(*x);
}
buf.push_back(0);
}
// Copy boot arguments into VMO.
zx::vmo args_vmo;
status = zx::vmo::create(buf.size(), ZX_VMO_NON_RESIZABLE, &args_vmo);
if (status != ZX_OK) {
return status;
}
status = args_vmo.write(buf.get(), 0, buf.size());
if (status != ZX_OK) {
return status;
}
status = args_vmo.replace(ZX_DEFAULT_VMO_RIGHTS & ~ZX_RIGHT_WRITE, &args_vmo);
if (status != ZX_OK) {
return status;
}
*out = std::move(args_vmo);
*size = buf.size();
return ZX_OK;
}
// Launch the next process in the boot chain.
// It will receive:
// - stdout wired up via a debuglog handle
// - The boot cmdline arguments, via envp
// - A namespace containing a /boot, serviced by bootsvc
// - A loader that can load libraries from /boot, serviced by bootsvc
// - A handle to the root job
// - A handle to each of the bootdata VMOs the kernel provided
// - A handle to a channel containing the root resource, with type
// BOOTSVC_ROOT_RESOURCE_CHANNEL_HND
void LaunchNextProcess(fbl::RefPtr<bootsvc::BootfsService> bootfs,
fbl::RefPtr<bootsvc::SvcfsService> svcfs,
fbl::RefPtr<bootsvc::BootfsLoaderService> loader_svc) {
const char* bootsvc_next = getenv("bootsvc.next");
if (bootsvc_next == nullptr) {
bootsvc_next = "bin/devcoordinator";
}
// Split the bootsvc.next value into 1 or more arguments using ',' as a
// delimiter.
printf("bootsvc: bootsvc.next = %s\n", bootsvc_next);
fbl::Vector<fbl::String> next_args = bootsvc::SplitString(bootsvc_next, ',');
// Open the executable we will start next
zx::vmo program;
uint64_t file_size;
const char* next_program = next_args[0].c_str();
zx_status_t status = bootfs->Open(next_program, &program, &file_size);
ZX_ASSERT_MSG(status == ZX_OK, "bootsvc: failed to open '%s': %s\n", next_program,
zx_status_get_string(status));
// Get the bootfs fuchsia.io.Node service channel that we will hand to the
// next process in the boot chain.
zx::channel bootfs_conn;
status = bootfs->CreateRootConnection(&bootfs_conn);
ZX_ASSERT_MSG(status == ZX_OK, "bootfs conn creation failed: %s\n",
zx_status_get_string(status));
zx::channel svcfs_conn;
status = svcfs->CreateRootConnection(&svcfs_conn);
ZX_ASSERT_MSG(status == ZX_OK, "svcfs conn creation failed: %s\n",
zx_status_get_string(status));
const char* nametable[2] = {};
uint32_t count = 0;
launchpad_t* lp;
launchpad_create(0, next_program, &lp);
{
// Use the local loader service backed directly by the primary BOOTFS.
zx::channel local_loader_conn;
status = loader_svc->Connect(&local_loader_conn);
ZX_ASSERT_MSG(status == ZX_OK,
"failed to connect to BootfsLoaderService : %s\n",
zx_status_get_string(status));
zx_handle_t old =
launchpad_use_loader_service(lp, local_loader_conn.release());
ZX_ASSERT(old == ZX_HANDLE_INVALID);
}
launchpad_load_from_vmo(lp, program.release());
launchpad_clone(lp, LP_CLONE_DEFAULT_JOB);
launchpad_add_handle(lp, bootfs_conn.release(), PA_HND(PA_NS_DIR, count));
nametable[count++] = "/boot";
launchpad_add_handle(lp, svcfs_conn.release(), PA_HND(PA_NS_DIR, count));
nametable[count++] = "/bootsvc";
int argc = static_cast<int>(next_args.size());
const char* argv[argc];
for (int i = 0; i < argc; ++i) {
argv[i] = next_args[i].c_str();
}
launchpad_set_args(lp, argc, argv);
ZX_ASSERT(count <= fbl::count_of(nametable));
launchpad_set_nametable(lp, count, nametable);
zx::debuglog debuglog;
status = zx::debuglog::create(zx::resource(), 0, &debuglog);
if (status != ZX_OK) {
launchpad_abort(lp, status, "bootsvc: cannot create debuglog handle");
} else {
launchpad_add_handle(lp, debuglog.release(), PA_HND(PA_FD, FDIO_FLAG_USE_FOR_STDIO | 0));
}
const char* errmsg;
status = launchpad_go(lp, nullptr, &errmsg);
if (status != ZX_OK) {
printf("bootsvc: launchpad %s failed: %s: %s\n", next_program, errmsg,
zx_status_get_string(status));
} else {
printf("bootsvc: Launched %s\n", next_program);
}
}
} // namespace
int main(int argc, char** argv) {
SetupStdout();
printf("bootsvc: Starting...\n");
// Close the loader-service channel so the service can go away.
// We won't use it any more (no dlopen calls in this process).
zx_handle_close(dl_set_loader_service(ZX_HANDLE_INVALID));
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
zx::vmo bootfs_vmo(zx_take_startup_handle(PA_HND(PA_VMO_BOOTFS, 0)));
ZX_ASSERT(bootfs_vmo.is_valid());
// Set up the bootfs service
printf("bootsvc: Creating bootfs service...\n");
fbl::RefPtr<bootsvc::BootfsService> bootfs_svc;
zx_status_t status = bootsvc::BootfsService::Create(loop.dispatcher(), &bootfs_svc);
ZX_ASSERT_MSG(status == ZX_OK, "BootfsService creation failed: %s\n",
zx_status_get_string(status));
status = bootfs_svc->AddBootfs(std::move(bootfs_vmo));
ZX_ASSERT_MSG(status == ZX_OK, "bootfs add failed: %s\n", zx_status_get_string(status));
// Process the ZBI boot image
printf("bootsvc: Retrieving boot image...\n");
zx::vmo image_vmo;
bootsvc::ItemMap item_map;
status = bootsvc::RetrieveBootImage(&image_vmo, &item_map);
ZX_ASSERT_MSG(status == ZX_OK, "Retrieving boot image failed: %s\n",
zx_status_get_string(status));
// Load boot arguments into VMO
printf("bootsvc: Loading boot arguments...\n");
zx::vmo args_vmo;
uint64_t args_size = 0;
status = LoadBootArgs(bootfs_svc, &args_vmo, &args_size);
ZX_ASSERT_MSG(status == ZX_OK, "Loading boot arguments failed: %s\n",
zx_status_get_string(status));
// Set up the svcfs service
printf("bootsvc: Creating svcfs service...\n");
fbl::RefPtr<bootsvc::SvcfsService> svcfs_svc = bootsvc::SvcfsService::Create(loop.dispatcher());
svcfs_svc->AddService(fuchsia_boot_Arguments_Name,
bootsvc::CreateArgumentsService(loop.dispatcher(), std::move(args_vmo),
args_size));
svcfs_svc->AddService(fuchsia_boot_Items_Name,
bootsvc::CreateItemsService(loop.dispatcher(), std::move(image_vmo),
std::move(item_map)));
zx::job::default_job()->set_property(ZX_PROP_NAME, "root", 4);
svcfs_svc->AddService(fuchsia_boot_RootJob_Name,
bootsvc::CreateRootJobService(loop.dispatcher()));
svcfs_svc->AddService(fuchsia_boot_RootResource_Name,
bootsvc::CreateRootResourceService(loop.dispatcher()));
// Consume certain VMO types from the startup handle table
printf("bootsvc: Loading kernel VMOs...\n");
bootfs_svc->PublishStartupVmos(PA_VMO_VDSO, "PA_VMO_VDSO");
bootfs_svc->PublishStartupVmos(PA_VMO_KERNEL_FILE, "PA_VMO_KERNEL_FILE");
// Creating the loader service
printf("bootsvc: Creating loader service...\n");
fbl::RefPtr<bootsvc::BootfsLoaderService> loader_svc;
status = bootsvc::BootfsLoaderService::Create(bootfs_svc, loop.dispatcher(), &loader_svc);
ZX_ASSERT_MSG(status == ZX_OK, "BootfsLoaderService creation failed: %s\n",
zx_status_get_string(status));
// Launch the next process in the chain. This must be in a thread, since
// it may issue requests to the loader, which runs in the async loop that
// starts running after this.
printf("bootsvc: Launching next process...\n");
std::thread(LaunchNextProcess, bootfs_svc, svcfs_svc, loader_svc).detach();
// Begin serving the bootfs fileystem and loader
loop.Run();
return 0;
}