Merge "Revert "Switch to Microdroid kernel""
diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md
index 9c9bf92..bfd5ccf 100644
--- a/docs/getting_started/index.md
+++ b/docs/getting_started/index.md
@@ -43,6 +43,35 @@
fastboot reboot
```
+Due to a bug in Android 13 for these devices, pKVM may stop working after an
+[OTA update](https://source.android.com/devices/tech/ota). To prevent this, it
+is necessary to manually replicate the `pvmfw` partition across A/B slots:
+
+```shell
+adb root
+SLOT=$(adb shell getprop ro.boot.slot_suffix)
+adb pull /dev/block/by-name/pvmfw${SLOT} pvmfw.img
+adb reboot bootloader
+fastboot --slot other flash pvmfw pvmfw.img
+fastboot reboot
+```
+
+Otherwise, if an OTA has already made pKVM unusable, the working partition
+should be copied over from the "other" slot:
+
+```shell
+adb pull $(adb shell ls "/dev/block/by-name/pvmfw!(${SLOT})") pvmfw.img
+adb reboot bootloader
+fastboot flash pvmfw pvmfw.img
+fastboot reboot
+```
+
+Finally, if the `pvmfw` partition has been corrupted, both slots may be flashed
+using the [`pvmfw.img` pre-built](https://android.googlesource.com/platform/packages/modules/Virtualization/+/refs/heads/master/pvmfw/pvmfw.img)
+as long as the bootloader remains unlocked. Otherwise, a fresh install of
+Android 13 followed by the manual steps above for flashing the `other` slot
+should be used as a last resort.
+
## Running demo app
The instruction is [here](../../demo/README.md).
diff --git a/tests/testapk/assets/vm_config_no_task.json b/tests/testapk/assets/vm_config_no_task.json
new file mode 100644
index 0000000..3162bd0
--- /dev/null
+++ b/tests/testapk/assets/vm_config_no_task.json
@@ -0,0 +1,6 @@
+{
+ "os": {
+ "name": "microdroid"
+ },
+ "export_tombstones": true
+}
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 5c48a41..59f9d17 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -27,6 +27,7 @@
import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
import android.sysprop.HypervisorProperties;
+import android.system.virtualizationservice.DeathReason;
import android.system.virtualmachine.VirtualMachine;
import android.system.virtualmachine.VirtualMachineCallback;
import android.system.virtualmachine.VirtualMachineConfig;
@@ -479,10 +480,21 @@
file.writeByte(b ^ 1);
}
- private boolean tryBootVm(String vmName)
+ private static class BootResult {
+ public final boolean payloadStarted;
+ public final int deathReason;
+
+ BootResult(boolean payloadStarted, int deathReason) {
+ this.payloadStarted = payloadStarted;
+ this.deathReason = deathReason;
+ }
+ }
+
+ private BootResult tryBootVm(String vmName)
throws VirtualMachineException, InterruptedException {
mInner.mVm = mInner.mVmm.get(vmName); // re-load the vm before running tests
final CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>();
+ final CompletableFuture<Integer> deathReason = new CompletableFuture<>();
VmEventListener listener =
new VmEventListener() {
@Override
@@ -490,9 +502,15 @@
payloadStarted.complete(true);
forceStop(vm);
}
+ @Override
+ public void onDied(VirtualMachine vm, int reason) {
+ deathReason.complete(reason);
+ super.onDied(vm, reason);
+ }
};
listener.runToFinish(mInner.mVm);
- return payloadStarted.getNow(false);
+ return new BootResult(
+ payloadStarted.getNow(false), deathReason.getNow(DeathReason.INFRASTRUCTURE_ERROR));
}
private RandomAccessFile prepareInstanceImage(String vmName)
@@ -506,7 +524,7 @@
oldVm.delete();
mInner.mVmm.getOrCreate(vmName, config);
- assertThat(tryBootVm(vmName)).isTrue();
+ assertThat(tryBootVm(vmName).payloadStarted).isTrue();
File vmRoot = new File(mInner.mContext.getFilesDir(), "vm");
File vmDir = new File(vmRoot, vmName);
@@ -530,7 +548,7 @@
assertThat(offset.isPresent()).isTrue();
flipBit(instanceFile, offset.getAsLong());
- assertThat(tryBootVm("test_vm_integrity")).isFalse();
+ assertThat(tryBootVm("test_vm_integrity").payloadStarted).isFalse();
}
@Test
@@ -571,4 +589,22 @@
assertThatPartitionIsMissing(PVM_FW_PARTITION_UUID);
}
}
+
+ @Test
+ public void bootFailsWhenConfigIsInvalid()
+ throws VirtualMachineException, InterruptedException, IOException {
+ VirtualMachine existingVm = mInner.mVmm.get("test_vm_invalid_config");
+ if (existingVm != null) {
+ existingVm.delete();
+ }
+
+ VirtualMachineConfig.Builder builder =
+ mInner.newVmConfigBuilder("assets/vm_config_no_task.json");
+ VirtualMachineConfig normalConfig = builder.debugLevel(DebugLevel.NONE).build();
+ mInner.mVmm.create("test_vm_invalid_config", normalConfig);
+
+ BootResult bootResult = tryBootVm("test_vm_invalid_config");
+ assertThat(bootResult.payloadStarted).isFalse();
+ assertThat(bootResult.deathReason).isEqualTo(DeathReason.MICRODROID_INVALID_PAYLOAD_CONFIG);
+ }
}
diff --git a/vmbase/example/Android.bp b/vmbase/example/Android.bp
index 0acef2b..a0d84b4 100644
--- a/vmbase/example/Android.bp
+++ b/vmbase/example/Android.bp
@@ -47,6 +47,9 @@
enabled: true,
},
},
+ sanitize: {
+ hwaddress: false,
+ },
apex_available: ["com.android.virt"],
}