blob: b22bc04c87ee2d3005f167a8ae4037ddf7d8202c [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.
use anyhow::Error;
use ffeedback::FileReportResults;
use fidl::prelude::*;
use fuchsia_component_test::LocalComponentHandles;
use futures::channel::mpsc::{self};
use futures::future::BoxFuture;
use futures::{FutureExt as _, SinkExt as _, StreamExt as _};
use std::sync::Arc;
use vfs::directory::entry_container::Directory;
use vfs::execution_scope::ExecutionScope;
use vfs::path::Path;
use vfs::service;
use {
fidl_fuchsia_boot as fboot, fidl_fuchsia_feedback as ffeedback, fidl_fuchsia_io as fio,
fuchsia_zircon as zx,
};
/// Identifier for ramdisk storage. Defined in sdk/lib/zbi-format/include/lib/zbi-format/zbi.h.
const ZBI_TYPE_STORAGE_RAMDISK: u32 = 0x4b534452;
pub async fn new_mocks(
netboot: bool,
vmo: Option<zx::Vmo>,
crash_reports_sink: mpsc::Sender<ffeedback::CrashReport>,
) -> impl Fn(LocalComponentHandles) -> BoxFuture<'static, Result<(), Error>> + Sync + Send + 'static
{
let vmo = vmo.map(Arc::new);
let mock = move |handles: LocalComponentHandles| {
let vmo_clone = vmo.clone();
run_mocks(handles, netboot, vmo_clone, crash_reports_sink.clone()).boxed()
};
mock
}
async fn run_mocks(
handles: LocalComponentHandles,
netboot: bool,
vmo: Option<Arc<zx::Vmo>>,
crash_reports_sink: mpsc::Sender<ffeedback::CrashReport>,
) -> Result<(), Error> {
let export = vfs::pseudo_directory! {
"svc" => vfs::pseudo_directory! {
fboot::ArgumentsMarker::PROTOCOL_NAME => service::host(move |stream| {
run_boot_args(stream, netboot)
}),
fboot::ItemsMarker::PROTOCOL_NAME => service::host(move |stream| {
let vmo_clone = vmo.clone();
run_boot_items(stream, vmo_clone)
}),
ffeedback::CrashReporterMarker::PROTOCOL_NAME => service::host(move |stream| {
run_crash_reporter(stream, crash_reports_sink.clone())
}),
},
};
let scope = ExecutionScope::new();
export.open(
scope.clone(),
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::DIRECTORY,
Path::dot(),
fidl::endpoints::ServerEnd::from(handles.outgoing_dir.into_channel()),
);
scope.wait().await;
Ok(())
}
/// fshost uses exactly one boot item - it checks to see if there is an item of type
/// ZBI_TYPE_STORAGE_RAMDISK. If it's there, it's a vmo that represents a ramdisk version of the
/// fvm, and fshost creates a ramdisk from the vmo so it can go through the normal device matching.
async fn run_boot_items(mut stream: fboot::ItemsRequestStream, vmo: Option<Arc<zx::Vmo>>) {
while let Some(request) = stream.next().await {
match request.unwrap() {
fboot::ItemsRequest::Get { type_, extra, responder } => {
assert_eq!(type_, ZBI_TYPE_STORAGE_RAMDISK);
assert_eq!(extra, 0);
let response_vmo = vmo.as_ref().map(|vmo| {
vmo.create_child(zx::VmoChildOptions::SLICE, 0, vmo.get_size().unwrap())
.unwrap()
});
responder.send(response_vmo, 0).unwrap();
}
fboot::ItemsRequest::Get2 { type_, extra, responder } => {
assert_eq!(type_, ZBI_TYPE_STORAGE_RAMDISK);
assert_eq!((*extra.unwrap()).n, 0);
responder.send(Ok(Vec::new())).unwrap();
}
fboot::ItemsRequest::GetBootloaderFile { .. } => {
panic!(
"unexpectedly called GetBootloaderFile on {}",
fboot::ItemsMarker::PROTOCOL_NAME
);
}
}
}
}
/// fshost expects a set of string and bool arguments to be available. This is a list of all the
/// arguments it looks for. NOTE: For what we are currently testing for, none of these are required,
/// so for now we either return None or the provided default depending on the context.
///
/// String args -
/// blobfs.write-compression-algorithm (optional)
/// blobfs.cache-eviction-policy (optional)
/// zircon.system.pkgfs.file.<path> - used when loading pkgfs and deps from blobfs
/// zircon.system.pkgfs.cmd - used when launching pkgfs
/// factory_verity_seal - only used when writing to the factory partition
/// Bool args -
/// netsvc.netboot (optional; default false)
/// zircon.system.disable-automount (optional; default false)
/// zircon.system.filesystem-check (optional; default false)
async fn run_boot_args(mut stream: fboot::ArgumentsRequestStream, netboot: bool) {
while let Some(request) = stream.next().await {
match request.unwrap() {
fboot::ArgumentsRequest::GetString { key: _, responder } => {
responder.send(None).unwrap();
}
fboot::ArgumentsRequest::GetStrings { keys, responder } => {
responder.send(&vec![None; keys.len()]).unwrap();
}
fboot::ArgumentsRequest::GetBool { key: _, defaultval, responder } => {
responder.send(defaultval).unwrap();
}
fboot::ArgumentsRequest::GetBools { keys, responder } => {
let vec: Vec<_> = keys
.iter()
.map(|bool_pair| {
if bool_pair.key == "netsvc.netboot".to_string() && netboot {
true
} else {
bool_pair.defaultval
}
})
.collect();
responder.send(&vec).unwrap();
}
fboot::ArgumentsRequest::Collect { .. } => {
// This seems to be deprecated. Either way, fshost doesn't use it.
panic!("unexpectedly called Collect on {}", fboot::ArgumentsMarker::PROTOCOL_NAME);
}
}
}
}
async fn run_crash_reporter(
mut stream: ffeedback::CrashReporterRequestStream,
mut crash_reports_sink: mpsc::Sender<ffeedback::CrashReport>,
) {
while let Some(request) = stream.next().await {
match request.unwrap() {
ffeedback::CrashReporterRequest::FileReport { report, responder } => {
crash_reports_sink.send(report).await.unwrap();
responder.send(Ok(&FileReportResults::default())).unwrap();
}
}
}
}