Merge "vmbase_example: Condition compilation for aarch64" into main
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt
index a4663c8..086ff3d 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt
@@ -25,9 +25,6 @@
import java.io.FileReader
import java.io.IOException
import java.io.RandomAccessFile
-import java.lang.IllegalArgumentException
-import java.lang.NumberFormatException
-import java.lang.RuntimeException
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption
@@ -91,6 +88,13 @@
}
@Throws(IOException::class)
+ fun getPhysicalSize(): Long {
+ val stat = RandomAccessFile(rootPartition.toFile(), "rw").use { raf -> Os.fstat(raf.fd) }
+ // The unit of st_blocks is 512 byte in Android.
+ return 512L * stat.st_blocks
+ }
+
+ @Throws(IOException::class)
fun getSmallestSizePossible(): Long {
runE2fsck(rootPartition)
val p: String = rootPartition.toAbsolutePath().toString()
@@ -129,6 +133,17 @@
return getSize()
}
+ @Throws(IOException::class)
+ fun truncate(size: Long) {
+ try {
+ RandomAccessFile(rootPartition.toFile(), "rw").use { raf -> Os.ftruncate(raf.fd, size) }
+ Log.d(TAG, "Truncated space to: $size bytes")
+ } catch (e: ErrnoException) {
+ Log.e(TAG, "Failed to allocate space", e)
+ throw IOException("Failed to allocate space", e)
+ }
+ }
+
companion object {
private const val INSTALL_DIRNAME = "linux"
private const val ROOTFS_FILENAME = "root_part"
@@ -148,10 +163,9 @@
@Throws(IOException::class)
private fun allocateSpace(path: Path, sizeInBytes: Long) {
try {
- val raf = RandomAccessFile(path.toFile(), "rw")
- val fd = raf.fd
- Os.posix_fallocate(fd, 0, sizeInBytes)
- raf.close()
+ RandomAccessFile(path.toFile(), "rw").use { raf ->
+ Os.posix_fallocate(raf.fd, 0, sizeInBytes)
+ }
Log.d(TAG, "Allocated space to: $sizeInBytes bytes")
} catch (e: ErrnoException) {
Log.e(TAG, "Failed to allocate space", e)
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
index e4eaecb..d85242b 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
@@ -49,7 +49,7 @@
import androidx.viewpager2.widget.ViewPager2
import com.android.internal.annotations.VisibleForTesting
import com.android.microdroid.test.common.DeviceProperties
-import com.android.system.virtualmachine.flags.Flags.terminalGuiSupport
+import com.android.system.virtualmachine.flags.Flags
import com.android.virtualization.terminal.VmLauncherService.VmLauncherServiceCallback
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
@@ -125,9 +125,9 @@
}
displayMenu?.also {
- it.visibility = if (terminalGuiSupport()) View.VISIBLE else View.GONE
+ it.visibility = if (Flags.terminalGuiSupport()) View.VISIBLE else View.GONE
it.setEnabled(false)
- if (terminalGuiSupport()) {
+ if (Flags.terminalGuiSupport()) {
it.setOnClickListener {
val intent = Intent(this, DisplayActivity::class.java)
intent.flags =
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
index 067d540..f426ce6 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
@@ -31,6 +31,7 @@
import android.os.Parcel
import android.os.Parcelable
import android.os.ResultReceiver
+import android.os.StatFs
import android.os.SystemProperties
import android.system.virtualmachine.VirtualMachine
import android.system.virtualmachine.VirtualMachineCustomImageConfig
@@ -139,6 +140,30 @@
return START_NOT_STICKY
}
+ private fun truncateDiskIfNecessary(image: InstalledImage) {
+ val curSize = image.getSize()
+ val physicalSize = image.getPhysicalSize()
+
+ // Change the rootfs disk's apparent size to GUEST_SPARSE_DISK_SIZE_PERCENTAGE of the total
+ // disk size.
+ // Note that the physical size is not changed.
+ val statFs = StatFs(filesDir.absolutePath)
+ val hostSize = statFs.totalBytes
+ val expectedSize = hostSize * GUEST_SPARSE_DISK_SIZE_PERCENTAGE / 100
+ Log.d(
+ TAG,
+ "rootfs apparent size=$curSize, physical size=$physicalSize, expectedSize=$expectedSize",
+ )
+
+ if (curSize != expectedSize) {
+ try {
+ image.truncate(expectedSize)
+ } catch (e: IOException) {
+ throw RuntimeException("Failed to truncate a disk", e)
+ }
+ }
+ }
+
@WorkerThread
private fun doStart(
notification: Notification,
@@ -150,7 +175,15 @@
val json = ConfigJson.from(this, image.configPath)
val configBuilder = json.toConfigBuilder(this)
val customImageConfigBuilder = json.toCustomImageConfigBuilder(this)
- image.resize(diskSize)
+
+ if (Flags.terminalStorageBalloon()) {
+ // When storage ballooning flag is enabled, convert rootfs disk into a sparse file.
+ truncateDiskIfNecessary(image)
+ } else {
+ // Note: this doesn't always do the resizing. If the current image size is the same as
+ // the requested size which is rounded up to the page alignment, resizing is not done.
+ image.resize(diskSize)
+ }
customImageConfigBuilder.setAudioConfig(
AudioConfig.Builder().setUseSpeaker(true).setUseMicrophone(true).build()
@@ -445,6 +478,8 @@
private const val KEY_TERMINAL_IPADDRESS = "address"
private const val KEY_TERMINAL_PORT = "port"
+ private const val GUEST_SPARSE_DISK_SIZE_PERCENTAGE = 95
+
private val VM_BOOT_TIMEOUT_SECONDS: Int =
{
val deviceName = SystemProperties.get("ro.product.vendor.device", "")
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index 1e756eb..d7f68b8 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -798,9 +798,8 @@
.or_service_specific_exception(-1)?;
}
- // Check if files for payloads and bases are NOT coming from /vendor and /odm, as they may
- // have unstable interfaces.
- // TODO(b/316431494): remove once Treble interfaces are stabilized.
+ // Check if files for payloads and bases are on the same side of the Treble boundary as the
+ // calling process, as they may have unstable interfaces.
check_partitions_for_files(config, calling_partition).or_service_specific_exception(-1)?;
let zero_filler_path = temporary_directory.join("zero.img");
diff --git a/guest/pvmfw/README.md b/guest/pvmfw/README.md
index 652ca90..c7f3dd6 100644
--- a/guest/pvmfw/README.md
+++ b/guest/pvmfw/README.md
@@ -147,6 +147,10 @@
| offset = (FOURTH - HEAD) |
| size = (FOURTH_END - FOURTH) |
+-------------------------------+
+| [Entry 4] | <-- Entry 4 is present since version 1.3
+| offset = (FIFTH - HEAD) |
+| size = (FIFTH_END - FIFTH) |
++-------------------------------+
| ... |
+-------------------------------+
| [Entry n] |
@@ -168,7 +172,11 @@
| {Fourth blob: VM reference DT}|
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ <-- FOURTH_END
| (Padding to 8-byte alignment) |
-+===============================+
++===============================+ <-- FIFTH
+| {Fifth blob: Reserved Memory} |
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ <-- FIFTH_END
+| (Padding to 8-byte alignment) |
++===============================+ <-- FIFTH
| ... |
+===============================+ <-- TAIL
```
@@ -238,6 +246,31 @@
[secretkeeper_key]: https://android.googlesource.com/platform/system/secretkeeper/+/refs/heads/main/README.md#secretkeeper-public-key
[vendor_hashtree_digest]: ../../build/microdroid/README.md#verification-of-vendor-image
+#### Version 1.3 {#pvmfw-data-v1-3}
+
+In version 1.3, a fifth blob is added.
+
+- entry 4, if present, contains potentially confidential data to be passed to
+ specific guests identified from their VM name. If the data is confidential,
+ this feature should only be used with guests using a fixed rollback
+ protection mechanism to prevent rollback attacks from a malicious host. Data
+ is passed as a reserved-memory region through the device tree with the
+ provided properties at an address which is implementation defined. Multiple
+ regions may be passed to the same guest. The format is as follows.
+
+ ```rust
+ #[repr(C)]
+ struct ReservedMemConfigEntry<const N: usize> {
+ /// The number of headers contained in this blob.
+ count: u32,
+ /// The [reserved memory headers](src/reserved_mem.rs) describing the passed data.
+ headers: [RMemHeader; N]
+ /// The actual data being passed. The reserved memory headers point to
+ /// offsets within this array.
+ data: [u8],
+ }
+ ```
+
#### Virtual Platform DICE Chain Handover
The format of the DICE chain entry mentioned above, compatible with the
diff --git a/libs/dice/open_dice/src/error.rs b/libs/dice/open_dice/src/error.rs
index c9eb5cc..87d463e 100644
--- a/libs/dice/open_dice/src/error.rs
+++ b/libs/dice/open_dice/src/error.rs
@@ -33,6 +33,8 @@
UnsupportedKeyAlgorithm(coset::iana::Algorithm),
/// A failed fallible allocation. Used in no_std environments.
MemoryAllocationError,
+ /// DICE chain not found in artifacts.
+ DiceChainNotFound,
}
/// This makes `DiceError` accepted by anyhow.
@@ -51,6 +53,7 @@
write!(f, "Unsupported key algorithm: {algorithm:?}")
}
Self::MemoryAllocationError => write!(f, "Memory allocation failed"),
+ Self::DiceChainNotFound => write!(f, "DICE chain not found in artifacts"),
}
}
}
diff --git a/libs/dice/open_dice/src/retry.rs b/libs/dice/open_dice/src/retry.rs
index d793218..2b7b740 100644
--- a/libs/dice/open_dice/src/retry.rs
+++ b/libs/dice/open_dice/src/retry.rs
@@ -17,7 +17,7 @@
//! of this buffer may fail and callers will see Error::MemoryAllocationError.
//! When running with std, allocation may fail.
-use crate::bcc::{bcc_format_config_descriptor, bcc_main_flow, DiceConfigValues};
+use crate::bcc::{bcc_format_config_descriptor, bcc_main_flow, BccHandover, DiceConfigValues};
use crate::dice::{
dice_main_flow, Cdi, CdiValues, DiceArtifacts, InputValues, CDI_SIZE, PRIVATE_KEY_SEED_SIZE,
PRIVATE_KEY_SIZE,
@@ -60,6 +60,20 @@
}
}
+impl TryFrom<BccHandover<'_>> for OwnedDiceArtifacts {
+ type Error = DiceError;
+
+ fn try_from(artifacts: BccHandover<'_>) -> Result<Self> {
+ let cdi_attest = artifacts.cdi_attest().to_vec().try_into().unwrap();
+ let cdi_seal = artifacts.cdi_seal().to_vec().try_into().unwrap();
+ let bcc = artifacts
+ .bcc()
+ .map(|bcc_slice| bcc_slice.to_vec())
+ .ok_or(DiceError::DiceChainNotFound)?;
+ Ok(OwnedDiceArtifacts { cdi_values: CdiValues { cdi_attest, cdi_seal }, bcc })
+ }
+}
+
/// Retries the given function with bigger measured buffer size.
fn retry_with_measured_buffer<F>(mut f: F) -> Result<Vec<u8>>
where
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java
index b8b4ace..eba6fcc 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java
@@ -1981,6 +1981,12 @@
@SystemApi
@SuppressLint("UnflaggedApi") // already existing functionality exposed, users should flag
public interface VsockConnectionProvider {
+ /**
+ * Returns a connection, either from {@link #connectVsock} or from
+ * the VM owner which would call {@link #connectVsock} on your behalf.
+ *
+ * <p>Each call should return a new connection.
+ */
@NonNull
@SuppressLint("UnflaggedApi") // already existing functionality exposed, users should flag
public ParcelFileDescriptor addConnection() throws VirtualMachineException;
@@ -2042,8 +2048,21 @@
/**
* Convert existing vsock connection to a binder connection.
*
+ * <p>See {@linkplain #connectToVsockServer} for details. This method allows
+ * you to create the connects independently from upgrading them to the
+ * binder connection. Specifically:
+ *
* <p>connectToVsockServer = connectToVsock + binderFromPreconnectedClient
*
+ * <p>This method is useful if you want to pass the vsock connection to
+ * another process before establishing the RPC binder connection, so that
+ * you can create a direct connection.
+ *
+ * @args
+ * provider: a provider that provides the vsock connection. This
+ * provider should return connections from
+ * {@link #connectVsock}, from the VM owner.
+ *
* @hide
*/
@SystemApi