Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
469e9eb
flowey: add cca-fvp-test option to install and build shrinkwrap
weiding-msft Jan 16, 2026
e959ab7
Convert platform and overlay paths to absolution path
wdng Jan 16, 2026
9903f4a
cca-fvp: create separate jobs to ensure ordering
wdng Jan 26, 2026
aeee1c5
cca_fvp: fix compilation warning and delete unused file
Jan 28, 2026
f9a613a
cca-fvp: set up aarch64 toolchain and build host kernel
wdng Jan 28, 2026
04e36bb
cca-fvp: set up openvmm as paravisor and build openvmm
wdng Jan 29, 2026
486c740
cca-fvp: enable local_shrinkwrap_run.rs to boot the host
wdng Jan 30, 2026
4fa88d4
cca-fvp: auto-clone planes.yaml to remove manual setup
wdng Feb 2, 2026
4133fd5
cca-fvp: refactor to reuse common code via helper function
weiding-msft Feb 3, 2026
aea2bed
cca-fvp: refactor linux kernel and rust binary build logic
weiding-msft Feb 3, 2026
b8ddd65
cca-fvp: refactor kernel config logic
weiding-msft Feb 3, 2026
d763a68
cca-fvp: use configurable rootfs path for cca-fvp tests
weiding-msft Feb 4, 2026
80367a8
cca-fvp: normalize platform and overlay config path handling
weiding-msft Feb 5, 2026
ddf123f
cca-fvp: fix CCA kernel config enable failure
weiding-msft Feb 6, 2026
83e8a24
cca-fvp: fix config resolution and output directory location
weiding-msft Feb 6, 2026
47b49e9
cca-fvp: add sensible defaults for CLI options
weiding-msft Feb 10, 2026
c9ddbb3
cca-fvp fix: make unmount more robust when device is busy
weiding-msft Feb 10, 2026
1df2e0e
cca-fvp: update local_install_shrinkwrap for RustRuntimeServices inte…
weiding-msft Feb 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 263 additions & 0 deletions flowey/flowey_hvlite/src/pipelines/cca_fvp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use flowey::node::prelude::ReadVar;
use flowey::pipeline::prelude::*;
use std::path::PathBuf;

/// Install Shrinkwrap, Build + run CCA FVP via Shrinkwrap (local)
#[derive(clap::Args)]
pub struct CcaFvpCli {
/// Directory for output artifacts/logs (pipeline working dir)
#[clap(long, default_value = "target/cca-fvp")]
pub dir: PathBuf,

/// Platform YAML (e.g. cca-3world.yaml). If not specified, defaults to cca-3world.yaml
#[clap(long, default_value = "cca-3world.yaml")]
pub platform: PathBuf,

/// Overlay YAMLs (repeatable), e.g. --overlay buildroot.yaml --overlay planes.yaml
/// If not specified, defaults to buildroot.yaml and planes.yaml
#[clap(long)]
pub overlay: Vec<PathBuf>,

/// Build-time variables (repeatable), e.g. --btvar 'GUEST_ROOTFS=${artifact:BUILDROOT}'
/// If not specified, defaults to GUEST_ROOTFS=${artifact:BUILDROOT}
#[clap(long)]
pub btvar: Vec<String>,

/// Rootfs path to pass at runtime, e.g.
/// --rootfs /abs/path/.shrinkwrap/package/cca-3world/rootfs.ext2
/// Default to ${SHRINKWRAP_PACKAGE:-$HOME/.shrinkwrap/package}/cca-3world/rootfs.ext2
#[clap(long)]
pub rootfs: Option<PathBuf>,

/// Additional runtime variables (repeatable), besides ROOTFS, e.g. --rtvar FOO=bar
#[clap(long)]
pub rtvar: Vec<String>,

/// Automatically install missing deps (requires sudo on Ubuntu)
#[clap(long, default_value_t = true)]
pub install_missing_deps: bool,

/// If repo already exists, attempt `git pull --ff-only`
#[clap(long, default_value_t = true)]
pub update_shrinkwrap_repo: bool,
Comment on lines +40 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's actually a way to turn these off if you default them to true (I think usage would be --install-missing-deps which is already true so this would be a no-op). We should have these default to false or flip the flag to --no-install-missing-deps.


/// Verbose pipeline output
#[clap(long)]
pub verbose: bool,
}

impl IntoPipeline for CcaFvpCli {
fn into_pipeline(self, backend_hint: PipelineBackendHint) -> anyhow::Result<Pipeline> {
let Self {
dir,
platform,
overlay,
btvar,
rootfs,
rtvar,
install_missing_deps,
update_shrinkwrap_repo,
verbose,
} = self;

let openvmm_repo = flowey_lib_common::git_checkout::RepoSource::ExistingClone(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a bail_if_running_in_ci() guard here (assuming this isn't supposed to be run as part of CI)

ReadVar::from_static(crate::repo_root()),
);

let mut pipeline = Pipeline::new();

// Store the original dir value for validation before canonicalization
let original_dir = dir.clone();

// Convert dir to absolute path to ensure consistency across jobs
// Relative paths are resolved from the repository root
let dir = std::fs::canonicalize(&dir)
.or_else(|_| {
// If dir doesn't exist yet, make it absolute relative to repo root
let abs = if dir.is_absolute() {
dir.clone()
} else {
crate::repo_root().join(&dir)
};
Ok::<_, anyhow::Error>(abs)
})?;

// Put Shrinkwrap repo under the pipeline working dir, so it's self-contained.
let shrinkwrap_dir = dir.join("shrinkwrap");
let shrinkwrap_config_dir = shrinkwrap_dir.join("config");

// Helper to resolve platform/overlay paths:
// - Absolute paths: use as-is
// - Simple filenames (no '/'): resolve to <dir>/shrinkwrap/config/
// - Relative paths with '/': must start with --dir prefix
let resolve_config_path = |p: PathBuf, arg_name: &str| -> anyhow::Result<PathBuf> {
if p.is_absolute() {
Ok(p)
} else {
let p_str = p.to_string_lossy();

// Check if it's a simple filename (no directory separators)
if !p_str.contains('/') {
// Simple filename: resolve to shrinkwrap/config/
return Ok(shrinkwrap_config_dir.join(p));
}

// It's a relative path with directories - validate it starts with --dir
let original_dir_str = original_dir.to_string_lossy();
let dir_prefix = original_dir_str.trim_start_matches("./");
let alt_dir_prefix = format!("./{}", dir_prefix);

if p_str.starts_with(dir_prefix) || p_str.starts_with(&alt_dir_prefix) {
// Valid: path starts with --dir prefix
// Strip the prefix and reconstruct using the canonical dir
let stripped = p_str.strip_prefix(dir_prefix)
.or_else(|| p_str.strip_prefix(alt_dir_prefix.as_str()))
.unwrap()
.trim_start_matches('/');

Ok(dir.join(stripped))
} else {
// Invalid: relative path doesn't start with --dir
anyhow::bail!(
"Relative path for {} must start with the --dir value ({}). Got: {}. \
Either use an absolute path, a simple filename, or a relative path starting with '{}/'.",
arg_name, original_dir.display(), p.display(), original_dir_str
)
}
}
};

// Apply defaults for options not provided by the user
let overlay = if overlay.is_empty() {
vec![PathBuf::from("buildroot.yaml"), PathBuf::from("planes.yaml")]
} else {
overlay
};

let btvar = if btvar.is_empty() {
vec!["GUEST_ROOTFS=${artifact:BUILDROOT}".to_string()]
} else {
btvar
};

let rootfs = rootfs.unwrap_or_else(|| {
// First try SHRINKWRAP_PACKAGE env var, then HOME env var
let base_path = std::env::var("SHRINKWRAP_PACKAGE")
.or_else(|_| std::env::var("HOME").map(|h| format!("{}/.shrinkwrap/package", h)))
.expect("Either SHRINKWRAP_PACKAGE or HOME environment variable must be set");
PathBuf::from(format!("{}/cca-3world/rootfs.ext2", base_path))
});

// Resolve platform YAML path
let platform = resolve_config_path(platform, "--platform")?;

// Resolve overlay YAML paths
let overlay: Vec<PathBuf> = overlay.into_iter()
.map(|p| resolve_config_path(p, "--overlay"))
.collect::<anyhow::Result<Vec<_>>>()?;

// Create separate jobs to ensure proper ordering
let install_job = pipeline
.new_job(
FlowPlatform::host(backend_hint),
FlowArch::host(backend_hint),
"cca-fvp: install shrinkwrap",
)
.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_versions::Request::Init)
.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_hvlite_reposource::Params {
hvlite_repo_source: openvmm_repo.clone(),
})
.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_common::Params {
local_only: Some(flowey_lib_hvlite::_jobs::cfg_common::LocalOnlyParams {
interactive: true,
auto_install: install_missing_deps,
force_nuget_mono: false,
external_nuget_auth: false,
ignore_rust_version: true,
}),
verbose: ReadVar::from_static(verbose),
locked: false,
deny_warnings: false,
})
.dep_on(|ctx| flowey_lib_hvlite::_jobs::local_install_shrinkwrap::Params {
shrinkwrap_dir: shrinkwrap_dir.clone(),
do_installs: install_missing_deps,
update_repo: update_shrinkwrap_repo,
done: ctx.new_done_handle(),
})
.finish();

let build_job = pipeline
.new_job(
FlowPlatform::host(backend_hint),
FlowArch::host(backend_hint),
"cca-fvp: shrinkwrap build",
)
.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_versions::Request::Init)
.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_hvlite_reposource::Params {
hvlite_repo_source: openvmm_repo.clone(),
})
.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_common::Params {
local_only: Some(flowey_lib_hvlite::_jobs::cfg_common::LocalOnlyParams {
interactive: true,
auto_install: install_missing_deps,
force_nuget_mono: false,
external_nuget_auth: false,
ignore_rust_version: true,
}),
verbose: ReadVar::from_static(verbose),
locked: false,
deny_warnings: false,
})
.dep_on(|ctx| flowey_lib_hvlite::_jobs::local_shrinkwrap_build::Params {
out_dir: dir.clone(),
shrinkwrap_dir: shrinkwrap_dir.clone(),
platform_yaml: platform.clone(),
overlays: overlay.clone(),
btvars: btvar.clone(),
done: ctx.new_done_handle(),
})
.finish();

// Shrinkwrap run job
let run_job = pipeline
.new_job(
FlowPlatform::host(backend_hint),
FlowArch::host(backend_hint),
"cca-fvp: shrinkwrap run",
)
.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_versions::Request::Init)
.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_hvlite_reposource::Params {
hvlite_repo_source: openvmm_repo.clone(),
})
.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_common::Params {
local_only: Some(flowey_lib_hvlite::_jobs::cfg_common::LocalOnlyParams {
interactive: true,
auto_install: install_missing_deps,
force_nuget_mono: false,
external_nuget_auth: false,
ignore_rust_version: true,
}),
verbose: ReadVar::from_static(verbose),
locked: false,
deny_warnings: false,
})
.dep_on(|ctx| flowey_lib_hvlite::_jobs::local_shrinkwrap_run::Params {
out_dir: dir.clone(),
shrinkwrap_dir: shrinkwrap_dir.clone(),
platform_yaml: platform.clone(),
rootfs_path: rootfs.clone(),
rtvars: rtvar.clone(),
done: ctx.new_done_handle(),
})
.finish();

// Explicitly declare job dependencies
pipeline.non_artifact_dep(&build_job, &install_job);
pipeline.non_artifact_dep(&run_job, &build_job);
Ok(pipeline)
}
}
6 changes: 6 additions & 0 deletions flowey/flowey_hvlite/src/pipelines/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
use flowey::pipeline::prelude::*;
use restore_packages::RestorePackagesCli;
use vmm_tests::VmmTestsCli;
use cca_fvp::CcaFvpCli;

pub mod build_docs;
pub mod build_igvm;
pub mod checkin_gates;
pub mod custom_vmfirmwareigvm_dll;
pub mod restore_packages;
pub mod vmm_tests;
pub mod cca_fvp;

#[derive(clap::Subcommand)]
#[expect(clippy::large_enum_variant)]
Expand All @@ -34,6 +36,9 @@ pub enum OpenvmmPipelines {

/// Build and run VMM tests
VmmTests(VmmTestsCli),

/// Build and run CCA FVP via Shrinkwrap
CcaFvp(CcaFvpCli),
}

#[derive(clap::Subcommand)]
Expand Down Expand Up @@ -61,6 +66,7 @@ impl IntoPipeline for OpenvmmPipelines {
},
OpenvmmPipelines::RestorePackages(cmd) => cmd.into_pipeline(pipeline_hint),
OpenvmmPipelines::VmmTests(cmd) => cmd.into_pipeline(pipeline_hint),
OpenvmmPipelines::CcaFvp(cmd) => cmd.into_pipeline(pipeline_hint),
}
}
}
Loading