blob: 9da18ff9e9c7f65d1c94dd783b49dfd95459c7b0 [file]
// Copyright 2026 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 "lib/userboot/startup.h"
#include <lib/zx/channel.h>
#include <lib/zx/time.h>
#include <lib/zx/vmar.h>
#include <zircon/assert.h>
#include <zircon/startup.h>
#include <zircon/status.h>
#include <zircon/syscalls/object.h>
#include <algorithm>
#include <array>
#include <cinttypes>
#include <span>
#include <utility>
namespace {
zx::channel gBootstrapChannel;
} // namespace
zx_handle_t TakeBootstrapChannel() { return gBootstrapChannel.release(); }
zx_startup_handles_t _zx_startup_get_handles(zx_handle_t process_start_handle) {
gBootstrapChannel.reset(process_start_handle);
zx_signals_t pending;
zx_status_t status =
gBootstrapChannel.wait_one(ZX_CHANNEL_READABLE, zx::time::infinite(), &pending);
ZX_ASSERT_MSG(status == ZX_OK, "bootstrap channel wait: %s", zx_status_get_string(status));
ZX_DEBUG_ASSERT(pending & ZX_CHANNEL_READABLE);
std::array<zx_handle_info_t, ZX_CHANNEL_MAX_MSG_HANDLES> handle_buffer;
uint32_t actual_handles = 0;
status = gBootstrapChannel.read_etc(0, nullptr, handle_buffer.data(), 0, handle_buffer.size(),
nullptr, &actual_handles);
ZX_ASSERT_MSG(status == ZX_OK, "bootstrap channel read: %s", zx_status_get_string(status));
std::span handles = std::span{handle_buffer}.subspan(0, actual_handles);
zx_startup_handles_t startup = {};
// For most things, there should only be one handle of that object type.
constexpr auto take = [](zx_handle_t handle, zx_handle_t& startup_handle, const char* what) {
if (startup_handle != ZX_HANDLE_INVALID) {
ZX_PANIC("multiple %s handles in first bootstrap message", what);
}
startup_handle = handle;
};
// For VMARs there are two, and we don't presume about their order.
auto take_vmar = [&startup, first_vmar = zx::vmar{}](zx_handle_t handle) mutable {
if (startup.allocation_vmar != ZX_HANDLE_INVALID) {
ZX_PANIC("more than two VMAR handles in first bootstrap message");
}
if (!first_vmar) {
first_vmar.reset(handle);
return;
}
zx::vmar second_vmar{handle};
// Find the bounds of each VMAR to see which is which.
auto get_vmar_info = [](const zx::vmar& vmar, zx_info_vmar_t& info) {
zx_status_t status = vmar.get_info(ZX_INFO_VMAR, &info, sizeof(info), nullptr, nullptr);
ZX_ASSERT_MSG(status == ZX_OK, "ZX_INFO_VMAR on VMAR from first bootstrap message: %s",
zx_status_get_string(status));
};
zx_info_vmar_t first, second;
get_vmar_info(first_vmar, first);
get_vmar_info(second_vmar, second);
// The root VMAR will wholly contain the ELF image VMAR.
if (first.len < second.len) {
std::swap(first_vmar, second_vmar);
std::swap(first, second);
}
ZX_ASSERT_MSG(first.len > second.len,
"{base=%#" PRIx64 ", len=%zx} vs {base=%#" PRIx64 ", len=%zx}", first.base,
first.len, second.base, second.len);
ZX_ASSERT_MSG(first.base <= second.base,
"{base=%#" PRIx64 ", len=%zx} vs {base=%#" PRIx64 ", len=%zx}", first.base,
first.len, second.base, second.len);
ZX_ASSERT_MSG(second.base - first.base < first.len,
"{base=%#" PRIx64 ", len=%zx} vs {base=%#" PRIx64 ", len=%zx}", first.base,
first.len, second.base, second.len);
ZX_ASSERT_MSG(second.len <= first.len - (second.base - first.base),
"{base=%#" PRIx64 ", len=%zx} vs {base=%#" PRIx64 ", len=%zx}", first.base,
first.len, second.base, second.len);
startup.allocation_vmar = first_vmar.release();
startup.executable_image_vmar = second_vmar.release();
};
for (const auto& handle : handles) {
switch (handle.type) {
case ZX_OBJ_TYPE_DEBUGLOG:
take(handle.handle, startup.log, "debuglog");
break;
case ZX_OBJ_TYPE_SOCKET:
take(handle.handle, startup.log, "logging socket");
break;
case ZX_OBJ_TYPE_PROCESS:
take(handle.handle, startup.process_self, "process");
break;
case ZX_OBJ_TYPE_THREAD:
take(handle.handle, startup.thread_self, "thread");
break;
case ZX_OBJ_TYPE_VMAR:
take_vmar(handle.handle);
break;
default:
ZX_PANIC("unexpected handle type %u in bootstrap channel first message", handle.type);
}
}
return startup;
}
zx_startup_arguments_t _zx_startup_get_arguments(void* hook) {
// No arguments or environment provided for main.
return {};
}
void _zx_startup_preinit(void* hook) {
// No other initialization work to do. The UTC clock is not available.
}