Skip to content

Commit dcbde90

Browse files
SashaMITcursoragent
andcommitted
feat(vz-backend): Phase 1 — scaffold elastos-vz crate (no Vz calls yet)
Per docs/vz-backend/PLAN.md Phase 1. Adds a new compute-provider substrate for macOS that mirrors elastos-crosvm's public surface and fails closed at every entry point. Phase 2 wires the actual Apple Virtualization.framework calls. New crate: elastos/crates/elastos-vz - VzProvider implements ComputeProvider; every method returns PHASE_1_STUB_MESSAGE ("vz backend not yet implemented (Phase 2)"). - VzConfig + VmConfig mirror crosvm shapes minus pivot_root_dir. Boot-args helper rewrites `console=ttyS0` → `console=hvc0` per Phase 0 finding (Vz exposes the kernel console only as virtio-console). - NetworkConfig (macOS) + network_stub.rs (non-macOS) mirror the crosvm pattern; setup() fails closed in Phase 1. - RunningVm placeholder; start() fails closed, stop() is an idempotent no-op (matches crosvm semantics on already-stopped VMs). - src/ffi/ placeholder documents the Phase 2 fill-in (per-class wrappers for VZLinuxBootLoader, VZVirtioBlockDeviceConfiguration, VZVirtioSocketDeviceConfiguration, etc.). - objc2-virtualization 0.3.2 declared as a target-conditional dep (cfg(target_os = "macos") only). Not yet `use`d in any function body. - 27 unit tests + 10 smoke tests; cargo clippy clean. Wired into elastos-server: - elastos-vz added as a platform-conditional dep (cfg(target_os = "macos") only). Linux build is byte-equivalent. - main.rs::create_runtime gets a SIBLING #[cfg(target_os = "macos")] registration block after the crosvm block. The crosvm block at L1862–L1876 is not touched. - supervisor.rs::launch_capsule cfg-gates the bail at L931: • Linux: identical behavior to pre-Vz commit. • macOS: fails closed with PHASE_1_STUB_MESSAGE so capsule launches surface the Phase status with a clear "Phase 2" marker rather than the (misleading) /dev/kvm message. • Other OS: bails with "no microVM substrate available". Linux-untouched gate: - scripts/check-linux-untouched.sh diffs the four protected crates (elastos-crosvm, elastos-runtime, elastos-common, elastos-compute) against a base ref and fails non-zero on any modification. Verified clean against Phase 0 baseline (a65dad3). Phase 1 prelude tool: - scripts/dev/mac-vz-feature-check/ — placeholder for the desk-research-confirming feature probe described in docs/vz-backend/PHASE_0_SCOPE.md appendix A. To be filled in Phase 1 Day 2 before Phase 2 begins. Verification gates passed: - cargo build -p elastos-vz (macOS) ✅ - cargo build --workspace (macOS) ✅ - cargo test -p elastos-vz (37 tests) ✅ - cargo clippy -p elastos-vz --all-targets (-D warnings) ✅ - cargo clippy -p elastos-server ✅ - runtime start logs: "vz provider enabled (Apple Virtualization.framework available; Phase 1 stub — microVM launch fails closed)" - protected crates touched: 0 Three pre-existing setup.rs test failures (darwin-arm64 component lookup) are inherited from Pre-Work commit 9fc4a7e and are not introduced or worsened by this commit. Linux behavior: byte-equivalent. Mac behavior: registration scaffolded, launch path fail-closed with Phase 2 marker. No Vz API calls. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent a65dad3 commit dcbde90

17 files changed

Lines changed: 1957 additions & 1 deletion

File tree

elastos/Cargo.lock

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

elastos/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ members = [
44
"crates/elastos-storage",
55
"crates/elastos-compute",
66
"crates/elastos-crosvm",
7+
"crates/elastos-vz",
78
"crates/elastos-tls",
89
"crates/elastos-identity",
910
"crates/elastos-namespace",

elastos/crates/elastos-server/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ futures-lite = "2.6"
9797
getrandom = "0.2"
9898
data-encoding = "2.6"
9999

100+
# Apple Virtualization.framework substrate — only compiled into the
101+
# server on macOS hosts. On Linux/Windows the server keeps its
102+
# Linux/crosvm-only behaviour byte-for-byte. Anchored in
103+
# docs/vz-backend/PLAN.md (Linux-untouched gate).
104+
[target.'cfg(target_os = "macos")'.dependencies]
105+
elastos-vz = { path = "../elastos-vz" }
106+
100107
[dev-dependencies]
101108
tokio = { version = "1.0", features = ["full", "test-util"] }
102109
tempfile = "3.10"

elastos/crates/elastos-server/src/main.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,34 @@ pub(crate) async fn create_runtime(
18751875
}
18761876
}
18771877

1878+
// Add Apple Virtualization.framework provider on macOS. This is a
1879+
// SIBLING block to the crosvm registration above — it never
1880+
// executes on Linux (compiled out by `cfg(target_os = "macos")`)
1881+
// and the crosvm block above is byte-identical to the
1882+
// pre-Vz-backend commit. Per `docs/vz-backend/PLAN.md` Phase 1
1883+
// ("Linux-untouched gate"). Phase 1 wires the provider as a stub
1884+
// — every `load`/`start` call fails closed with the message in
1885+
// `elastos_vz::PHASE_1_STUB_MESSAGE`.
1886+
#[cfg(target_os = "macos")]
1887+
if elastos_vz::is_supported() {
1888+
match elastos_vz::VzProvider::new(elastos_vz::VzConfig::default()) {
1889+
Ok(provider) => {
1890+
if let Err(e) = provider.init().await {
1891+
tracing::warn!("Failed to initialize vz provider: {}", e);
1892+
} else {
1893+
tracing::info!(
1894+
"vz provider enabled (Apple Virtualization.framework available; \
1895+
Phase 1 stub — microVM launch fails closed)"
1896+
);
1897+
compute_providers.push(Arc::new(provider));
1898+
}
1899+
}
1900+
Err(e) => {
1901+
tracing::warn!("vz provider not available: {}", e);
1902+
}
1903+
}
1904+
}
1905+
18781906
Ok(Runtime::with_providers(
18791907
storage,
18801908
compute_providers,

elastos/crates/elastos-server/src/supervisor.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,19 @@ impl Supervisor {
911911
/// `config` is an opaque JSON payload from the CLI command. For the shell
912912
/// capsule, this contains the forwarded command (e.g. `{"command":"chat",...}`).
913913
/// It is base64-encoded and passed via the `elastos.command` kernel boot arg.
914+
///
915+
/// **Linux:** the crosvm path below is the only substrate; behavior is
916+
/// byte-identical to the pre-Vz-backend commit. **macOS:** the launch
917+
/// path short-circuits at the substrate-check site below with a Phase 1
918+
/// `vz backend not yet implemented` bail. Phase 2/3 (see
919+
/// `docs/vz-backend/PLAN.md`) replace that bail with a real
920+
/// `VzProvider.load(...)` route, at which point the rest of this
921+
/// function body remains the Linux/crosvm-only code path.
922+
///
923+
/// The `cfg_attr` below silences the expected `unreachable_code` warning
924+
/// on macOS, where the Phase 1 bail makes the rest of the function dead
925+
/// code. The warning is informational on Mac and a true error on Linux.
926+
#[cfg_attr(not(target_os = "linux"), allow(unreachable_code, unused_variables))]
914927
async fn launch_capsule(&self, name: &str, config: serde_json::Value) -> Result<(String, u32)> {
915928
let (capsule_dir, manifest) = self.load_capsule_manifest(name).await?;
916929

@@ -927,10 +940,42 @@ impl Supervisor {
927940
.await;
928941
}
929942

930-
// VM path — hard require KVM
943+
// VM path — hard require a microVM substrate.
944+
//
945+
// Linux: behavior is byte-identical to the pre-Vz-backend commit;
946+
// crosvm is the only substrate, and `/dev/kvm` must be present.
947+
//
948+
// macOS: the Vz substrate is registered in main.rs but the
949+
// per-VM launch path is not yet routed through it. Phase 1
950+
// delivers scaffold only; Phase 2 wires `VzProvider.load(...)`
951+
// and Phase 3 routes the supervisor through this site. Until
952+
// then, fail closed with the same single-source-of-truth
953+
// message used by VzProvider's stubs. See
954+
// `docs/vz-backend/PLAN.md`.
955+
//
956+
// Other OS: no microVM substrate is available at all.
957+
#[cfg(target_os = "linux")]
931958
if !elastos_crosvm::is_supported() {
932959
bail!("/dev/kvm not available — crosvm requires KVM. Cannot launch capsule '{name}'.");
933960
}
961+
962+
#[cfg(target_os = "macos")]
963+
{
964+
if !elastos_vz::is_supported() {
965+
bail!(
966+
"Apple Virtualization.framework not available — cannot launch capsule '{name}' on this host. Requires macOS 12+ on Apple Silicon."
967+
);
968+
}
969+
bail!(
970+
"{} (supervisor: launch capsule '{}' on macOS not yet routed through Vz)",
971+
elastos_vz::PHASE_1_STUB_MESSAGE,
972+
name
973+
);
974+
}
975+
976+
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
977+
bail!("no microVM substrate available on this OS — cannot launch capsule '{name}'");
978+
934979
self.crosvm_config.validate().map_err(|e| {
935980
anyhow::anyhow!(
936981
"VM prerequisites missing: {}. Run `elastos setup --with crosvm --with vmlinux` \
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[package]
2+
name = "elastos-vz"
3+
version.workspace = true
4+
edition.workspace = true
5+
license.workspace = true
6+
description = "ElastOS Apple Virtualization.framework Compute Provider — Run capsules in real microVMs on macOS (Phase 1: scaffold; no Vz API calls yet)"
7+
8+
[dependencies]
9+
elastos-common = { path = "../elastos-common" }
10+
elastos-compute = { path = "../elastos-compute" }
11+
12+
tokio = { version = "1.0", features = ["full", "process"] }
13+
serde = { version = "1.0", features = ["derive"] }
14+
serde_json = "1.0"
15+
uuid = { version = "1.0", features = ["v4"] }
16+
async-trait = "0.1"
17+
tracing = "0.1"
18+
thiserror = "1.0"
19+
libc = "0.2"
20+
21+
# Apple Virtualization.framework bindings — fetched and linked ONLY on macOS.
22+
# On Linux the crate compiles as a stub (`is_supported()` returns false,
23+
# every entry point fails closed) so the workspace build stays green and
24+
# nothing is silently downgraded. The choice is anchored in
25+
# docs/vz-backend/PHASE_0_SCOPE.md §A. Phase 1 declares the dependency but
26+
# does not `use` it in any function body — actual Vz calls land in Phase 2.
27+
[target.'cfg(target_os = "macos")'.dependencies]
28+
objc2 = "0.6"
29+
objc2-virtualization = "0.3"
30+
objc2-foundation = "0.3"
31+
block2 = "0.6"
32+
dispatch2 = "0.3"
33+
34+
[dev-dependencies]
35+
tokio = { version = "1.0", features = ["full", "test-util"] }
36+
tempfile = "3.10"

0 commit comments

Comments
 (0)