Merge "[dice] Replace DiceContext with OwnedDiceArtifacts in microdroid"
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index f6811cb..59fdd9f 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -22,6 +22,7 @@
COMPOS_APEX_ROOT, COMPOS_DATA_ROOT, COMPOS_VSOCK_PORT,
};
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+ CpuTopology::CpuTopology,
IVirtualizationService::IVirtualizationService,
VirtualMachineAppConfig::{DebugLevel::DebugLevel, Payload::Payload, VirtualMachineAppConfig},
VirtualMachineConfig::VirtualMachineConfig,
@@ -32,20 +33,29 @@
use log::{info, warn};
use rustutils::system_properties;
use std::fs::{self, File};
-use std::num::NonZeroU32;
use std::path::{Path, PathBuf};
use vmclient::{DeathReason, ErrorCode, VmInstance, VmWaitError};
/// This owns an instance of the CompOS VM.
pub struct ComposClient(VmInstance);
+/// CPU topology configuration for a virtual machine.
+#[derive(Default, Debug, Clone)]
+pub enum VmCpuTopology {
+ /// Run VM with 1 vCPU only.
+ #[default]
+ OneCpu,
+ /// Run VM vCPU topology matching that of the host.
+ MatchHost,
+}
+
/// Parameters to be used when creating a virtual machine instance.
#[derive(Default, Debug, Clone)]
pub struct VmParameters {
/// Whether the VM should be debuggable.
pub debug_mode: bool,
- /// Number of vCPUs to have in the VM. If None, defaults to 1.
- pub cpus: Option<NonZeroU32>,
+ /// CPU topology of the VM. Defaults to 1 vCPU.
+ pub cpu_topology: VmCpuTopology,
/// List of task profiles to apply to the VM
pub task_profiles: Vec<String>,
/// If present, overrides the amount of RAM to give the VM
@@ -111,6 +121,11 @@
(Some(console_fd), Some(log_fd))
};
+ let cpu_topology = match parameters.cpu_topology {
+ VmCpuTopology::OneCpu => CpuTopology::ONE_CPU,
+ VmCpuTopology::MatchHost => CpuTopology::MATCH_HOST,
+ };
+
let config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
name: String::from("Compos"),
apk: Some(apk_fd),
@@ -122,7 +137,7 @@
extraIdsigs: extra_idsigs,
protectedVm: protected_vm,
memoryMib: parameters.memory_mib.unwrap_or(0), // 0 means use the default
- numCpus: parameters.cpus.map_or(1, NonZeroU32::get) as i32,
+ cpuTopology: cpu_topology,
taskProfiles: parameters.task_profiles.clone(),
});
diff --git a/compos/composd/Android.bp b/compos/composd/Android.bp
index 07a9be3..cee4b01 100644
--- a/compos/composd/Android.bp
+++ b/compos/composd/Android.bp
@@ -17,7 +17,6 @@
"libcompos_common",
"libcomposd_native_rust",
"libminijail_rust",
- "libnum_cpus",
"libnix",
"liblibc",
"liblog_rust",
diff --git a/compos/composd/src/instance_manager.rs b/compos/composd/src/instance_manager.rs
index 0a6c3d6..2db13c7 100644
--- a/compos/composd/src/instance_manager.rs
+++ b/compos/composd/src/instance_manager.rs
@@ -21,9 +21,8 @@
use android_system_virtualizationservice::aidl::android::system::virtualizationservice;
use anyhow::{bail, Result};
use binder::Strong;
-use compos_common::compos_client::VmParameters;
+use compos_common::compos_client::{VmCpuTopology, VmParameters};
use compos_common::{CURRENT_INSTANCE_DIR, TEST_INSTANCE_DIR};
-use std::num::NonZeroU32;
use std::sync::{Arc, Mutex, Weak};
use virtualizationservice::IVirtualizationService::IVirtualizationService;
@@ -80,9 +79,14 @@
// By default, dex2oat starts as many threads as there are CPUs. This can be overridden with
// a system property. Start the VM with all CPUs and assume the guest will start a suitable
// number of dex2oat threads.
- let cpus = NonZeroU32::new(num_cpus::get() as u32);
+ let cpu_topology = VmCpuTopology::MatchHost;
let task_profiles = vec!["SCHED_SP_COMPUTE".to_string()];
- Ok(VmParameters { cpus, task_profiles, memory_mib: Some(VM_MEMORY_MIB), ..Default::default() })
+ Ok(VmParameters {
+ cpu_topology,
+ task_profiles,
+ memory_mib: Some(VM_MEMORY_MIB),
+ ..Default::default()
+ })
}
// Ensures we only run one instance at a time.
diff --git a/compos/verify/verify.rs b/compos/verify/verify.rs
index 528719f..13e9292 100644
--- a/compos/verify/verify.rs
+++ b/compos/verify/verify.rs
@@ -21,7 +21,7 @@
use anyhow::{bail, Context, Result};
use binder::ProcessState;
use clap::{Parser, ValueEnum};
-use compos_common::compos_client::{ComposClient, VmParameters};
+use compos_common::compos_client::{ComposClient, VmCpuTopology, VmParameters};
use compos_common::odrefresh::{
CURRENT_ARTIFACTS_SUBDIR, ODREFRESH_OUTPUT_ROOT_DIR, PENDING_ARTIFACTS_SUBDIR,
TEST_ARTIFACTS_SUBDIR,
@@ -33,7 +33,6 @@
use log::error;
use std::fs::File;
use std::io::Read;
-use std::num::NonZeroU32;
use std::panic;
use std::path::Path;
@@ -116,7 +115,7 @@
&idsig_manifest_apk,
&idsig_manifest_ext_apk,
&VmParameters {
- cpus: Some(NonZeroU32::new(1).unwrap()), // This VM runs very little work at boot
+ cpu_topology: VmCpuTopology::OneCpu, // This VM runs very little work at boot
debug_mode: args.debug,
..Default::default()
},
diff --git a/encryptedstore/src/main.rs b/encryptedstore/src/main.rs
index 96c80db..7a41f13 100644
--- a/encryptedstore/src/main.rs
+++ b/encryptedstore/src/main.rs
@@ -95,6 +95,8 @@
.data_device(data_device, dev_size)
.cipher(CipherType::AES256HCTR2)
.key(&key)
+ .opt_param("sector_size:4096")
+ .opt_param("iv_large_sectors")
.build()
.context("Couldn't build the DMCrypt target")?;
let dm = dm::DeviceMapper::new()?;
@@ -125,6 +127,7 @@
let mkfs_options = [
"-j", // Create appropriate sized journal
"-O metadata_csum", // Metadata checksum for filesystem integrity
+ "-b 4096", // block size in the filesystem
];
let mut cmd = Command::new(MK2FS_BIN);
let status = cmd
diff --git a/javalib/api/system-current.txt b/javalib/api/system-current.txt
index b455c85..1ba479f 100644
--- a/javalib/api/system-current.txt
+++ b/javalib/api/system-current.txt
@@ -57,15 +57,17 @@
public final class VirtualMachineConfig {
method @Nullable public String getApkPath();
- method @NonNull public int getDebugLevel();
+ method public int getCpuTopology();
+ method public int getDebugLevel();
method @IntRange(from=0) public long getEncryptedStorageBytes();
method @IntRange(from=0) public long getMemoryBytes();
- method @IntRange(from=1) public int getNumCpus();
method @Nullable public String getPayloadBinaryName();
method public boolean isCompatibleWith(@NonNull android.system.virtualmachine.VirtualMachineConfig);
method public boolean isEncryptedStorageEnabled();
method public boolean isProtectedVm();
method public boolean isVmOutputCaptured();
+ field public static final int CPU_TOPOLOGY_MATCH_HOST = 1; // 0x1
+ field public static final int CPU_TOPOLOGY_ONE_CPU = 0; // 0x0
field public static final int DEBUG_LEVEL_FULL = 1; // 0x1
field public static final int DEBUG_LEVEL_NONE = 0; // 0x0
}
@@ -74,10 +76,10 @@
ctor public VirtualMachineConfig.Builder(@NonNull android.content.Context);
method @NonNull public android.system.virtualmachine.VirtualMachineConfig build();
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setApkPath(@NonNull String);
+ method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setCpuTopology(int);
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setDebugLevel(int);
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setEncryptedStorageBytes(@IntRange(from=1) long);
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setMemoryBytes(@IntRange(from=1) long);
- method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setNumCpus(@IntRange(from=1) int);
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setPayloadBinaryName(@NonNull String);
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setProtectedVm(boolean);
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setVmOutputCaptured(boolean);
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index cb9bad0..5842d01 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -60,7 +60,7 @@
private static final String[] EMPTY_STRING_ARRAY = {};
// These define the schema of the config file persisted on disk.
- private static final int VERSION = 5;
+ private static final int VERSION = 6;
private static final String KEY_VERSION = "version";
private static final String KEY_PACKAGENAME = "packageName";
private static final String KEY_APKPATH = "apkPath";
@@ -70,6 +70,7 @@
private static final String KEY_PROTECTED_VM = "protectedVm";
private static final String KEY_MEMORY_BYTES = "memoryBytes";
private static final String KEY_NUM_CPUS = "numCpus";
+ private static final String KEY_CPU_TOPOLOGY = "cpuTopology";
private static final String KEY_ENCRYPTED_STORAGE_BYTES = "encryptedStorageBytes";
private static final String KEY_VM_OUTPUT_CAPTURED = "vmOutputCaptured";
@@ -97,6 +98,33 @@
*/
@SystemApi public static final int DEBUG_LEVEL_FULL = 1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = "CPU_TOPOLOGY_",
+ value = {
+ CPU_TOPOLOGY_ONE_CPU,
+ CPU_TOPOLOGY_MATCH_HOST,
+ })
+ public @interface CpuTopology {}
+
+ /**
+ * Run VM with 1 vCPU. This is the default option, usually the fastest to boot and consuming the
+ * least amount of resources. Typically the best option for small or ephemeral workloads.
+ *
+ * @hide
+ */
+ @SystemApi public static final int CPU_TOPOLOGY_ONE_CPU = 0;
+
+ /**
+ * Run VM with vCPU topology matching the physical CPU topology of the host. Usually takes
+ * longer to boot and cosumes more resources compared to a single vCPU. Typically a good option
+ * for long-running workloads that benefit from parallel execution.
+ *
+ * @hide
+ */
+ @SystemApi public static final int CPU_TOPOLOGY_MATCH_HOST = 1;
+
/** Name of a package whose primary APK contains the VM payload. */
@Nullable private final String mPackageName;
@@ -116,10 +144,8 @@
*/
private final long mMemoryBytes;
- /**
- * Number of vCPUs in the VM. Defaults to 1 when not specified.
- */
- private final int mNumCpus;
+ /** CPU topology configuration of the VM. */
+ @CpuTopology private final int mCpuTopology;
/**
* Path within the APK to the payload config file that defines software aspects of the VM.
@@ -143,7 +169,7 @@
@DebugLevel int debugLevel,
boolean protectedVm,
long memoryBytes,
- int numCpus,
+ @CpuTopology int cpuTopology,
long encryptedStorageBytes,
boolean vmOutputCaptured) {
// This is only called from Builder.build(); the builder handles parameter validation.
@@ -154,7 +180,7 @@
mDebugLevel = debugLevel;
mProtectedVm = protectedVm;
mMemoryBytes = memoryBytes;
- mNumCpus = numCpus;
+ mCpuTopology = cpuTopology;
mEncryptedStorageBytes = encryptedStorageBytes;
mVmOutputCaptured = vmOutputCaptured;
}
@@ -225,7 +251,7 @@
if (memoryBytes != 0) {
builder.setMemoryBytes(memoryBytes);
}
- builder.setNumCpus(b.getInt(KEY_NUM_CPUS));
+ builder.setCpuTopology(b.getInt(KEY_CPU_TOPOLOGY));
long encryptedStorageBytes = b.getLong(KEY_ENCRYPTED_STORAGE_BYTES);
if (encryptedStorageBytes != 0) {
builder.setEncryptedStorageBytes(encryptedStorageBytes);
@@ -258,7 +284,7 @@
b.putString(KEY_PAYLOADBINARYNAME, mPayloadBinaryName);
b.putInt(KEY_DEBUGLEVEL, mDebugLevel);
b.putBoolean(KEY_PROTECTED_VM, mProtectedVm);
- b.putInt(KEY_NUM_CPUS, mNumCpus);
+ b.putInt(KEY_CPU_TOPOLOGY, mCpuTopology);
if (mMemoryBytes > 0) {
b.putLong(KEY_MEMORY_BYTES, mMemoryBytes);
}
@@ -312,7 +338,6 @@
* @hide
*/
@SystemApi
- @NonNull
@DebugLevel
public int getDebugLevel() {
return mDebugLevel;
@@ -341,14 +366,14 @@
}
/**
- * Returns the number of vCPUs that the VM will have.
+ * Returns the CPU topology configuration of the VM.
*
* @hide
*/
@SystemApi
- @IntRange(from = 1)
- public int getNumCpus() {
- return mNumCpus;
+ @CpuTopology
+ public int getCpuTopology() {
+ return mCpuTopology;
}
/**
@@ -455,7 +480,14 @@
}
vsConfig.protectedVm = mProtectedVm;
vsConfig.memoryMib = bytesToMebiBytes(mMemoryBytes);
- vsConfig.numCpus = mNumCpus;
+ switch (mCpuTopology) {
+ case CPU_TOPOLOGY_MATCH_HOST:
+ vsConfig.cpuTopology = android.system.virtualizationservice.CpuTopology.MATCH_HOST;
+ break;
+ default:
+ vsConfig.cpuTopology = android.system.virtualizationservice.CpuTopology.ONE_CPU;
+ break;
+ }
// Don't allow apps to set task profiles ... at least for now.
vsConfig.taskProfiles = EMPTY_STRING_ARRAY;
return vsConfig;
@@ -486,7 +518,7 @@
private boolean mProtectedVm;
private boolean mProtectedVmSet;
private long mMemoryBytes;
- private int mNumCpus = 1;
+ @CpuTopology private int mCpuTopology = CPU_TOPOLOGY_ONE_CPU;
private long mEncryptedStorageBytes;
private boolean mVmOutputCaptured = false;
@@ -555,7 +587,7 @@
mDebugLevel,
mProtectedVm,
mMemoryBytes,
- mNumCpus,
+ mCpuTopology,
mEncryptedStorageBytes,
mVmOutputCaptured);
}
@@ -687,25 +719,21 @@
}
/**
- * Sets the number of vCPUs in the VM. Defaults to 1. Cannot be more than the number of real
- * CPUs (as returned by {@link Runtime#availableProcessors}).
+ * Sets the CPU topology configuration of the VM. Defaults to {@link #CPU_TOPOLOGY_ONE_CPU}.
+ *
+ * <p>This determines how many virtual CPUs will be created, and their performance and
+ * scheduling characteristics, such as affinity masks. Topology also has an effect on memory
+ * usage as each vCPU requires additional memory to keep its state.
*
* @hide
*/
@SystemApi
@NonNull
- public Builder setNumCpus(@IntRange(from = 1) int numCpus) {
- int availableCpus = Runtime.getRuntime().availableProcessors();
- if (numCpus < 1 || numCpus > availableCpus) {
- throw new IllegalArgumentException(
- "Number of vCPUs ("
- + numCpus
- + ") is out of "
- + "range [1, "
- + availableCpus
- + "]");
+ public Builder setCpuTopology(@CpuTopology int cpuTopology) {
+ if (cpuTopology != CPU_TOPOLOGY_ONE_CPU && cpuTopology != CPU_TOPOLOGY_MATCH_HOST) {
+ throw new IllegalArgumentException("Invalid cpuTopology: " + cpuTopology);
}
- mNumCpus = numCpus;
+ mCpuTopology = cpuTopology;
return this;
}
diff --git a/libs/devicemapper/src/crypt.rs b/libs/devicemapper/src/crypt.rs
index b2e677a..8281b34 100644
--- a/libs/devicemapper/src/crypt.rs
+++ b/libs/devicemapper/src/crypt.rs
@@ -76,7 +76,7 @@
device_path: Option<&'a Path>,
offset: u64,
device_size: u64,
- // TODO(b/238179332) Extend this to include opt_params, in particular 'integrity'
+ opt_params: Vec<&'a str>,
}
impl<'a> Default for DmCryptTargetBuilder<'a> {
@@ -88,6 +88,7 @@
device_path: None,
offset: 0,
device_size: 0,
+ opt_params: Vec::new(),
}
}
}
@@ -124,6 +125,12 @@
self
}
+ /// Add additional optional parameter
+ pub fn opt_param(&mut self, param: &'a str) -> &mut Self {
+ self.opt_params.push(param);
+ self
+ }
+
/// Constructs a `DmCryptTarget`.
pub fn build(&self) -> Result<DmCryptTarget> {
// The `DmCryptTarget` struct actually is a flattened data consisting of a header and
@@ -154,6 +161,7 @@
write!(&mut body, "{} ", self.iv_offset)?;
write!(&mut body, "{} ", device_path)?;
write!(&mut body, "{} ", self.offset)?;
+ write!(&mut body, "{} {} ", self.opt_params.len(), self.opt_params.join(" "))?;
write!(&mut body, "\0")?; // null terminator
let size = size_of::<DmTargetSpec>() + body.len();
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 9264692..dc59fff 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -85,9 +85,12 @@
"microdroid_property_contexts",
"mke2fs.microdroid",
- // TODO(b/195425111) these should be added automatically
- "libcrypto", // used by many (init_second_stage, microdroid_manager, toybox, etc)
- "liblzma", // used by init_second_stage
+ // Adding more libs manually for unbundled build.
+ // TODO(b/268557568) these should be added automatically.
+ "libcrypto",
+ "liblzma",
+ "libc++",
+ "libssl",
"libvm_payload", // used by payload to interact with microdroid manager
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index b25034f..04a87f3 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -16,7 +16,7 @@
use android_system_virtualizationservice::{
aidl::android::system::virtualizationservice::{
- VirtualMachineConfig::VirtualMachineConfig,
+ CpuTopology::CpuTopology, VirtualMachineConfig::VirtualMachineConfig,
VirtualMachineRawConfig::VirtualMachineRawConfig,
},
binder::{ParcelFileDescriptor, ProcessState},
@@ -65,7 +65,7 @@
disks: vec![],
protectedVm: false,
memoryMib: 300,
- numCpus: 1,
+ cpuTopology: CpuTopology::ONE_CPU,
platformVersion: "~1.0".to_string(),
taskProfiles: vec![],
});
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index db87126..e979f30 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -16,6 +16,8 @@
package com.android.microdroid.benchmark;
+import static android.system.virtualmachine.VirtualMachineConfig.CPU_TOPOLOGY_ONE_CPU;
+import static android.system.virtualmachine.VirtualMachineConfig.CPU_TOPOLOGY_MATCH_HOST;
import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_FULL;
import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_NONE;
@@ -190,21 +192,21 @@
@Test
public void testMicrodroidBootTime()
throws VirtualMachineException, InterruptedException, IOException {
- BootTimeStats stats = runBootTimeTest("test_vm_boot_time", (builder) -> builder);
+ BootTimeStats stats =
+ runBootTimeTest(
+ "test_vm_boot_time",
+ (builder) -> builder.setCpuTopology(CPU_TOPOLOGY_ONE_CPU));
reportMetrics(stats.get(BootTimeMetric.TOTAL), "boot_time", "ms");
}
@Test
- public void testMicrodroidMulticoreBootTime()
+ public void testMicrodroidHostCpuTopologyBootTime()
throws VirtualMachineException, InterruptedException, IOException {
- for (int numCpus : new int[] {2, 4, 8}) {
- BootTimeStats stats =
- runBootTimeTest(
- "test_vm_boot_time_multicore",
- (builder) -> builder.setNumCpus(numCpus));
- String metricName = "boot_time_" + numCpus + "cpus";
- reportMetrics(stats.get(BootTimeMetric.TOTAL), metricName, "ms");
- }
+ BootTimeStats stats =
+ runBootTimeTest(
+ "test_vm_boot_time_host_topology",
+ (builder) -> builder.setCpuTopology(CPU_TOPOLOGY_MATCH_HOST));
+ reportMetrics(stats.get(BootTimeMetric.TOTAL), "boot_time", "ms");
}
@Test
diff --git a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
index c47e915..4dbf4ba 100644
--- a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
+++ b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
@@ -79,7 +79,6 @@
private static final int ROUND_IGNORE_STARTUP_TIME = 3;
private static final String APK_NAME = "MicrodroidTestApp.apk";
private static final String PACKAGE_NAME = "com.android.microdroid.test";
- private static final int NUM_VCPUS = 3;
private MetricsProcessor mMetricsProcessor;
@Rule public TestMetrics mMetrics = new TestMetrics();
@@ -95,6 +94,14 @@
@After
public void tearDown() throws Exception {
+ try {
+ testIfDeviceIsCapable(getDevice());
+ } catch (Exception e) {
+ // Suppress execption here.
+ // If we throw exceptions in both setUp() and tearDown(),
+ // then test is reported as fail with org.junit.TestCouldNotBeSkippedException.
+ return;
+ }
// Set PKVM enable and reboot to prevent previous staged session.
if (!isCuttlefish()) {
setPKVMStatusWithRebootToBootloader(true);
@@ -247,7 +254,7 @@
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
.debugLevel("full")
.memoryMib(vm_mem_mb)
- .numCpus(NUM_VCPUS)
+ .cpuTopology("match_host")
.build(device);
microdroidDevice.waitForBootComplete(30000);
microdroidDevice.enableAdbRoot();
diff --git a/tests/hostside/helper/Android.bp b/tests/hostside/helper/Android.bp
index 6196ec5..e8b6f36 100644
--- a/tests/hostside/helper/Android.bp
+++ b/tests/hostside/helper/Android.bp
@@ -6,6 +6,7 @@
name: "MicrodroidHostTestHelper",
srcs: ["java/**/*.java"],
libs: [
+ "androidx.annotation_annotation",
"compatibility-tradefed",
"tradefed",
"truth-prebuilt",
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/Pvmfw.java b/tests/hostside/helper/java/com/android/microdroid/test/host/Pvmfw.java
new file mode 100644
index 0000000..95eaa58
--- /dev/null
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/Pvmfw.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2023 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 com.android.microdroid.test.host;
+
+import static java.nio.ByteOrder.LITTLE_ENDIAN;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+import java.nio.ByteBuffer;
+
+/** pvmfw.bin with custom config payloads on host. */
+public final class Pvmfw {
+ private static final int SIZE_8B = 8; // 8 bytes
+ private static final int SIZE_4K = 4 << 10; // 4 KiB, PAGE_SIZE
+ private static final int BUFFER_SIZE = 1024;
+ private static final int HEADER_SIZE = Integer.BYTES * 8; // Header has 8 integers.
+ private static final int HEADER_MAGIC = 0x666d7670;
+ private static final int HEADER_VERSION = getVersion(1, 0);
+ private static final int HEADER_FLAGS = 0;
+
+ @NonNull private final File mPvmfwBinFile;
+ @NonNull private final File mBccFile;
+ @Nullable private final File mDebugPolicyFile;
+
+ private Pvmfw(
+ @NonNull File pvmfwBinFile, @NonNull File bccFile, @Nullable File debugPolicyFile) {
+ mPvmfwBinFile = Objects.requireNonNull(pvmfwBinFile);
+ mBccFile = Objects.requireNonNull(bccFile);
+ mDebugPolicyFile = debugPolicyFile;
+ }
+
+ /**
+ * Serializes pvmfw.bin and its config, as written in the <a
+ * href="https://android.googlesource.com/platform/packages/modules/Virtualization/+/master/pvmfw/README.md">README.md</a>
+ */
+ public void serialize(@NonNull File outFile) throws IOException {
+ Objects.requireNonNull(outFile);
+
+ int bccOffset = HEADER_SIZE;
+ int bccSize = (int) mBccFile.length();
+
+ int debugPolicyOffset = alignTo(bccOffset + bccSize, SIZE_8B);
+ int debugPolicySize = mDebugPolicyFile == null ? 0 : (int) mDebugPolicyFile.length();
+
+ int totalSize = debugPolicyOffset + debugPolicySize;
+
+ ByteBuffer header = ByteBuffer.allocate(HEADER_SIZE).order(LITTLE_ENDIAN);
+ header.putInt(HEADER_MAGIC);
+ header.putInt(HEADER_VERSION);
+ header.putInt(totalSize);
+ header.putInt(HEADER_FLAGS);
+ header.putInt(bccOffset);
+ header.putInt(bccSize);
+ header.putInt(debugPolicyOffset);
+ header.putInt(debugPolicySize);
+
+ try (FileOutputStream pvmfw = new FileOutputStream(outFile)) {
+ appendFile(pvmfw, mPvmfwBinFile);
+ padTo(pvmfw, SIZE_4K);
+ pvmfw.write(header.array());
+ padTo(pvmfw, HEADER_SIZE);
+ appendFile(pvmfw, mBccFile);
+ if (mDebugPolicyFile != null) {
+ padTo(pvmfw, SIZE_8B);
+ appendFile(pvmfw, mDebugPolicyFile);
+ }
+ padTo(pvmfw, SIZE_4K);
+ }
+ }
+
+ private void appendFile(@NonNull FileOutputStream out, @NonNull File inFile)
+ throws IOException {
+ byte buffer[] = new byte[BUFFER_SIZE];
+ try (FileInputStream in = new FileInputStream(inFile)) {
+ int size;
+ while (true) {
+ size = in.read(buffer);
+ if (size < 0) {
+ return;
+ }
+ out.write(buffer, /* offset= */ 0, size);
+ }
+ }
+ }
+
+ private void padTo(@NonNull FileOutputStream out, int size) throws IOException {
+ int streamSize = (int) out.getChannel().size();
+ for (int i = streamSize; i < alignTo(streamSize, size); i++) {
+ out.write(0); // write byte.
+ }
+ }
+
+ private static int alignTo(int x, int size) {
+ return (x + size - 1) & ~(size - 1);
+ }
+
+ private static int getVersion(int major, int minor) {
+ return ((major & 0xFFFF) << 16) | (minor & 0xFFFF);
+ }
+
+ /** Builder for {@link Pvmfw}. */
+ public static final class Builder {
+ @NonNull private final File mPvmfwBinFile;
+ @NonNull private final File mBccFile;
+ @Nullable private File mDebugPolicyFile;
+
+ public Builder(@NonNull File pvmfwBinFile, @NonNull File bccFile) {
+ mPvmfwBinFile = Objects.requireNonNull(pvmfwBinFile);
+ mBccFile = Objects.requireNonNull(bccFile);
+ }
+
+ @NonNull
+ public Builder setDebugPolicyOverlay(@Nullable File debugPolicyFile) {
+ mDebugPolicyFile = debugPolicyFile;
+ return this;
+ }
+
+ @NonNull
+ public Pvmfw build() {
+ return new Pvmfw(mPvmfwBinFile, mBccFile, mDebugPolicyFile);
+ }
+ }
+}
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index d40dcba..a780a8b 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -92,9 +92,6 @@
private static final int MIN_MEM_ARM64 = 145;
private static final int MIN_MEM_X86_64 = 196;
- // Number of vCPUs for testing purpose
- private static final int NUM_VCPUS = 3;
-
private static final int BOOT_COMPLETE_TIMEOUT = 30000; // 30 seconds
private static final Pattern sCIDPattern = Pattern.compile("with CID (\\d+)");
@@ -222,6 +219,14 @@
assertThat(callable.call(), matcher);
}
+ private int getDeviceNumCpus(CommandRunner runner) throws DeviceNotAvailableException {
+ return Integer.parseInt(runner.run("nproc --all").trim());
+ }
+
+ private int getDeviceNumCpus(ITestDevice device) throws DeviceNotAvailableException {
+ return getDeviceNumCpus(new CommandRunner(device));
+ }
+
static class ActiveApexInfo {
public String name;
public String path;
@@ -443,7 +448,7 @@
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
.debugLevel("full")
.memoryMib(minMemorySize())
- .numCpus(NUM_VCPUS)
+ .cpuTopology("match_host")
.protectedVm(protectedVm)
.build(getAndroidDevice());
@@ -554,7 +559,7 @@
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
.debugLevel("full")
.memoryMib(minMemorySize())
- .numCpus(NUM_VCPUS)
+ .cpuTopology("match_host")
.build(getAndroidDevice());
mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
mMicrodroidDevice.enableAdbRoot();
@@ -657,7 +662,7 @@
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
.debugLevel("full")
.memoryMib(minMemorySize())
- .numCpus(NUM_VCPUS)
+ .cpuTopology("match_host")
.build(device);
microdroid.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
device.shutdownMicrodroid(microdroid);
@@ -685,7 +690,7 @@
assertThat(atomVmCreationRequested.getVmIdentifier()).isEqualTo("VmRunApp");
assertThat(atomVmCreationRequested.getConfigType())
.isEqualTo(AtomsProto.VmCreationRequested.ConfigType.VIRTUAL_MACHINE_APP_CONFIG);
- assertThat(atomVmCreationRequested.getNumCpus()).isEqualTo(NUM_VCPUS);
+ assertThat(atomVmCreationRequested.getNumCpus()).isEqualTo(getDeviceNumCpus(device));
assertThat(atomVmCreationRequested.getMemoryMib()).isEqualTo(minMemorySize());
assertThat(atomVmCreationRequested.getApexes())
.isEqualTo("com.android.art:com.android.compos:com.android.sdkext");
@@ -720,7 +725,7 @@
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
.debugLevel("full")
.memoryMib(minMemorySize())
- .numCpus(NUM_VCPUS)
+ .cpuTopology("match_host")
.build(getAndroidDevice());
mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
CommandRunner microdroid = new CommandRunner(mMicrodroidDevice);
@@ -749,8 +754,7 @@
assertThat(android.tryRun("egrep", "'avc:[[:space:]]{1,2}denied'", LOG_PATH)).isNull();
assertThat(android.tryRun("egrep", "'avc:[[:space:]]{1,2}denied'", CONSOLE_PATH)).isNull();
- assertThat(microdroid.run("cat /proc/cpuinfo | grep processor | wc -l"))
- .isEqualTo(Integer.toString(NUM_VCPUS));
+ assertThat(getDeviceNumCpus(microdroid)).isEqualTo(getDeviceNumCpus(android));
// Check that selinux is enabled
assertThat(microdroid.run("getenforce")).isEqualTo("Enforcing");
@@ -786,7 +790,7 @@
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
.debugLevel("full")
.memoryMib(minMemorySize())
- .numCpus(NUM_VCPUS)
+ .cpuTopology("match_host")
.build(getAndroidDevice());
mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
mMicrodroidDevice.enableAdbRoot();
diff --git a/tests/hostside/tools/Android.bp b/tests/hostside/tools/Android.bp
new file mode 100644
index 0000000..f3cc275
--- /dev/null
+++ b/tests/hostside/tools/Android.bp
@@ -0,0 +1,10 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_binary_host {
+ name: "pvmfw-tool",
+ manifest: "pvmfw-tool-manifest.txt",
+ srcs: ["PvmfwTool.java"],
+ static_libs: ["MicrodroidHostTestHelper"],
+}
diff --git a/tests/hostside/tools/PvmfwTool.java b/tests/hostside/tools/PvmfwTool.java
new file mode 100644
index 0000000..18dd6d7
--- /dev/null
+++ b/tests/hostside/tools/PvmfwTool.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 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 com.android.microdroid;
+
+import com.android.microdroid.test.host.Pvmfw;
+
+import java.io.File;
+import java.io.IOException;
+
+/** CLI for {@link com.android.microdroid.test.host.Pvmfw}. */
+public class PvmfwTool {
+ public static void printUsage() {
+ System.out.println("pvmfw-tool: Appends pvmfw.bin and config payloads.");
+ System.out.println("Requires BCC and debug policy dtbo files");
+ System.out.println("");
+ System.out.println("Usage: pvmfw-tool <pvmfw_with_config> <pvmfw_bin> <bcc.dat> <dp.dtbo>");
+ }
+
+ public static void main(String[] args) {
+ if (args.length != 4) {
+ printUsage();
+ System.exit(1);
+ }
+
+ File out = new File(args[0]);
+ File pvmfw_bin = new File(args[1]);
+ File bcc_dat = new File(args[2]);
+ File dtbo = new File(args[3]);
+
+ try {
+ Pvmfw pvmfw = new Pvmfw.Builder(pvmfw_bin, bcc_dat).setDebugPolicyOverlay(dtbo).build();
+ pvmfw.serialize(out);
+ } catch (IOException e) {
+ e.printStackTrace();
+ printUsage();
+ System.exit(1);
+ }
+ }
+}
diff --git a/tests/hostside/tools/pvmfw-tool-manifest.txt b/tests/hostside/tools/pvmfw-tool-manifest.txt
new file mode 100644
index 0000000..dc71fd2
--- /dev/null
+++ b/tests/hostside/tools/pvmfw-tool-manifest.txt
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Main-Class: com.android.microdroid.PvmfwTool
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index e0748c1..4a76ead 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -18,6 +18,8 @@
import static android.system.virtualmachine.VirtualMachine.STATUS_DELETED;
import static android.system.virtualmachine.VirtualMachine.STATUS_RUNNING;
import static android.system.virtualmachine.VirtualMachine.STATUS_STOPPED;
+import static android.system.virtualmachine.VirtualMachineConfig.CPU_TOPOLOGY_ONE_CPU;
+import static android.system.virtualmachine.VirtualMachineConfig.CPU_TOPOLOGY_MATCH_HOST;
import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_FULL;
import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_NONE;
import static android.system.virtualmachine.VirtualMachineManager.CAPABILITY_NON_PROTECTED_VM;
@@ -350,7 +352,7 @@
assertThat(minimal.getApkPath()).isNull();
assertThat(minimal.getDebugLevel()).isEqualTo(DEBUG_LEVEL_NONE);
assertThat(minimal.getMemoryBytes()).isEqualTo(0);
- assertThat(minimal.getNumCpus()).isEqualTo(1);
+ assertThat(minimal.getCpuTopology()).isEqualTo(CPU_TOPOLOGY_ONE_CPU);
assertThat(minimal.getPayloadBinaryName()).isEqualTo("binary.so");
assertThat(minimal.getPayloadConfigPath()).isNull();
assertThat(minimal.isProtectedVm()).isEqualTo(isProtectedVm());
@@ -366,9 +368,9 @@
.setProtectedVm(mProtectedVm)
.setPayloadConfigPath("config/path")
.setApkPath("/apk/path")
- .setNumCpus(maxCpus)
.setDebugLevel(DEBUG_LEVEL_FULL)
.setMemoryBytes(42)
+ .setCpuTopology(CPU_TOPOLOGY_MATCH_HOST)
.setEncryptedStorageBytes(1_000_000)
.setVmOutputCaptured(true);
VirtualMachineConfig maximal = maximalBuilder.build();
@@ -376,7 +378,7 @@
assertThat(maximal.getApkPath()).isEqualTo("/apk/path");
assertThat(maximal.getDebugLevel()).isEqualTo(DEBUG_LEVEL_FULL);
assertThat(maximal.getMemoryBytes()).isEqualTo(42);
- assertThat(maximal.getNumCpus()).isEqualTo(maxCpus);
+ assertThat(maximal.getCpuTopology()).isEqualTo(CPU_TOPOLOGY_MATCH_HOST);
assertThat(maximal.getPayloadBinaryName()).isNull();
assertThat(maximal.getPayloadConfigPath()).isEqualTo("config/path");
assertThat(maximal.isProtectedVm()).isEqualTo(isProtectedVm());
@@ -408,7 +410,7 @@
IllegalArgumentException.class, () -> builder.setPayloadBinaryName("dir/file.so"));
assertThrows(IllegalArgumentException.class, () -> builder.setDebugLevel(-1));
assertThrows(IllegalArgumentException.class, () -> builder.setMemoryBytes(0));
- assertThrows(IllegalArgumentException.class, () -> builder.setNumCpus(0));
+ assertThrows(IllegalArgumentException.class, () -> builder.setCpuTopology(-1));
assertThrows(IllegalArgumentException.class, () -> builder.setEncryptedStorageBytes(0));
// Consistency checks enforced at build time.
@@ -433,8 +435,6 @@
@Test
@CddTest(requirements = {"9.17/C-1-1"})
public void compatibleConfigTests() {
- int maxCpus = Runtime.getRuntime().availableProcessors();
-
VirtualMachineConfig baseline = newBaselineBuilder().build();
// A config must be compatible with itself
@@ -442,9 +442,9 @@
// Changes that must always be compatible
assertConfigCompatible(baseline, newBaselineBuilder().setMemoryBytes(99)).isTrue();
- if (maxCpus > 1) {
- assertConfigCompatible(baseline, newBaselineBuilder().setNumCpus(2)).isTrue();
- }
+ assertConfigCompatible(
+ baseline, newBaselineBuilder().setCpuTopology(CPU_TOPOLOGY_MATCH_HOST))
+ .isTrue();
// Changes that must be incompatible, since they must change the VM identity.
assertConfigCompatible(baseline, newBaselineBuilder().setDebugLevel(DEBUG_LEVEL_FULL))
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 678c91f..89f74d6 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -27,6 +27,7 @@
ErrorCode::ErrorCode,
};
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+ CpuTopology::CpuTopology,
DiskImage::DiskImage,
IVirtualMachine::{BnVirtualMachine, IVirtualMachine},
IVirtualMachineCallback::IVirtualMachineCallback,
@@ -384,6 +385,18 @@
})
.collect::<Result<Vec<DiskFile>, _>>()?;
+ let (cpus, host_cpu_topology) = match config.cpuTopology {
+ CpuTopology::MATCH_HOST => (None, true),
+ CpuTopology::ONE_CPU => (NonZeroU32::new(1), false),
+ val => {
+ error!("Unexpected value of CPU topology: {:?}", val);
+ return Err(Status::new_service_specific_error_str(
+ -1,
+ Some(format!("Failed to parse CPU topology value: {:?}", val)),
+ ));
+ }
+ };
+
// Creating this ramdump file unconditionally is not harmful as ramdump will be created
// only when the VM is configured as such. `ramdump_write` is sent to crosvm and will
// be the backing store for the /dev/hvc1 where VM will emit ramdump to. `ramdump_read`
@@ -408,7 +421,8 @@
params: config.params.to_owned(),
protected: *is_protected,
memory_mib: config.memoryMib.try_into().ok().and_then(NonZeroU32::new),
- cpus: config.numCpus.try_into().ok().and_then(NonZeroU32::new),
+ cpus,
+ host_cpu_topology,
task_profiles: config.taskProfiles.clone(),
console_fd,
log_fd,
@@ -567,7 +581,7 @@
vm_config.name = config.name.clone();
vm_config.protectedVm = config.protectedVm;
- vm_config.numCpus = config.numCpus;
+ vm_config.cpuTopology = config.cpuTopology;
vm_config.taskProfiles = config.taskProfiles.clone();
// Microdroid takes additional init ramdisk & (optionally) storage image
diff --git a/virtualizationmanager/src/atom.rs b/virtualizationmanager/src/atom.rs
index c33f262..567fce9 100644
--- a/virtualizationmanager/src/atom.rs
+++ b/virtualizationmanager/src/atom.rs
@@ -19,6 +19,7 @@
use crate::get_calling_uid;
use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::DeathReason::DeathReason;
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+ CpuTopology::CpuTopology,
IVirtualMachine::IVirtualMachine,
VirtualMachineAppConfig::{Payload::Payload, VirtualMachineAppConfig},
VirtualMachineConfig::VirtualMachineConfig,
@@ -38,6 +39,8 @@
use std::time::{Duration, SystemTime};
use zip::ZipArchive;
+const INVALID_NUM_CPUS: i32 = -1;
+
fn get_apex_list(config: &VirtualMachineAppConfig) -> String {
match &config.payload {
Payload::PayloadConfig(_) => String::new(),
@@ -76,6 +79,19 @@
}
}
+// Returns the number of CPUs configured in the host system.
+// This matches how crosvm determines the number of logical cores.
+// For telemetry purposes only.
+pub(crate) fn get_num_cpus() -> Option<usize> {
+ // SAFETY - Only integer constants passed back and forth.
+ let ret = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_CONF) };
+ if ret > 0 {
+ ret.try_into().ok()
+ } else {
+ None
+ }
+}
+
/// Write the stats of VMCreation to statsd
pub fn write_vm_creation_stats(
config: &VirtualMachineConfig,
@@ -94,23 +110,33 @@
binder_exception_code = e.exception_code() as i32;
}
}
- let (vm_identifier, config_type, num_cpus, memory_mib, apexes) = match config {
+ let (vm_identifier, config_type, cpu_topology, memory_mib, apexes) = match config {
VirtualMachineConfig::AppConfig(config) => (
config.name.clone(),
vm_creation_requested::ConfigType::VirtualMachineAppConfig,
- config.numCpus,
+ config.cpuTopology,
config.memoryMib,
get_apex_list(config),
),
VirtualMachineConfig::RawConfig(config) => (
config.name.clone(),
vm_creation_requested::ConfigType::VirtualMachineRawConfig,
- config.numCpus,
+ config.cpuTopology,
config.memoryMib,
String::new(),
),
};
+ let num_cpus: i32 = match cpu_topology {
+ CpuTopology::MATCH_HOST => {
+ get_num_cpus().and_then(|v| v.try_into().ok()).unwrap_or_else(|| {
+ warn!("Failed to determine the number of CPUs in the host");
+ INVALID_NUM_CPUS
+ })
+ }
+ _ => 1,
+ };
+
let atom = AtomVmCreationRequested {
uid: get_calling_uid() as i32,
vmIdentifier: vm_identifier,
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 19d862a..ea1146e 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -15,7 +15,7 @@
//! Functions for running instances of `crosvm`.
use crate::aidl::{remove_temporary_files, Cid, VirtualMachineCallbacks};
-use crate::atom::write_vm_exited_stats;
+use crate::atom::{get_num_cpus, write_vm_exited_stats};
use anyhow::{anyhow, bail, Context, Error, Result};
use command_fds::CommandFdExt;
use lazy_static::lazy_static;
@@ -97,6 +97,7 @@
pub protected: bool,
pub memory_mib: Option<NonZeroU32>,
pub cpus: Option<NonZeroU32>,
+ pub host_cpu_topology: bool,
pub task_profiles: Vec<String>,
pub console_fd: Option<File>,
pub log_fd: Option<File>,
@@ -732,6 +733,15 @@
command.arg("--cpus").arg(cpus.to_string());
}
+ if config.host_cpu_topology {
+ // TODO(b/266664564): replace with --host-cpu-topology once available
+ if let Some(cpus) = get_num_cpus() {
+ command.arg("--cpus").arg(cpus.to_string());
+ } else {
+ bail!("Could not determine the number of CPUs in the system");
+ }
+ }
+
if !config.task_profiles.is_empty() {
command.arg("--task-profiles").arg(config.task_profiles.join(","));
}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/CpuTopology.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/CpuTopology.aidl
new file mode 100644
index 0000000..8a8e3d0
--- /dev/null
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/CpuTopology.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 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 android.system.virtualizationservice;
+
+/** The vCPU topology that will be generated for the VM. */
+@Backing(type="byte")
+enum CpuTopology {
+ /** One vCPU */
+ ONE_CPU = 0,
+ /** Match physical CPU topology of the host. */
+ MATCH_HOST = 1,
+}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index 884561d..7f90ed6 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -15,6 +15,7 @@
*/
package android.system.virtualizationservice;
+import android.system.virtualizationservice.CpuTopology;
import android.system.virtualizationservice.VirtualMachinePayloadConfig;
/** Configuration for running an App in a VM */
@@ -78,10 +79,8 @@
*/
int memoryMib;
- /**
- * Number of vCPUs in the VM. Defaults to 1.
- */
- int numCpus = 1;
+ /** The vCPU topology that will be generated for the VM. Default to 1 vCPU. */
+ CpuTopology cpuTopology = CpuTopology.ONE_CPU;
/**
* List of task profile names to apply for the VM
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index 993bbb0..1cda163 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -15,6 +15,7 @@
*/
package android.system.virtualizationservice;
+import android.system.virtualizationservice.CpuTopology;
import android.system.virtualizationservice.DiskImage;
/** Raw configuration for running a VM. */
@@ -49,10 +50,8 @@
/** The amount of RAM to give the VM, in MiB. 0 or negative to use the default. */
int memoryMib;
- /**
- * Number of vCPUs in the VM. Defaults to 1.
- */
- int numCpus = 1;
+ /** The vCPU topology that will be generated for the VM. Default to 1 vCPU. */
+ CpuTopology cpuTopology = CpuTopology.ONE_CPU;
/**
* A version or range of versions of the virtual platform that this config is compatible with.
diff --git a/vm/src/main.rs b/vm/src/main.rs
index ea744f7..e1c3413 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -19,8 +19,8 @@
mod run;
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
- IVirtualizationService::IVirtualizationService, PartitionType::PartitionType,
- VirtualMachineAppConfig::DebugLevel::DebugLevel,
+ CpuTopology::CpuTopology, IVirtualizationService::IVirtualizationService,
+ PartitionType::PartitionType, VirtualMachineAppConfig::DebugLevel::DebugLevel,
};
use anyhow::{Context, Error};
use binder::ProcessState;
@@ -91,9 +91,9 @@
#[clap(short, long)]
mem: Option<u32>,
- /// Number of vCPUs in the VM. If unspecified, defaults to 1.
- #[clap(long)]
- cpus: Option<u32>,
+ /// Run VM with vCPU topology matching that of the host. If unspecified, defaults to 1 vCPU.
+ #[clap(long, default_value = "one_cpu", value_parser = parse_cpu_topology)]
+ cpu_topology: CpuTopology,
/// Comma separated list of task profile names to apply to the VM
#[clap(long)]
@@ -146,9 +146,9 @@
#[clap(short, long)]
mem: Option<u32>,
- /// Number of vCPUs in the VM. If unspecified, defaults to 1.
- #[clap(long)]
- cpus: Option<u32>,
+ /// Run VM with vCPU topology matching that of the host. If unspecified, defaults to 1 vCPU.
+ #[clap(long, default_value = "one_cpu", value_parser = parse_cpu_topology)]
+ cpu_topology: CpuTopology,
/// Comma separated list of task profile names to apply to the VM
#[clap(long)]
@@ -163,9 +163,9 @@
#[clap(long)]
name: Option<String>,
- /// Number of vCPUs in the VM. If unspecified, defaults to 1.
- #[clap(long)]
- cpus: Option<u32>,
+ /// Run VM with vCPU topology matching that of the host. If unspecified, defaults to 1 vCPU.
+ #[clap(long, default_value = "one_cpu", value_parser = parse_cpu_topology)]
+ cpu_topology: CpuTopology,
/// Comma separated list of task profile names to apply to the VM
#[clap(long)]
@@ -222,6 +222,14 @@
}
}
+fn parse_cpu_topology(s: &str) -> Result<CpuTopology, String> {
+ match s {
+ "one_cpu" => Ok(CpuTopology::ONE_CPU),
+ "match_host" => Ok(CpuTopology::MATCH_HOST),
+ _ => Err(format!("Invalid cpu topology {}", s)),
+ }
+}
+
fn main() -> Result<(), Error> {
env_logger::init();
let opt = Opt::parse();
@@ -248,7 +256,7 @@
debug,
protected,
mem,
- cpus,
+ cpu_topology,
task_profiles,
extra_idsigs,
} => command_run_app(
@@ -266,7 +274,7 @@
debug,
protected,
mem,
- cpus,
+ cpu_topology,
task_profiles,
&extra_idsigs,
),
@@ -280,7 +288,7 @@
debug,
protected,
mem,
- cpus,
+ cpu_topology,
task_profiles,
} => command_run_microdroid(
name,
@@ -293,10 +301,10 @@
debug,
protected,
mem,
- cpus,
+ cpu_topology,
task_profiles,
),
- Opt::Run { name, config, cpus, task_profiles, console, log } => {
+ Opt::Run { name, config, cpu_topology, task_profiles, console, log } => {
command_run(
name,
service.as_ref(),
@@ -304,7 +312,7 @@
console.as_deref(),
log.as_deref(),
/* mem */ None,
- cpus,
+ cpu_topology,
task_profiles,
)
}
diff --git a/vm/src/run.rs b/vm/src/run.rs
index e229933..fa84591 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -16,6 +16,7 @@
use crate::create_partition::command_create_partition;
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+ CpuTopology::CpuTopology,
IVirtualizationService::IVirtualizationService,
PartitionType::PartitionType,
VirtualMachineAppConfig::{DebugLevel::DebugLevel, Payload::Payload, VirtualMachineAppConfig},
@@ -54,7 +55,7 @@
debug_level: DebugLevel,
protected: bool,
mem: Option<u32>,
- cpus: Option<u32>,
+ cpu_topology: CpuTopology,
task_profiles: Vec<String>,
extra_idsigs: &[PathBuf],
) -> Result<(), Error> {
@@ -141,7 +142,7 @@
debugLevel: debug_level,
protectedVm: protected,
memoryMib: mem.unwrap_or(0) as i32, // 0 means use the VM default
- numCpus: cpus.unwrap_or(1) as i32,
+ cpuTopology: cpu_topology,
taskProfiles: task_profiles,
});
run(service, &config, &payload_config_str, console_path, log_path)
@@ -182,7 +183,7 @@
debug_level: DebugLevel,
protected: bool,
mem: Option<u32>,
- cpus: Option<u32>,
+ cpu_topology: CpuTopology,
task_profiles: Vec<String>,
) -> Result<(), Error> {
let apk = find_empty_payload_apk_path()?;
@@ -211,7 +212,7 @@
debug_level,
protected,
mem,
- cpus,
+ cpu_topology,
task_profiles,
&extra_sig,
)
@@ -226,7 +227,7 @@
console_path: Option<&Path>,
log_path: Option<&Path>,
mem: Option<u32>,
- cpus: Option<u32>,
+ cpu_topology: CpuTopology,
task_profiles: Vec<String>,
) -> Result<(), Error> {
let config_file = File::open(config_path).context("Failed to open config file")?;
@@ -235,14 +236,12 @@
if let Some(mem) = mem {
config.memoryMib = mem as i32;
}
- if let Some(cpus) = cpus {
- config.numCpus = cpus as i32;
- }
if let Some(name) = name {
config.name = name;
} else {
config.name = String::from("VmRun");
}
+ config.cpuTopology = cpu_topology;
config.taskProfiles = task_profiles;
run(
service,
diff --git a/vmbase/example/tests/test.rs b/vmbase/example/tests/test.rs
index c6aea8c..cfb0225 100644
--- a/vmbase/example/tests/test.rs
+++ b/vmbase/example/tests/test.rs
@@ -16,7 +16,7 @@
use android_system_virtualizationservice::{
aidl::android::system::virtualizationservice::{
- DiskImage::DiskImage, VirtualMachineConfig::VirtualMachineConfig,
+ CpuTopology::CpuTopology, DiskImage::DiskImage, VirtualMachineConfig::VirtualMachineConfig,
VirtualMachineRawConfig::VirtualMachineRawConfig,
},
binder::{ParcelFileDescriptor, ProcessState},
@@ -84,7 +84,7 @@
disks: vec![disk_image],
protectedVm: false,
memoryMib: 300,
- numCpus: 1,
+ cpuTopology: CpuTopology::ONE_CPU,
platformVersion: "~1.0".to_string(),
taskProfiles: vec![],
});