Merge "Statically link more libraries"
diff --git a/apex/Android.bp b/apex/Android.bp
index e0ca9bf..2d6c757 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -15,20 +15,55 @@
"microdroid_vendor_boot",
]
-apex {
- name: "com.android.virt",
+soong_config_module_type {
+ name: "virt_apex",
+ module_type: "apex",
+ config_namespace: "ANDROID",
+ bool_variables: ["avf_enabled"],
+ properties: ["defaults"],
+}
+virt_apex {
+ name: "com.android.virt",
+ soong_config_variables: {
+ avf_enabled: {
+ defaults: ["com.android.virt_avf_enabled"],
+ conditions_default: {
+ defaults: ["com.android.virt_avf_disabled"],
+ },
+ },
+ },
+}
+
+apex_defaults {
+ name: "com.android.virt_common",
// TODO(jiyong): make it updatable
updatable: false,
future_updatable: true,
platform_apis: true,
- system_ext_specific: true,
-
manifest: "manifest.json",
key: "com.android.virt.key",
certificate: ":com.android.virt.certificate",
+
+ apps: [
+ "android.system.virtualmachine.res",
+ ],
+
+ file_contexts: ":com.android.virt-file_contexts",
+ canned_fs_config: "canned_fs_config",
+
+ bootclasspath_fragments: [
+ "com.android.virt-bootclasspath-fragment",
+ ],
+}
+
+apex_defaults {
+ name: "com.android.virt_avf_enabled",
+
+ defaults: ["com.android.virt_common"],
+
custom_sign_tool: "sign_virt_apex",
// crosvm and virtualizationservice are only enabled for 64-bit targets on device
@@ -52,17 +87,12 @@
"fd_server",
"vm",
],
- java_libs: [
- "android.system.virtualmachine",
- ],
jni_libs: [
"libvirtualmachine_jni",
],
- apps: [
- "android.system.virtualmachine.res",
- ],
prebuilts: [
"com.android.virt.init.rc",
+ "features_com.android.virt.xml",
"microdroid_initrd_app_debuggable",
"microdroid_initrd_full_debuggable",
"microdroid_initrd_normal",
@@ -71,13 +101,17 @@
"microdroid_bootloader.avbpubkey",
"microdroid_kernel",
],
- file_contexts: ":com.android.virt-file_contexts",
- canned_fs_config: "canned_fs_config",
host_required: [
"vm_shell",
],
}
+apex_defaults {
+ name: "com.android.virt_avf_disabled",
+
+ defaults: ["com.android.virt_common"],
+}
+
apex_key {
name: "com.android.virt.key",
public_key: "com.android.virt.avbpubkey",
@@ -174,3 +208,43 @@
},
},
}
+
+// Encapsulate the contributions made by the com.android.virt to the bootclasspath.
+bootclasspath_fragment {
+ name: "com.android.virt-bootclasspath-fragment",
+ contents: ["framework-virtualization"],
+ apex_available: ["com.android.virt"],
+
+ // The bootclasspath_fragments that provide APIs on which this depends.
+ fragments: [
+ {
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ },
+ ],
+
+ // Additional stubs libraries that this fragment's contents use which are
+ // not provided by another bootclasspath_fragment.
+ additional_stubs: [
+ "android-non-updatable",
+ ],
+
+ hidden_api: {
+
+ // This module does not contain any split packages.
+ split_packages: [],
+
+ // The following packages and all their subpackages currently only
+ // contain classes from this bootclasspath_fragment. Listing a package
+ // here won't prevent other bootclasspath modules from adding classes in
+ // any of those packages but it will prevent them from adding those
+ // classes into an API surface, e.g. public, system, etc.. Doing so will
+ // result in a build failure due to inconsistent flags.
+ package_prefixes: [
+ "android.system.virtualmachine",
+ "android.system.virtualizationservice",
+ // android.sysprop.*, renamed by jarjar
+ "com.android.system.virtualmachine.sysprop",
+ ],
+ },
+}
diff --git a/apex/permissions/Android.bp b/apex/permissions/Android.bp
new file mode 100644
index 0000000..0c925ce
--- /dev/null
+++ b/apex/permissions/Android.bp
@@ -0,0 +1,24 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+prebuilt_etc {
+ name: "features_com.android.virt.xml",
+ sub_dir: "permissions",
+ src: "features_com.android.virt.xml",
+}
diff --git a/apex/permissions/features_com.android.virt.xml b/apex/permissions/features_com.android.virt.xml
new file mode 100644
index 0000000..d2b32e6
--- /dev/null
+++ b/apex/permissions/features_com.android.virt.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<permissions>
+ <feature name="android.software.virtualization_framework" />
+</permissions>
diff --git a/apex/product_packages.mk b/apex/product_packages.mk
index 5bc0044..4293c80 100644
--- a/apex/product_packages.mk
+++ b/apex/product_packages.mk
@@ -21,7 +21,6 @@
PRODUCT_PACKAGES += \
com.android.compos \
- com.android.virt \
# TODO(b/207336449): Figure out how to get these off /system
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST := \
@@ -33,3 +32,5 @@
PRODUCT_SYSTEM_EXT_PROPERTIES := ro.config.isolated_compilation_enabled=true
PRODUCT_FSVERITY_GENERATE_METADATA := true
+
+PRODUCT_AVF_ENABLED := true
diff --git a/demo/Android.bp b/demo/Android.bp
index 8613166..5241e25 100644
--- a/demo/Android.bp
+++ b/demo/Android.bp
@@ -13,7 +13,7 @@
"com.google.android.material_material",
],
libs: [
- "android.system.virtualmachine",
+ "framework-virtualization",
],
jni_libs: ["MicrodroidTestNativeLib"],
platform_apis: true,
diff --git a/demo/AndroidManifest.xml b/demo/AndroidManifest.xml
index 6669adb..17a7680 100644
--- a/demo/AndroidManifest.xml
+++ b/demo/AndroidManifest.xml
@@ -4,11 +4,11 @@
<uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
<uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33"/>
+ <uses-feature android:name="android.software.virtualization_framework" android:required="true" />
<application
android:label="MicrodroidDemo"
android:theme="@style/Theme.MicrodroidDemo"
android:testOnly="true">
- <uses-library android:name="android.system.virtualmachine" android:required="true" />
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/javalib/Android.bp b/javalib/Android.bp
index cb03fa1..1d89059 100644
--- a/javalib/Android.bp
+++ b/javalib/Android.bp
@@ -11,10 +11,16 @@
}
java_sdk_library {
- name: "android.system.virtualmachine",
- installable: true,
+ name: "framework-virtualization",
+ installable: false,
compile_dex: true,
+ shared_library: false,
+
+ // TODO(b/243512044): use framework-module-defaults
+
+ dist_group: "android",
+
jarjar_rules: "jarjar-rules.txt",
srcs: ["src/**/*.java"],
@@ -25,6 +31,7 @@
],
apex_available: ["com.android.virt"],
+
permitted_packages: [
"android.system.virtualmachine",
"android.system.virtualizationservice",
@@ -38,6 +45,9 @@
"-Xep:GuardedBy:ERROR",
],
},
+
+ // Temporary workaround, will be removed in a follow-up child cl.
+ unsafe_ignore_missing_latest_api: true,
}
prebuilt_apis {
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index d1742b2..6233e0a 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -69,7 +69,6 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -319,13 +318,7 @@
try {
VirtualMachine vm = new VirtualMachine(context, name, config);
-
- try (FileOutputStream output = new FileOutputStream(vm.mConfigFilePath)) {
- config.serialize(output);
- } catch (IOException e) {
- throw new VirtualMachineException("failed to write VM config", e);
- }
-
+ config.serialize(vm.mConfigFilePath);
try {
vm.mInstanceFilePath.createNewFile();
} catch (IOException e) {
@@ -374,13 +367,7 @@
return null;
}
File configFilePath = new File(thisVmDir, CONFIG_FILE);
- VirtualMachineConfig config;
- try (FileInputStream input = new FileInputStream(configFilePath)) {
- config = VirtualMachineConfig.from(input);
- } catch (IOException e) {
- throw new VirtualMachineException("Failed to read config file", e);
- }
-
+ VirtualMachineConfig config = VirtualMachineConfig.from(configFilePath);
Map<String, WeakReference<VirtualMachine>> instancesMap = getInstancesMap(context);
VirtualMachine vm = null;
@@ -846,14 +833,7 @@
throw new VirtualMachineException("incompatible config");
}
checkStopped();
-
- try {
- FileOutputStream output = new FileOutputStream(mConfigFilePath);
- newConfig.serialize(output);
- output.close();
- } catch (IOException e) {
- throw new VirtualMachineException("Failed to persist config", e);
- }
+ newConfig.serialize(mConfigFilePath);
mConfig = newConfig;
return oldConfig;
}
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index b814367..593a57d 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -33,7 +33,9 @@
import android.system.virtualizationservice.VirtualMachinePayloadConfig;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -181,9 +183,19 @@
mNumCpus = numCpus;
}
+ /** Loads a config from a file. */
+ @NonNull
+ static VirtualMachineConfig from(@NonNull File file) throws VirtualMachineException {
+ try (FileInputStream input = new FileInputStream(file)) {
+ return fromInputStream(input);
+ } catch (IOException e) {
+ throw new VirtualMachineException("Failed to read VM config from file", e);
+ }
+ }
+
/** Loads a config from a stream, for example a file. */
@NonNull
- static VirtualMachineConfig from(@NonNull InputStream input)
+ private static VirtualMachineConfig fromInputStream(@NonNull InputStream input)
throws IOException, VirtualMachineException {
PersistableBundle b = PersistableBundle.readFromStream(input);
int version = b.getInt(KEY_VERSION);
@@ -215,8 +227,17 @@
protectedVm, memoryMib, numCpus);
}
+ /** Persists this config to a file. */
+ void serialize(@NonNull File file) throws VirtualMachineException {
+ try (FileOutputStream output = new FileOutputStream(file)) {
+ serializeOutputStream(output);
+ } catch (IOException e) {
+ throw new VirtualMachineException("failed to write VM config", e);
+ }
+ }
+
/** Persists this config to a stream, for example a file. */
- void serialize(@NonNull OutputStream output) throws IOException {
+ private void serializeOutputStream(@NonNull OutputStream output) throws IOException {
PersistableBundle b = new PersistableBundle();
b.putInt(KEY_VERSION, VERSION);
b.putString(KEY_APKPATH, mApkPath);
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 71bac72..77de696 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -8,6 +8,9 @@
defaults: ["vmbase_ffi_defaults"],
srcs: ["src/main.rs"],
edition: "2021",
+ features: [
+ "legacy",
+ ],
rustlibs: [
"libbuddy_system_allocator",
"liblog_rust_nostd",
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index dc2087d..c0ad878 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -19,11 +19,15 @@
use crate::mmio_guard;
use core::arch::asm;
use core::slice;
-use log::{debug, error, LevelFilter};
-use vmbase::{console, logger, main, power::reboot};
+use log::debug;
+use log::error;
+use log::LevelFilter;
+use vmbase::{console, layout, logger, main, power::reboot};
#[derive(Debug, Clone)]
enum RebootReason {
+ /// A malformed BCC was received.
+ InvalidBcc,
/// An unexpected internal error happened.
InternalError,
}
@@ -80,8 +84,17 @@
RebootReason::InternalError
})?;
+ // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
+ // script prevents it from overlapping with other objects.
+ let bcc = as_bcc(unsafe { get_appended_data_slice() }).ok_or_else(|| {
+ error!("Invalid BCC");
+ RebootReason::InvalidBcc
+ })?;
+
// This wrapper allows main() to be blissfully ignorant of platform details.
- crate::main(fdt, payload);
+ crate::main(fdt, payload, bcc);
+
+ // TODO: Overwrite BCC before jumping to payload to avoid leaking our sealing key.
mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
error!("Failed to unshare the UART: {e}");
@@ -147,3 +160,22 @@
);
};
}
+
+unsafe fn get_appended_data_slice() -> &'static mut [u8] {
+ let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
+ // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
+ let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
+
+ slice::from_raw_parts_mut(base as *mut u8, size)
+}
+
+fn as_bcc(data: &mut [u8]) -> Option<&mut [u8]> {
+ const BCC_SIZE: usize = helpers::SIZE_4KB;
+
+ if cfg!(feature = "legacy") {
+ // TODO(b/256148034): return None if BccHandoverParse(bcc) != kDiceResultOk.
+ Some(&mut data[..BCC_SIZE])
+ } else {
+ None
+ }
+}
diff --git a/pvmfw/src/helpers.rs b/pvmfw/src/helpers.rs
index adfc189..59cf9f3 100644
--- a/pvmfw/src/helpers.rs
+++ b/pvmfw/src/helpers.rs
@@ -17,12 +17,25 @@
pub const SIZE_4KB: usize = 4 << 10;
pub const SIZE_2MB: usize = 2 << 20;
-/// Computes the address of the page containing a given address.
-pub const fn page_of(addr: usize, page_size: usize) -> usize {
- addr & !(page_size - 1)
+/// Computes the largest multiple of the provided alignment smaller or equal to the address.
+///
+/// Note: the result is undefined if alignment isn't a power of two.
+pub const fn unchecked_align_down(addr: usize, alignment: usize) -> usize {
+ addr & !(alignment - 1)
+}
+
+/// Safe wrapper around unchecked_align_up() that validates its assumptions and doesn't wrap.
+pub const fn align_up(addr: usize, alignment: usize) -> Option<usize> {
+ if !alignment.is_power_of_two() {
+ None
+ } else if let Some(s) = addr.checked_add(alignment - 1) {
+ Some(unchecked_align_down(s, alignment))
+ } else {
+ None
+ }
}
/// Computes the address of the 4KiB page containing a given address.
pub const fn page_4kb_of(addr: usize) -> usize {
- page_of(addr, SIZE_4KB)
+ unchecked_align_down(addr, SIZE_4KB)
}
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index c0bb263..8178d0b 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -29,7 +29,7 @@
use avb::PUBLIC_KEY;
use log::{debug, info};
-fn main(fdt: &mut [u8], payload: &[u8]) {
+fn main(fdt: &mut [u8], payload: &[u8], bcc: &[u8]) {
info!("pVM firmware");
debug!(
"fdt_address={:#018x}, payload_start={:#018x}, payload_size={:#018x}",
@@ -37,6 +37,7 @@
payload.as_ptr() as usize,
payload.len(),
);
+ debug!("BCC: {:?} ({:#x} bytes)", bcc.as_ptr(), bcc.len());
debug!("AVB public key: addr={:?}, size={:#x} ({1})", PUBLIC_KEY.as_ptr(), PUBLIC_KEY.len());
info!("Starting payload...");
}
diff --git a/tests/benchmark/Android.bp b/tests/benchmark/Android.bp
index a92c4ed..bccea6b 100644
--- a/tests/benchmark/Android.bp
+++ b/tests/benchmark/Android.bp
@@ -16,7 +16,7 @@
"com.android.microdroid.testservice-java",
"truth-prebuilt",
],
- libs: ["android.system.virtualmachine"],
+ libs: ["framework-virtualization"],
jni_libs: [
"MicrodroidBenchmarkNativeLib",
"MicrodroidIdleNativeLib",
diff --git a/tests/benchmark/AndroidManifest.xml b/tests/benchmark/AndroidManifest.xml
index c39b91c..8a7366a 100644
--- a/tests/benchmark/AndroidManifest.xml
+++ b/tests/benchmark/AndroidManifest.xml
@@ -18,7 +18,6 @@
<uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
<uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
<application>
- <uses-library android:name="android.system.virtualmachine" android:required="false" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.microdroid.benchmark"
diff --git a/tests/helper/Android.bp b/tests/helper/Android.bp
index 60d4be1..bd92020 100644
--- a/tests/helper/Android.bp
+++ b/tests/helper/Android.bp
@@ -24,5 +24,5 @@
"VirtualizationTestHelper",
"truth-prebuilt",
],
- libs: ["android.system.virtualmachine"],
+ libs: ["framework-virtualization"],
}
diff --git a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
index 1e57ff8..d1e1f6c 100644
--- a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
+++ b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
@@ -15,9 +15,9 @@
*/
package com.android.microdroid.test.device;
-import static com.google.common.truth.TruthJUnit.assume;
+import static android.content.pm.PackageManager.FEATURE_VIRTUALIZATION_FRAMEWORK;
-import static org.junit.Assume.assumeNoException;
+import static com.google.common.truth.TruthJUnit.assume;
import android.app.Instrumentation;
import android.app.UiAutomation;
@@ -119,17 +119,12 @@
}
public void prepareTestSetup(boolean protectedVm) {
- // In case when the virt APEX doesn't exist on the device, classes in the
- // android.system.virtualmachine package can't be loaded. Therefore, before using the
- // classes, check the existence of a class in the package and skip this test if not exist.
- try {
- Class.forName("android.system.virtualmachine.VirtualMachineManager");
- } catch (ClassNotFoundException e) {
- assumeNoException(e);
- return;
- }
- Context context = ApplicationProvider.getApplicationContext();
- mInner = new Inner(context, protectedVm, VirtualMachineManager.getInstance(context));
+ Context ctx = ApplicationProvider.getApplicationContext();
+ assume().withMessage("Device doesn't support AVF")
+ .that(ctx.getPackageManager().hasSystemFeature(FEATURE_VIRTUALIZATION_FRAMEWORK))
+ .isTrue();
+
+ mInner = new Inner(ctx, protectedVm, VirtualMachineManager.getInstance(ctx));
int capabilities = mInner.getVirtualMachineManager().getCapabilities();
if (protectedVm) {
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 9b05080..8d49721 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -19,7 +19,7 @@
"truth-prebuilt",
"compatibility-common-util-devicesidelib",
],
- libs: ["android.system.virtualmachine"],
+ libs: ["framework-virtualization"],
jni_libs: [
"MicrodroidTestNativeLib",
"MicrodroidIdleNativeLib",
diff --git a/tests/testapk/AndroidManifest.xml b/tests/testapk/AndroidManifest.xml
index ab22546..fefd20a 100644
--- a/tests/testapk/AndroidManifest.xml
+++ b/tests/testapk/AndroidManifest.xml
@@ -18,8 +18,8 @@
<uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
<uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
<uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
+ <uses-feature android:name="android.software.virtualization_framework" android:required="false" />
<application>
- <uses-library android:name="android.system.virtualmachine" android:required="false" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.microdroid.test"