-
Notifications
You must be signed in to change notification settings - Fork 173
Add ARM CCA FVP (Fixed Virtual Platform) support to Flowey #2866
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
weiding-msft
wants to merge
18
commits into
microsoft:main
Choose a base branch
from
weiding-msft:fvp-cca-enablement
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
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 e959ab7
Convert platform and overlay paths to absolution path
wdng 9903f4a
cca-fvp: create separate jobs to ensure ordering
wdng aeee1c5
cca_fvp: fix compilation warning and delete unused file
f9a613a
cca-fvp: set up aarch64 toolchain and build host kernel
wdng 04e36bb
cca-fvp: set up openvmm as paravisor and build openvmm
wdng 486c740
cca-fvp: enable local_shrinkwrap_run.rs to boot the host
wdng 4fa88d4
cca-fvp: auto-clone planes.yaml to remove manual setup
wdng 4133fd5
cca-fvp: refactor to reuse common code via helper function
weiding-msft aea2bed
cca-fvp: refactor linux kernel and rust binary build logic
weiding-msft b8ddd65
cca-fvp: refactor kernel config logic
weiding-msft d763a68
cca-fvp: use configurable rootfs path for cca-fvp tests
weiding-msft 80367a8
cca-fvp: normalize platform and overlay config path handling
weiding-msft ddf123f
cca-fvp: fix CCA kernel config enable failure
weiding-msft 83e8a24
cca-fvp: fix config resolution and output directory location
weiding-msft 47b49e9
cca-fvp: add sensible defaults for CLI options
weiding-msft c9ddbb3
cca-fvp fix: make unmount more robust when device is busy
weiding-msft 1df2e0e
cca-fvp: update local_install_shrinkwrap for RustRuntimeServices inte…
weiding-msft File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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, | ||
|
|
||
| /// 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( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add a |
||
| 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) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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-depswhich 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.