Reland Add microdroid_16k

So far only non-protected VMs can boot with 16k kernel.

Support for microdroid_16k pvms is tracked in b/376870129.

Bug: 317201718
Bug: 376517173
Bug: 333730505
Bug: 376870129
Test: adb shell /apex/com.android.virt/bin/vm run-microdroid \
    --os microdroid_16k \
    --debug full
Test: atest MicrodropidTests
Test: atest MicrodroidHostTestCases
Test: atest virtualizationmanager_device_test
Test: presubmit
Change-Id: I0fe5cd431b16f57e5bcc51146691fd0368d30818
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index d12f4bf..f55471e 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -2237,6 +2237,14 @@
     }
 
     #[test]
+    fn test_extract_os_name_from_microdroid_16k_config() -> Result<()> {
+        test_extract_os_name_from_config_path(
+            Path::new("/apex/com.android.virt/etc/microdroid_16k.json"),
+            Some("microdroid_16k"),
+        )
+    }
+
+    #[test]
     fn test_extract_os_name_from_microdroid_gki_config() -> Result<()> {
         test_extract_os_name_from_config_path(
             Path::new("/apex/com.android.virt/etc/microdroid_gki-android14-6.1.json"),
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index e940e71..4759c19 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -154,6 +154,14 @@
     }) + select(release_flag("RELEASE_AVF_ENABLE_NETWORK"), {
         true: ["com.android.virt.vmnic.rc"],
         default: [],
+    }) + select(soong_config_variable("ANDROID", "target_boots_16k"), {
+        true: [
+            "microdroid_16k_initrd_debuggable",
+            "microdroid_16k_initrd_normal",
+            "microdroid_kernel_16k",
+            "microdroid_16k.json",
+        ],
+        default: [],
     }),
     host_required: [
         "vm_shell",
diff --git a/build/apex/sign_virt_apex.py b/build/apex/sign_virt_apex.py
index 4a19c3d..e042f8d 100644
--- a/build/apex/sign_virt_apex.py
+++ b/build/apex/sign_virt_apex.py
@@ -523,6 +523,14 @@
             ret[f'gki-{ver}_initrd_normal.img']     = initrd_normal
             ret[f'gki-{ver}_initrd_debuggable.img'] = initrd_debug
 
+    kernel_16k = os.path.join(input_dir, 'etc/fs/microdroid_kernel_16k')
+    initrd_normal_16k = os.path.join(input_dir, 'etc/microdroid_16k_initrd_normal.img')
+    initrd_debug_16k = os.path.join(input_dir, 'etc/microdroid_16k_initrd_debuggable.img')
+    if os.path.isfile(kernel_16k):
+        ret['kernel_16k'] = kernel_16k
+        ret['16k_initrd_normal.img'] = initrd_normal_16k
+        ret['16k_initrd_debuggable.img'] = initrd_debug_16k
+
     return ret
 
 def IsInitrdImage(path):
diff --git a/build/microdroid/Android.bp b/build/microdroid/Android.bp
index abb97da..d5d8108 100644
--- a/build/microdroid/Android.bp
+++ b/build/microdroid/Android.bp
@@ -344,6 +344,11 @@
     cmd: "cat $(in) > $(out)",
 }
 
+filegroup {
+    name: "microdroid_16k_bootconfig_x86_64_gen",
+    srcs: ["bootconfig.x86_64_16k"],
+}
+
 prebuilt_etc {
     name: "microdroid_fstab",
     src: "fstab.microdroid",
@@ -377,6 +382,11 @@
 }
 
 prebuilt_etc {
+    name: "microdroid_16k.json",
+    src: "microdroid_16k.json",
+}
+
+prebuilt_etc {
     name: "microdroid_manifest",
     src: "microdroid_manifest.xml",
     filename: "manifest.xml",
@@ -431,6 +441,12 @@
     src: ":microdroid_initrd_normal",
 }
 
+avb_gen_vbmeta_image {
+    name: "microdroid_16k_initrd_normal_hashdesc",
+    defaults: ["microdroid_initrd_normal_defaults"],
+    src: ":microdroid_16k_initrd_normal",
+}
+
 // python -c "import hashlib; print(hashlib.sha256(b'initrd_debug').hexdigest())"
 initrd_debug_salt = "8ab9dc9cb7e6456700ff6ef18c6b4c3acc24c5fa5381b829563f8d7a415d869a"
 
@@ -447,6 +463,12 @@
     src: ":microdroid_initrd_debuggable",
 }
 
+avb_gen_vbmeta_image {
+    name: "microdroid_16k_initrd_debug_hashdesc",
+    defaults: ["microdroid_initrd_debug_defaults"],
+    src: ":microdroid_16k_initrd_debuggable",
+}
+
 soong_config_module_type {
     name: "flag_aware_avb_add_hash_footer_defaults",
     module_type: "avb_add_hash_footer_defaults",
@@ -521,6 +543,40 @@
     },
 }
 
+avb_add_hash_footer {
+    name: "microdroid_kernel_16k_signed",
+    defaults: ["microdroid_kernel_signed_defaults"],
+    filename: "microdroid_kernel_16k",
+    arch: {
+        arm64: {
+            src: ":microdroid_kernel_16k_prebuilt-arm64",
+        },
+        // There is no 16k x86_64 kernel. Instead the 16k emulation is triggered by adding
+        // `page_shift=14` to the kernel cmdline or bootconfig.
+        x86_64: {
+            src: ":microdroid_kernel_prebuilt-x86_64",
+        },
+    },
+    include_descriptors_from_images: [
+        ":microdroid_16k_initrd_normal_hashdesc",
+        ":microdroid_16k_initrd_debug_hashdesc",
+    ],
+}
+
+prebuilt_etc {
+    name: "microdroid_kernel_16k",
+    src: ":empty_file",
+    relative_install_path: "fs",
+    arch: {
+        arm64: {
+            src: ":microdroid_kernel_16k_signed",
+        },
+        x86_64: {
+            src: ":microdroid_kernel_16k_signed",
+        },
+    },
+}
+
 ///////////////////////////////////////
 // GKI-android15-6.6
 ///////////////////////////////////////
diff --git a/build/microdroid/bootconfig.x86_64_16k b/build/microdroid/bootconfig.x86_64_16k
new file mode 100644
index 0000000..6a8423b
--- /dev/null
+++ b/build/microdroid/bootconfig.x86_64_16k
@@ -0,0 +1 @@
+page_shift=14
diff --git a/build/microdroid/initrd/Android.bp b/build/microdroid/initrd/Android.bp
index 6d45417..7331e0b 100644
--- a/build/microdroid/initrd/Android.bp
+++ b/build/microdroid/initrd/Android.bp
@@ -84,6 +84,10 @@
     ":microdroid_vbmeta_bootconfig_gen",
 ]
 
+bootconfigs_x86_64_16k = bootconfigs_x86_64 + [
+    ":microdroid_16k_bootconfig_x86_64_gen",
+]
+
 java_genrule {
     name: "microdroid_initrd_debuggable_arm64",
     tools: ["initrd_bootconfig"],
@@ -118,6 +122,17 @@
 }
 
 java_genrule {
+    name: "microdroid_16k_initrd_debuggable_x86_64",
+    tools: ["initrd_bootconfig"],
+    srcs: [
+        ":microdroid_initrd_gen",
+        ":microdroid_bootconfig_debuggable_src",
+    ] + bootconfigs_x86_64_16k,
+    out: ["microdroid_16k_initrd_debuggable_x86_64"],
+    cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
+}
+
+java_genrule {
     name: "microdroid_gki-android15-6.6_initrd_debuggable_x86_64",
     tools: ["initrd_bootconfig"],
     srcs: [
@@ -162,6 +177,17 @@
 }
 
 java_genrule {
+    name: "microdroid_16k_initrd_normal_x86_64",
+    tools: ["initrd_bootconfig"],
+    srcs: [
+        ":microdroid_initrd_gen",
+        ":microdroid_bootconfig_normal_src",
+    ] + bootconfigs_x86_64_16k,
+    out: ["microdroid_16k_initrd_normal_x86_64"],
+    cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
+}
+
+java_genrule {
     name: "microdroid_gki-android15-6.6_initrd_normal_x86_64",
     tools: ["initrd_bootconfig"],
     srcs: [
@@ -188,6 +214,24 @@
 }
 
 prebuilt_etc {
+    name: "microdroid_16k_initrd_debuggable",
+    // We don't have ramdisk for architectures other than x86_64 & arm64
+    src: ":empty_file",
+    arch: {
+        // For x86_64 we emulate 16k by adding `page_shift=14` to bootconfig, that's why we need
+        // separate initrd.
+        x86_64: {
+            src: ":microdroid_16k_initrd_debuggable_x86_64",
+        },
+        // For arm64, the initrd for 16k kernel is the same.
+        arm64: {
+            src: ":microdroid_initrd_debuggable_arm64",
+        },
+    },
+    filename: "microdroid_16k_initrd_debuggable.img",
+}
+
+prebuilt_etc {
     name: "microdroid_gki-android15-6.6_initrd_debuggable",
     // We don't have ramdisk for architectures other than x86_64 & arm64
     src: ":empty_file",
@@ -218,6 +262,24 @@
 }
 
 prebuilt_etc {
+    name: "microdroid_16k_initrd_normal",
+    // We don't have ramdisk for architectures other than x86_64 & arm64
+    src: ":empty_file",
+    arch: {
+        // For x86_64 we emulate 16k by adding `page_shift=14` to bootconfig, that's why we need
+        // separate initrd.
+        x86_64: {
+            src: ":microdroid_16k_initrd_normal_x86_64",
+        },
+        // For arm64, the initrd for 16k kernel is the same.
+        arm64: {
+            src: ":microdroid_initrd_normal_arm64",
+        },
+    },
+    filename: "microdroid_16k_initrd_normal.img",
+}
+
+prebuilt_etc {
     name: "microdroid_gki-android15-6.6_initrd_normal",
     // We don't have ramdisk for architectures other than x86_64 & arm64
     src: ":empty_file",
diff --git a/build/microdroid/microdroid_16k.json b/build/microdroid/microdroid_16k.json
new file mode 100644
index 0000000..ba6a949
--- /dev/null
+++ b/build/microdroid/microdroid_16k.json
@@ -0,0 +1,21 @@
+{
+  "kernel": "/apex/com.android.virt/etc/fs/microdroid_kernel_16k",
+  "disks": [
+    {
+      "partitions": [
+        {
+          "label": "vbmeta_a",
+          "path": "/apex/com.android.virt/etc/fs/microdroid_vbmeta.img"
+        },
+        {
+          "label": "super",
+          "path": "/apex/com.android.virt/etc/fs/microdroid_super.img"
+        }
+      ],
+      "writable": false
+    }
+  ],
+  "memory_mib": 256,
+  "console_input_device": "hvc0",
+  "platform_version": "~1.0"
+}
diff --git a/guest/kernel/Android.bp b/guest/kernel/Android.bp
index 8c6cccb..d3249f6 100644
--- a/guest/kernel/Android.bp
+++ b/guest/kernel/Android.bp
@@ -43,3 +43,12 @@
         default: [],
     }),
 }
+
+filegroup {
+    name: "microdroid_kernel_16k_prebuilt-arm64",
+    // Below are properties that are conditionally set depending on value of build flags.
+    srcs: select(release_flag("RELEASE_AVF_MICRODROID_KERNEL_VERSION"), {
+        "android15_66": ["android15-6.6/arm64/16k/kernel-6.6"],
+        default: [],
+    }),
+}
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 72e5c75..5309ceb 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
@@ -72,7 +72,11 @@
     protected static final String KERNEL_VERSION = SystemProperties.get("ro.kernel.version");
     protected static final Set<String> SUPPORTED_OSES =
             Collections.unmodifiableSet(
-                    new HashSet<>(Arrays.asList("microdroid", "microdroid_gki-android15-6.6")));
+                    new HashSet<>(
+                            Arrays.asList(
+                                    "microdroid",
+                                    "microdroid_16k",
+                                    "microdroid_gki-android15-6.6")));
 
     public static boolean isCuttlefish() {
         return getDeviceProperties().isCuttlefish();
@@ -205,6 +209,10 @@
             assume().withMessage("Testing protected VMs on GSI isn't supported. b/272443823")
                     .that(isGsi())
                     .isFalse();
+            // TODO(b/376870129): remove this
+            assume().withMessage("pVMs with 16k kernel are not supported yet :(")
+                    .that(mOs)
+                    .doesNotContain("_16k");
         } else {
             assume().withMessage("Skip where VMs aren't supported")
                     .that(capabilities & VirtualMachineManager.CAPABILITY_NON_PROTECTED_VM)
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
index 811d8db..ad37dda 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
@@ -73,6 +73,7 @@
     protected static final Map<String, String> SUPPORTED_OSES =
             Map.ofEntries(
                     Map.entry("microdroid", "microdroid"),
+                    Map.entry("microdroid_16k", "microdroid_16k"),
                     Map.entry("android15_66", "microdroid_gki-android15-6.6"));
 
     /* Keep this sync with AssignableDevice.aidl */
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 20f71e5..adab521 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -415,7 +415,7 @@
     @VsrTest(requirements = {"VSR-7.1-001.008"})
     public void UpgradedPackageIsAcceptedWithSecretkeeper() throws Exception {
         // Preconditions
-        assumeVmTypeSupported(true); // Non-protected VMs may not support upgrades
+        assumeVmTypeSupported("microdroid", true); // Non-protected VMs may not support upgrades
         ensureUpdatableVmSupported();
         getDevice().uninstallPackage(PACKAGE_NAME);
         getDevice().installPackage(findTestFile(APK_NAME), /* reinstall= */ true);
@@ -433,7 +433,7 @@
     @VsrTest(requirements = {"VSR-7.1-001.008"})
     public void DowngradedPackageIsRejectedProtectedVm() throws Exception {
         // Preconditions: Rollback protection is provided only for protected VM.
-        assumeVmTypeSupported(true);
+        assumeVmTypeSupported("microdroid", true);
 
         // Install the upgraded version (v6)
         getDevice().uninstallPackage(PACKAGE_NAME);
@@ -483,7 +483,7 @@
     public void protectedVmRunsPvmfw(String os) throws Exception {
         // Arrange
         assumeKernelSupported(os);
-        assumeVmTypeSupported(true);
+        assumeVmTypeSupported(os, true);
         final String configPath = "assets/vm_config_apex.json";
 
         // Act
@@ -516,7 +516,7 @@
     public void protectedVmWithImageSignedWithDifferentKeyFailsToVerifyPayload(String os)
             throws Exception {
         assumeKernelSupported(os);
-        assumeVmTypeSupported(true);
+        assumeVmTypeSupported(os, true);
         File key = findTestFile("test.com.android.virt.pem");
 
         // Act
@@ -664,7 +664,7 @@
             throws Exception {
         // Preconditions
         assumeKernelSupported(os);
-        assumeVmTypeSupported(protectedVm);
+        assumeVmTypeSupported(os, protectedVm);
         // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
         assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assertThat(
@@ -685,7 +685,7 @@
             boolean protectedVm, String os) throws Exception {
         // Preconditions
         assumeKernelSupported(os);
-        assumeVmTypeSupported(protectedVm);
+        assumeVmTypeSupported(os, protectedVm);
         // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
         assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assertThat(
@@ -707,7 +707,7 @@
             throws Exception {
         // Preconditions
         assumeKernelSupported(os);
-        assumeVmTypeSupported(protectedVm);
+        assumeVmTypeSupported(os, protectedVm);
         assumeFalse("Cuttlefish is not supported", isCuttlefish());
         assumeFalse("Skipping test because ramdump is disabled on user build", isUserBuild());
 
@@ -784,7 +784,7 @@
         // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
         assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assumeKernelSupported(os);
-        assumeVmTypeSupported(protectedVm);
+        assumeVmTypeSupported(os, protectedVm);
 
         // Act
         assertThat(isTombstoneGeneratedWithCrashPayload(protectedVm, os, /* debuggable= */ true))
@@ -800,7 +800,7 @@
         // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
         assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assumeKernelSupported(os);
-        assumeVmTypeSupported(protectedVm);
+        assumeVmTypeSupported(os, protectedVm);
 
         // Act
         assertThat(isTombstoneGeneratedWithCrashPayload(protectedVm, os, /* debuggable= */ false))
@@ -822,7 +822,7 @@
         // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
         assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assumeKernelSupported(os);
-        assumeVmTypeSupported(protectedVm);
+        assumeVmTypeSupported(os, protectedVm);
 
         // Act
         assertThat(isTombstoneGeneratedWithCrashConfig(protectedVm, os, /* debuggable= */ true))
@@ -837,7 +837,7 @@
         // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
         assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assumeKernelSupported(os);
-        assumeVmTypeSupported(protectedVm);
+        assumeVmTypeSupported(os, protectedVm);
         assertThat(isTombstoneGeneratedWithCrashConfig(protectedVm, os, /* debuggable= */ false))
                 .isFalse();
     }
@@ -847,7 +847,7 @@
     @TestCaseName("{method}_protectedVm_{0}_os_{1}")
     public void testTelemetryPushedAtoms(boolean protectedVm, String os) throws Exception {
         assumeKernelSupported(os);
-        assumeVmTypeSupported(protectedVm);
+        assumeVmTypeSupported(os, protectedVm);
         // Reset statsd config and report before the test
         ConfigUtils.removeConfig(getDevice());
         ReportUtils.clearReports(getDevice());
@@ -1011,7 +1011,7 @@
     public void testMicrodroidBoots(boolean protectedVm, String os) throws Exception {
         // Preconditions
         assumeKernelSupported(os);
-        assumeVmTypeSupported(protectedVm);
+        assumeVmTypeSupported(os, protectedVm);
 
         final String configPath = "assets/vm_config.json"; // path inside the APK
         testMicrodroidBootsWithBuilder(
@@ -1049,7 +1049,7 @@
     void checkMicrodroidRamUsage(boolean protectedVm, String os) throws Exception {
         // Preconditions
         assumeKernelSupported(os);
-        assumeVmTypeSupported(protectedVm);
+        assumeVmTypeSupported(os, protectedVm);
 
         final String configPath = "assets/vm_config.json";
         mMicrodroidDevice =
@@ -1245,7 +1245,7 @@
     public void testDeviceAssignment(boolean protectedVm, String os) throws Exception {
         // Preconditions
         assumeKernelSupported(os);
-        assumeVmTypeSupported(protectedVm);
+        assumeVmTypeSupported(os, protectedVm);
         assumeVfioPlatformSupported();
 
         List<AssignableDevice> devices = getAssignableDevices();
@@ -1309,7 +1309,7 @@
     public void testHugePages(boolean protectedVm, String os) throws Exception {
         // Preconditions
         assumeKernelSupported(os);
-        assumeVmTypeSupported(protectedVm);
+        assumeVmTypeSupported(os, protectedVm);
 
         ITestDevice device = getDevice();
         boolean disableRoot = !device.isAdbRoot();
@@ -1382,7 +1382,7 @@
         final String configPath = "assets/vm_config.json";
         // Preconditions
         assumeKernelSupported(os);
-        assumeVmTypeSupported(true);
+        assumeVmTypeSupported(os, true);
         int mem_size = 256;
         assertTrue("Memory size too small", mem_size >= minMemorySize());
 
@@ -1551,7 +1551,11 @@
                 getSupportedOSList().contains(os));
     }
 
-    private void assumeVmTypeSupported(boolean protectedVm) throws Exception {
+    private void assumeVmTypeSupported(String os, boolean protectedVm) throws Exception {
+        // TODO(b/376870129): remove this check
+        if (protectedVm) {
+            assumeFalse("pVMs with 16k kernel are not supported yet :(", os.endsWith("_16k"));
+        }
         assumeTrue(
                 "Microdroid is not supported for specific VM protection type",
                 getAndroidDevice().supportsMicrodroid(protectedVm));