Merge "Reland: Migrate AuthFsHostTest to use MicrodroidBuilder"
diff --git a/libs/avb_bindgen/Android.bp b/libs/avb_bindgen/Android.bp
index 1035498..1e62864 100644
--- a/libs/avb_bindgen/Android.bp
+++ b/libs/avb_bindgen/Android.bp
@@ -4,6 +4,7 @@
rust_bindgen {
name: "libavb_bindgen",
+ host_supported: true,
wrapper_src: "bindgen/avb.h",
crate_name: "avb_bindgen",
source_stem: "bindings",
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 60f8fd4..b7d844f 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -79,6 +79,7 @@
"tombstoned",
"tombstone_transmit.microdroid",
"cgroups.json",
+ "task_profiles.json",
"public.libraries.android.txt",
"microdroid_compatibility_matrix",
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 9e159d2..63a7fb3 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -146,17 +146,17 @@
}
}
-fn dice_derivation(verified_data: MicrodroidData, payload_config_path: &str) -> Result<()> {
+fn dice_derivation(verified_data: &MicrodroidData, payload_config_path: &str) -> Result<()> {
// Calculate compound digests of code and authorities
let mut code_hash_ctx = digest::Context::new(&digest::SHA512);
let mut authority_hash_ctx = digest::Context::new(&digest::SHA512);
code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
- for extra_apk in verified_data.extra_apks_data {
+ for extra_apk in &verified_data.extra_apks_data {
code_hash_ctx.update(extra_apk.root_hash.as_ref());
authority_hash_ctx.update(extra_apk.pubkey.as_ref());
}
- for apex in verified_data.apex_data {
+ for apex in &verified_data.apex_data {
code_hash_ctx.update(apex.root_digest.as_ref());
authority_hash_ctx.update(apex.public_key.as_ref());
}
@@ -189,7 +189,7 @@
authorityHash: authority_hash,
authorityDescriptor: None,
mode: if app_debuggable { Mode::DEBUG } else { Mode::NORMAL },
- hidden: verified_data.salt.try_into().unwrap(),
+ hidden: verified_data.salt.clone().try_into().unwrap(),
}])
.context("IDiceMaintenance::demoteSelf failed")?;
Ok(())
@@ -240,6 +240,10 @@
instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
}
+ // To minimize the exposure to untrusted data, derive dice profile as soon as possible.
+ info!("DICE derivation for payload");
+ dice_derivation(&verified_data, &metadata.payload_config_path)?;
+
// Before reading a file from the APK, start zipfuse
run_zipfuse(
"fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
@@ -263,9 +267,6 @@
}
mount_extra_apks(&config)?;
- info!("DICE derivation for payload");
- dice_derivation(verified_data, &metadata.payload_config_path)?;
-
// Wait until apex config is done. (e.g. linker configuration for apexes)
// TODO(jooyung): wait until sys.boot_completed?
wait_for_apex_config_done()?;
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 66b4c9e..8af40e2 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -20,3 +20,28 @@
},
apex_available: ["com.android.virt"],
}
+
+cc_binary {
+ name: "pvmfw",
+ srcs: [
+ "entry.S",
+ "idmap.S",
+ ],
+ static_libs: [
+ "libpvmfw",
+ ],
+ static_executable: true,
+ no_libcrt: true,
+ nocrt: true,
+ system_shared_libs: [],
+ ldflags: [
+ "-Tpackages/modules/Virtualization/pvmfw/image.ld",
+ ],
+ enabled: false,
+ target: {
+ android_arm64: {
+ enabled: true,
+ },
+ },
+ apex_available: ["com.android.virt"],
+}
diff --git a/pvmfw/entry.S b/pvmfw/entry.S
new file mode 100644
index 0000000..787b4ff
--- /dev/null
+++ b/pvmfw/entry.S
@@ -0,0 +1,140 @@
+/*
+ * Copyright 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
+ *
+ * https://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.
+ */
+
+.macro adr_l, reg:req, sym:req
+ adrp \reg, \sym
+ add \reg, \reg, :lo12:\sym
+.endm
+
+.macro mov_i, reg:req, imm:req
+ movz \reg, :abs_g3:\imm
+ movk \reg, :abs_g2_nc:\imm
+ movk \reg, :abs_g1_nc:\imm
+ movk \reg, :abs_g0_nc:\imm
+.endm
+
+.set .L_MAIR_DEV_nGnRE, 0x04
+.set .L_MAIR_MEM_WBWA, 0xff
+.set .Lmairval, .L_MAIR_DEV_nGnRE | (.L_MAIR_MEM_WBWA << 8)
+
+/* 4 KiB granule size for TTBR0_EL1. */
+.set .L_TCR_TG0_4KB, 0x0 << 14
+/* 4 KiB granule size for TTBR1_EL1. */
+.set .L_TCR_TG1_4KB, 0x2 << 30
+/* Disable translation table walk for TTBR1_EL1, generating a translation fault instead. */
+.set .L_TCR_EPD1, 0x1 << 23
+/* Translation table walks for TTBR0_EL1 are inner sharable. */
+.set .L_TCR_SH_INNER, 0x3 << 12
+/*
+ * Translation table walks for TTBR0_EL1 are outer write-back read-allocate write-allocate
+ * cacheable.
+ */
+.set .L_TCR_RGN_OWB, 0x1 << 10
+/*
+ * Translation table walks for TTBR0_EL1 are inner write-back read-allocate write-allocate
+ * cacheable.
+ */
+.set .L_TCR_RGN_IWB, 0x1 << 8
+/* Size offset for TTBR0_EL1 is 2**39 bytes (512 GiB). */
+.set .L_TCR_T0SZ_512, 64 - 39
+.set .Ltcrval, .L_TCR_TG0_4KB | .L_TCR_TG1_4KB | .L_TCR_EPD1 | .L_TCR_RGN_OWB
+.set .Ltcrval, .Ltcrval | .L_TCR_RGN_IWB | .L_TCR_SH_INNER | .L_TCR_T0SZ_512
+
+/* Stage 1 instruction access cacheability is unaffected. */
+.set .L_SCTLR_ELx_I, 0x1 << 12
+/* SP alignment fault if SP is not aligned to a 16 byte boundary. */
+.set .L_SCTLR_ELx_SA, 0x1 << 3
+/* Stage 1 data access cacheability is unaffected. */
+.set .L_SCTLR_ELx_C, 0x1 << 2
+/* EL0 and EL1 stage 1 MMU enabled. */
+.set .L_SCTLR_ELx_M, 0x1 << 0
+/* Privileged Access Never is unchanged on taking an exception to EL1. */
+.set .L_SCTLR_EL1_SPAN, 0x1 << 23
+/* SETEND instruction disabled at EL0 in aarch32 mode. */
+.set .L_SCTLR_EL1_SED, 0x1 << 8
+/* Various IT instructions are disabled at EL0 in aarch32 mode. */
+.set .L_SCTLR_EL1_ITD, 0x1 << 7
+.set .L_SCTLR_EL1_RES1, (0x1 << 11) | (0x1 << 20) | (0x1 << 22) | (0x1 << 28) | (0x1 << 29)
+.set .Lsctlrval, .L_SCTLR_ELx_M | .L_SCTLR_ELx_C | .L_SCTLR_ELx_SA | .L_SCTLR_EL1_ITD | .L_SCTLR_EL1_SED
+.set .Lsctlrval, .Lsctlrval | .L_SCTLR_ELx_I | .L_SCTLR_EL1_SPAN | .L_SCTLR_EL1_RES1
+/**
+ * This is a generic entry point for an image. It carries out the operations
+ * required to prepare the loaded image to be run. Specifically, it zeroes the
+ * bss section using registers x25 and above, prepares the stack, enables
+ * floating point, and sets up the exception vector.
+ */
+.section .init.entry, "ax"
+.global entry
+entry:
+ /* Enable MMU and caches. */
+
+ /*
+ * Load and apply the memory management configuration.
+ */
+ adrp x1, idmap
+ mov_i x2, .Lmairval
+ mov_i x3, .Ltcrval
+ mov_i x4, .Lsctlrval
+
+ /* Copy the supported PA range into TCR_EL1.IPS. */
+ mrs x6, id_aa64mmfr0_el1
+ bfi x3, x6, #32, #4
+
+ msr ttbr0_el1, x1
+ msr mair_el1, x2
+ msr tcr_el1, x3
+
+ /*
+ * Ensure everything before this point has completed, then invalidate any potentially stale
+ * local TLB entries before they start being used.
+ */
+ isb
+ tlbi vmalle1
+ ic iallu
+ dsb nsh
+ isb
+
+ /*
+ * Configure sctlr_el1 to enable MMU and cache and don't proceed until
+ * this has completed.
+ */
+ msr sctlr_el1, x4
+ isb
+
+ /* Disable trapping floating point access in EL1. */
+ mrs x30, cpacr_el1
+ orr x30, x30, #(0x3 << 20)
+ msr cpacr_el1, x30
+ isb
+
+ /* Zero out the bss section. */
+ adr_l x29, bss_begin
+ adr_l x30, bss_end
+0: cmp x29, x30
+ b.hs 1f
+ stp xzr, xzr, [x29], #16
+ b 0b
+
+1: /* Prepare the stack. */
+ adr x30, boot_stack_end
+ mov sp, x30
+
+ /* Call into Rust code. */
+ bl main
+
+ /* Loop forever waiting for interrupts. */
+2: wfi
+ b 2b
diff --git a/pvmfw/idmap.S b/pvmfw/idmap.S
new file mode 100644
index 0000000..f5050af
--- /dev/null
+++ b/pvmfw/idmap.S
@@ -0,0 +1,48 @@
+/*
+ * Copyright 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
+ *
+ * https://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.
+ */
+
+.set .L_TT_TYPE_BLOCK, 0x1
+.set .L_TT_TYPE_PAGE, 0x3
+.set .L_TT_TYPE_TABLE, 0x3
+
+/* Access flag. */
+.set .L_TT_AF, 0x1 << 10
+/* Not global. */
+.set .L_TT_NG, 0x1 << 11
+.set .L_TT_RO, 0x2 << 6
+.set .L_TT_XN, 0x3 << 53
+
+.set .L_TT_MT_DEV, 0x0 << 2 // MAIR #0 (DEV_nGnRE)
+.set .L_TT_MT_MEM, (0x1 << 2) | (0x3 << 8) // MAIR #1 (MEM_WBWA), inner shareable
+
+.set .L_BLOCK_RO, .L_TT_TYPE_BLOCK | .L_TT_MT_MEM | .L_TT_AF | .L_TT_RO | .L_TT_XN
+.set .L_BLOCK_DEV, .L_TT_TYPE_BLOCK | .L_TT_MT_DEV | .L_TT_AF | .L_TT_XN
+.set .L_BLOCK_MEM_XIP, .L_TT_TYPE_BLOCK | .L_TT_MT_MEM | .L_TT_AF | .L_TT_NG
+
+.section ".rodata.idmap", "a", %progbits
+.global idmap
+.align 12
+idmap:
+ /* level 1 */
+ .quad .L_BLOCK_DEV | 0x0 // 1 GB of device mappings
+ .quad .L_BLOCK_DEV | 0x40000000 // Another 1 GB of device mapppings
+ .quad .L_TT_TYPE_TABLE + 0f // up to 1 GB of DRAM
+ .fill 509, 8, 0x0 // 509 GB of remaining VA space
+
+ /* level 2 */
+0: .quad .L_BLOCK_RO | 0x80000000 // DT provided by VMM
+ .quad .L_BLOCK_MEM_XIP | 0x80200000 // 2 MB of DRAM containing image
+ .fill 510, 8, 0x0
diff --git a/pvmfw/image.ld b/pvmfw/image.ld
new file mode 100644
index 0000000..e08fbe2
--- /dev/null
+++ b/pvmfw/image.ld
@@ -0,0 +1,117 @@
+/*
+ * Copyright 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
+ *
+ * https://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.
+ */
+
+MEMORY
+{
+ dtb_region : ORIGIN = 0x80000000, LENGTH = 2M
+ image : ORIGIN = 0x80200000, LENGTH = 2M
+}
+
+/*
+ * Code will start running at this symbol which is placed at the start of the
+ * image.
+ */
+ENTRY(entry)
+
+/*
+ * The following would be useful to check that .init code is not called back
+ * into once it has completed but it isn't supported by ld.lld.
+ *
+ * NOCROSSREFS_TO(.init .text)
+ */
+
+SECTIONS
+{
+ .dtb (NOLOAD) : {
+ dtb_begin = .;
+ . += LENGTH(dtb_region);
+ dtb_end = .;
+ } >dtb_region
+
+ /*
+ * Collect together the code. This is page aligned so it can be mapped
+ * as executable-only.
+ */
+ .init : ALIGN(4096) {
+ text_begin = .;
+ *(.init.entry)
+ *(.init.*)
+ } >image
+ .text : {
+ *(.text.*)
+ } >image
+ text_end = .;
+
+ /*
+ * Collect together read-only data. This is page aligned so it can be
+ * mapped as read-only and non-executable.
+ */
+ .rodata : ALIGN(4096) {
+ rodata_begin = .;
+ *(.rodata.*)
+ } >image
+ .got : {
+ *(.got)
+ } >image
+ rodata_end = .;
+
+ /*
+ * Collect together the read-write data including .bss at the end which
+ * will be zero'd by the entry code. This is page aligned so it can be
+ * mapped as non-executable.
+ */
+ .data : ALIGN(4096) {
+ data_begin = .;
+ *(.data.*)
+ /*
+ * The entry point code assumes that .data is a multiple of 32
+ * bytes long.
+ */
+ . = ALIGN(32);
+ data_end = .;
+ } >image
+ /* Everything beyond this point will not be included in the binary. */
+ bin_end = .;
+
+ /* The entry point code assumes that .bss is 16-byte aligned. */
+ .bss : ALIGN(16) {
+ bss_begin = .;
+ *(.bss.*)
+ *(COMMON)
+ . = ALIGN(16);
+ bss_end = .;
+ } >image
+
+ .stack (NOLOAD) : ALIGN(4096) {
+ boot_stack_begin = .;
+ . += 40 * 4096;
+ . = ALIGN(4096);
+ boot_stack_end = .;
+ } >image
+
+ /*
+ * Remove unused sections from the image.
+ */
+ /DISCARD/ : {
+ /* The image loads itself so doesn't need these sections. */
+ *(.gnu.hash)
+ *(.hash)
+ *(.interp)
+ *(.eh_frame_hdr)
+ *(.eh_frame)
+ *(.note.gnu.build-id)
+ }
+}
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
index df3e247..440ae18 100644
--- a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -133,6 +133,19 @@
return result.getStdout().trim();
}
+ // Same as runOnMicrodroid, but keeps retrying on error till timeout
+ private static String runOnMicrodroidRetryingOnFailure(String... cmd) {
+ final long timeoutMs = 30000; // 30 sec. Microdroid is extremely slow on GCE-on-CF.
+ int attempts = (int) MICRODROID_ADB_CONNECT_TIMEOUT_MINUTES * 60 * 1000 / 500;
+ CommandResult result = RunUtil.getDefault()
+ .runTimedCmdRetry(timeoutMs, 500, attempts,
+ "adb", "-s", MICRODROID_SERIAL, "shell", join(cmd));
+ if (result.getStatus() != CommandStatus.SUCCESS) {
+ fail(join(cmd) + " has failed: " + result);
+ }
+ return result.getStdout().trim();
+ }
+
// Same as runOnMicrodroid, but returns null on error.
public static String tryRunOnMicrodroid(String... cmd) {
CommandResult result = runOnMicrodroidForResult(cmd);
@@ -332,13 +345,16 @@
public static void rootMicrodroid() {
runOnHost("adb", "-s", MICRODROID_SERIAL, "root");
-
runOnHostWithTimeout(
MICRODROID_ADB_CONNECT_TIMEOUT_MINUTES * 60 * 1000,
"adb",
"-s",
MICRODROID_SERIAL,
"wait-for-device");
+ // There have been tests when adb wait-for-device succeeded but the following command
+ // fails with error: closed. Hence, we run adb shell true in microdroid with retries
+ // before returning.
+ runOnMicrodroidRetryingOnFailure("true");
}
// Establish an adb connection to microdroid by letting Android forward the connection to
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 73cbcfa..a2e856c 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -132,139 +132,26 @@
console_fd: Option<&ParcelFileDescriptor>,
log_fd: Option<&ParcelFileDescriptor>,
) -> binder::Result<Strong<dyn IVirtualMachine>> {
- check_manage_access()?;
- let state = &mut *self.state.lock().unwrap();
- let console_fd = console_fd.map(clone_file).transpose()?;
- let log_fd = log_fd.map(clone_file).transpose()?;
- let requester_uid = ThreadState::get_calling_uid();
- let requester_sid = get_calling_sid()?;
- let requester_debug_pid = ThreadState::get_calling_pid();
- let cid = next_cid().or(Err(ExceptionCode::ILLEGAL_STATE))?;
-
- // Counter to generate unique IDs for temporary image files.
- let mut next_temporary_image_id = 0;
- // Files which are referred to from composite images. These must be mapped to the crosvm
- // child process, and not closed before it is started.
- let mut indirect_files = vec![];
-
- // Make directory for temporary files.
- let temporary_directory: PathBuf = format!("{}/{}", TEMPORARY_DIRECTORY, cid).into();
- create_dir(&temporary_directory).map_err(|e| {
- // At this point, we do not know the protected status of Vm
- // setting it to false, though this may not be correct.
- write_vm_creation_stats(false, false);
- error!(
- "Failed to create temporary directory {:?} for VM files: {}",
- temporary_directory, e
- );
- new_binder_exception(
- ExceptionCode::SERVICE_SPECIFIC,
- format!(
- "Failed to create temporary directory {:?} for VM files: {}",
- temporary_directory, e
- ),
- )
- })?;
-
- let is_app_config = matches!(config, VirtualMachineConfig::AppConfig(_));
-
- let config = match config {
- VirtualMachineConfig::AppConfig(config) => BorrowedOrOwned::Owned(
- load_app_config(config, &temporary_directory).map_err(|e| {
- error!("Failed to load app config from {}: {}", &config.configPath, e);
- write_vm_creation_stats(config.protectedVm, false);
- new_binder_exception(
- ExceptionCode::SERVICE_SPECIFIC,
- format!("Failed to load app config from {}: {}", &config.configPath, e),
- )
- })?,
- ),
- VirtualMachineConfig::RawConfig(config) => BorrowedOrOwned::Borrowed(config),
- };
- let config = config.as_ref();
- let protected = config.protectedVm;
-
- // Check if partition images are labeled incorrectly. This is to prevent random images
- // which are not protected by the Android Verified Boot (e.g. bits downloaded by apps) from
- // being loaded in a pVM. Specifically, for images in the raw config, nothing is allowed
- // to be labeled as app_data_file. For images in the app config, nothing but the instance
- // partition is allowed to be labeled as such.
- config
- .disks
- .iter()
- .flat_map(|disk| disk.partitions.iter())
- .filter(|partition| {
- if is_app_config {
- partition.label != "vm-instance"
- } else {
- true // all partitions are checked
- }
- })
- .try_for_each(check_label_for_partition)
- .map_err(|e| new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, e.to_string()))?;
-
- let zero_filler_path = temporary_directory.join("zero.img");
- write_zero_filler(&zero_filler_path).map_err(|e| {
- error!("Failed to make composite image: {}", e);
- write_vm_creation_stats(protected, false);
- new_binder_exception(
- ExceptionCode::SERVICE_SPECIFIC,
- format!("Failed to make composite image: {}", e),
- )
- })?;
-
- // Assemble disk images if needed.
- let disks = config
- .disks
- .iter()
- .map(|disk| {
- assemble_disk_image(
- disk,
- &zero_filler_path,
- &temporary_directory,
- &mut next_temporary_image_id,
- &mut indirect_files,
- )
- })
- .collect::<Result<Vec<DiskFile>, _>>()?;
-
- // Actually start the VM.
- let crosvm_config = CrosvmConfig {
- cid,
- bootloader: maybe_clone_file(&config.bootloader)?,
- kernel: maybe_clone_file(&config.kernel)?,
- initrd: maybe_clone_file(&config.initrd)?,
- disks,
- params: config.params.to_owned(),
- protected,
- memory_mib: config.memoryMib.try_into().ok().and_then(NonZeroU32::new),
- cpus: config.numCpus.try_into().ok().and_then(NonZeroU32::new),
- cpu_affinity: config.cpuAffinity.clone(),
- console_fd,
- log_fd,
- indirect_files,
- platform_version: parse_platform_version_req(&config.platformVersion)?,
- };
- let instance = Arc::new(
- VmInstance::new(
- crosvm_config,
- temporary_directory,
- requester_uid,
- requester_sid,
- requester_debug_pid,
- )
- .map_err(|e| {
- error!("Failed to create VM with config {:?}: {}", config, e);
- write_vm_creation_stats(protected, false);
- new_binder_exception(
- ExceptionCode::SERVICE_SPECIFIC,
- format!("Failed to create VM: {}", e),
- )
- })?,
- );
- state.add_vm(Arc::downgrade(&instance));
- write_vm_creation_stats(protected, true);
- Ok(VirtualMachine::create(instance))
+ let mut is_protected = false;
+ let ret = self.create_vm_internal(config, console_fd, log_fd, &mut is_protected);
+ match ret {
+ Ok(_) => {
+ let ok_status = Status::ok();
+ write_vm_creation_stats(
+ is_protected,
+ /*creation_succeeded*/ true,
+ ok_status.exception_code() as i32,
+ );
+ }
+ Err(ref e) => {
+ write_vm_creation_stats(
+ is_protected,
+ /*creation_succeeded*/ false,
+ e.exception_code() as i32,
+ );
+ }
+ }
+ ret
}
/// Initialise an empty partition image of the given size to be used as a writable partition.
@@ -466,11 +353,149 @@
});
service
}
+
+ fn create_vm_internal(
+ &self,
+ config: &VirtualMachineConfig,
+ console_fd: Option<&ParcelFileDescriptor>,
+ log_fd: Option<&ParcelFileDescriptor>,
+ is_protected: &mut bool,
+ ) -> binder::Result<Strong<dyn IVirtualMachine>> {
+ check_manage_access()?;
+ let state = &mut *self.state.lock().unwrap();
+ let console_fd = console_fd.map(clone_file).transpose()?;
+ let log_fd = log_fd.map(clone_file).transpose()?;
+ let requester_uid = ThreadState::get_calling_uid();
+ let requester_sid = get_calling_sid()?;
+ let requester_debug_pid = ThreadState::get_calling_pid();
+ let cid = next_cid().or(Err(ExceptionCode::ILLEGAL_STATE))?;
+
+ // Counter to generate unique IDs for temporary image files.
+ let mut next_temporary_image_id = 0;
+ // Files which are referred to from composite images. These must be mapped to the crosvm
+ // child process, and not closed before it is started.
+ let mut indirect_files = vec![];
+
+ // Make directory for temporary files.
+ let temporary_directory: PathBuf = format!("{}/{}", TEMPORARY_DIRECTORY, cid).into();
+ create_dir(&temporary_directory).map_err(|e| {
+ // At this point, we do not know the protected status of Vm
+ // setting it to false, though this may not be correct.
+ error!(
+ "Failed to create temporary directory {:?} for VM files: {}",
+ temporary_directory, e
+ );
+ new_binder_exception(
+ ExceptionCode::SERVICE_SPECIFIC,
+ format!(
+ "Failed to create temporary directory {:?} for VM files: {}",
+ temporary_directory, e
+ ),
+ )
+ })?;
+
+ let is_app_config = matches!(config, VirtualMachineConfig::AppConfig(_));
+
+ let config = match config {
+ VirtualMachineConfig::AppConfig(config) => BorrowedOrOwned::Owned(
+ load_app_config(config, &temporary_directory).map_err(|e| {
+ error!("Failed to load app config from {}: {}", &config.configPath, e);
+ *is_protected = config.protectedVm;
+ new_binder_exception(
+ ExceptionCode::SERVICE_SPECIFIC,
+ format!("Failed to load app config from {}: {}", &config.configPath, e),
+ )
+ })?,
+ ),
+ VirtualMachineConfig::RawConfig(config) => BorrowedOrOwned::Borrowed(config),
+ };
+ let config = config.as_ref();
+ *is_protected = config.protectedVm;
+
+ // Check if partition images are labeled incorrectly. This is to prevent random images
+ // which are not protected by the Android Verified Boot (e.g. bits downloaded by apps) from
+ // being loaded in a pVM. Specifically, for images in the raw config, nothing is allowed
+ // to be labeled as app_data_file. For images in the app config, nothing but the instance
+ // partition is allowed to be labeled as such.
+ config
+ .disks
+ .iter()
+ .flat_map(|disk| disk.partitions.iter())
+ .filter(|partition| {
+ if is_app_config {
+ partition.label != "vm-instance"
+ } else {
+ true // all partitions are checked
+ }
+ })
+ .try_for_each(check_label_for_partition)
+ .map_err(|e| new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, e.to_string()))?;
+
+ let zero_filler_path = temporary_directory.join("zero.img");
+ write_zero_filler(&zero_filler_path).map_err(|e| {
+ error!("Failed to make composite image: {}", e);
+ new_binder_exception(
+ ExceptionCode::SERVICE_SPECIFIC,
+ format!("Failed to make composite image: {}", e),
+ )
+ })?;
+
+ // Assemble disk images if needed.
+ let disks = config
+ .disks
+ .iter()
+ .map(|disk| {
+ assemble_disk_image(
+ disk,
+ &zero_filler_path,
+ &temporary_directory,
+ &mut next_temporary_image_id,
+ &mut indirect_files,
+ )
+ })
+ .collect::<Result<Vec<DiskFile>, _>>()?;
+
+ // Actually start the VM.
+ let crosvm_config = CrosvmConfig {
+ cid,
+ bootloader: maybe_clone_file(&config.bootloader)?,
+ kernel: maybe_clone_file(&config.kernel)?,
+ initrd: maybe_clone_file(&config.initrd)?,
+ disks,
+ 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),
+ cpu_affinity: config.cpuAffinity.clone(),
+ console_fd,
+ log_fd,
+ indirect_files,
+ platform_version: parse_platform_version_req(&config.platformVersion)?,
+ };
+ let instance = Arc::new(
+ VmInstance::new(
+ crosvm_config,
+ temporary_directory,
+ requester_uid,
+ requester_sid,
+ requester_debug_pid,
+ )
+ .map_err(|e| {
+ error!("Failed to create VM with config {:?}: {}", config, e);
+ new_binder_exception(
+ ExceptionCode::SERVICE_SPECIFIC,
+ format!("Failed to create VM: {}", e),
+ )
+ })?,
+ );
+ state.add_vm(Arc::downgrade(&instance));
+ Ok(VirtualMachine::create(instance))
+ }
}
/// Write the stats of VMCreation to statsd
-fn write_vm_creation_stats(protected: bool, success: bool) {
- match stats_write(Hypervisor::Pkvm, protected, success) {
+fn write_vm_creation_stats(is_protected: bool, creation_succeeded: bool, exception_code: i32) {
+ match stats_write(Hypervisor::Pkvm, is_protected, creation_succeeded, exception_code) {
Err(e) => {
warn!("statslog_rust failed with error: {}", e);
}