blob: 02aed2e9570ee306e05d5225492f6da238a1dc28 [file] [log] [blame]
// Copyright 2021 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.
#![recursion_limit = "256"]
#![allow(clippy::too_many_arguments)]
// TODO(https://fxbug.dev/42073005): Remove this allow once the lint is fixed.
#![allow(unknown_lints, clippy::extra_unused_type_parameters)]
// Avoid unused crate warnings on non-test/non-debug builds because this needs to be an
// unconditional dependency for rustdoc generation.
use extended_pstate as _;
use tracing_mutex as _;
use anyhow::{Context as _, Error};
use fidl::endpoints::ControlHandle;
use fidl_fuchsia_component_runner as frunner;
use fidl_fuchsia_process_lifecycle as flifecycle;
use fidl_fuchsia_starnix_container as fstarcontainer;
use fuchsia_async as fasync;
use fuchsia_component::server::ServiceFs;
use fuchsia_inspect::health::Reporter;
use fuchsia_runtime as fruntime;
use fuchsia_zircon as zx;
use futures::{StreamExt, TryStreamExt};
use starnix_core::mm::{init_usercopy, zxio_maybe_faultable_copy_impl};
use starnix_kernel_runner::{
create_component_from_stream, serve_component_runner, serve_container_controller, Container,
ContainerServiceConfig,
};
use starnix_logging::{log_debug, trace_instant, CATEGORY_STARNIX, NAME_START_KERNEL};
/// Overrides the `zxio_maybe_faultable_copy` weak symbol found in zxio.
#[no_mangle]
extern "C" fn zxio_maybe_faultable_copy(
dest: *mut u8,
src: *const u8,
count: usize,
ret_dest: bool,
) -> bool {
// SAFETY: we know that we are either copying from or to a buffer that
// zxio (and thus Starnix) owns per `zxio_maybe_faultable_copy`'s
// documentation.
unsafe { zxio_maybe_faultable_copy_impl(dest, src, count, ret_dest) }
}
/// Overrides the `zxio_fault_catching_disabled` weak symbol found in zxio.
#[no_mangle]
extern "C" fn zxio_fault_catching_disabled() -> bool {
false
}
fn maybe_serve_lifecycle() {
if let Some(lifecycle) =
fruntime::take_startup_handle(fruntime::HandleInfo::new(fruntime::HandleType::Lifecycle, 0))
{
fasync::Task::local(async move {
if let Ok(mut stream) =
fidl::endpoints::ServerEnd::<flifecycle::LifecycleMarker>::new(lifecycle.into())
.into_stream()
{
if let Ok(Some(request)) = stream.try_next().await {
match request {
flifecycle::LifecycleRequest::Stop { control_handle } => {
control_handle.shutdown();
std::process::exit(0);
}
}
}
}
})
.detach();
}
}
enum KernelServices {
/// This service lets clients start a single container using this kernel.
///
/// The `starnix_kernel` is capable of running a single container, which can be started using
/// this protocol. Attempts to use this protocol a second time will fail.
///
/// This service uses the `ComponentRunner` protocol but the service is exposed using the name
/// `fuchsia.starnix.container.Runner` to reduce confusion with the instance of the
/// `ComponentRunner` protocol that runs components inside the container.
ContainerRunner(frunner::ComponentRunnerRequestStream),
/// This service lets clients run components inside the container being run by this kernel.
///
/// This service will wait to process any requests until the kernel starts a container.
///
/// This service is also exposed via the container itself.
ComponentRunner(frunner::ComponentRunnerRequestStream),
/// This service lets clients control the container being run by this kernel.
///
/// This service will wait to process any requests until the kernel starts a container.
///
/// This service is also exposed via the container itself.
ContainerController(fstarcontainer::ControllerRequestStream),
}
async fn build_container(
stream: frunner::ComponentRunnerRequestStream,
returned_config: &mut Option<ContainerServiceConfig>,
) -> Result<Container, Error> {
let (container, config) = create_component_from_stream(stream).await?;
*returned_config = Some(config);
Ok(container)
}
#[fuchsia::main(
logging_tags = ["starnix"],
logging_blocking,
logging_panic_prefix="\n\n\n\nSTARNIX KERNEL PANIC\n\n\n\n",
)]
async fn main() -> Result<(), Error> {
// Make sure that if this process panics in normal mode that the whole kernel's job is killed.
fruntime::job_default()
.set_critical(zx::JobCriticalOptions::RETCODE_NONZERO, &*fruntime::process_self())
.context("ensuring main process panics kill whole kernel")?;
let _inspect_server_task = inspect_runtime::publish(
fuchsia_inspect::component::init_inspector_with_size(1_000_000),
inspect_runtime::PublishOptions::default(),
);
let mut health = fuchsia_inspect::component::health();
health.set_starting_up();
fuchsia_trace_provider::trace_provider_create_with_fdio();
fuchsia_trace_provider::trace_provider_wait_for_init();
trace_instant!(CATEGORY_STARNIX, NAME_START_KERNEL, fuchsia_trace::Scope::Thread);
let container = async_lock::OnceCell::<Container>::new();
maybe_serve_lifecycle();
let mut fs = ServiceFs::new_local();
fs.dir("svc")
.add_fidl_service_at("fuchsia.starnix.container.Runner", KernelServices::ContainerRunner)
.add_fidl_service(KernelServices::ComponentRunner)
.add_fidl_service(KernelServices::ContainerController);
let inspector = fuchsia_inspect::component::inspector();
#[cfg(target_arch = "x86_64")]
{
inspector.root().record_string(
"x86_64_extended_pstate_strategy",
format!("{:?}", *extended_pstate::x86_64::PREFERRED_STRATEGY),
);
}
inspector.root().record_lazy_child("not_found", starnix_logging::not_found_lazy_node_callback);
inspector.root().record_lazy_child("stubs", starnix_logging::track_stub_lazy_node_callback);
log_debug!("Serving kernel services on outgoing directory handle.");
fs.take_and_serve_directory_handle()?;
health.set_ok();
// We call this early during Starnix boot to make sure the usercopy utilities
// are ready for use before any restricted-mode/Linux processes are created.
init_usercopy();
fs.for_each_concurrent(None, |request: KernelServices| async {
match request {
KernelServices::ContainerRunner(stream) => {
let mut config: Option<ContainerServiceConfig> = None;
let container = container
.get_or_try_init(|| build_container(stream, &mut config))
.await
.expect("failed to start container");
if let Some(config) = config {
container
.serve(config)
.await
.expect("failed to serve the expected services from the container");
}
}
KernelServices::ComponentRunner(stream) => {
serve_component_runner(stream, &container.wait().await.system_task())
.await
.expect("failed to start component runner");
}
KernelServices::ContainerController(stream) => {
serve_container_controller(stream, &container.wait().await.system_task())
.await
.expect("failed to start container controller");
}
}
})
.await;
Ok(())
}