Merge "[doc] Add doc about pVM DICE chain testing" into main
diff --git a/android/VmLauncherApp/Android.bp b/android/VmLauncherApp/Android.bp
index 7103d53..7dd2473 100644
--- a/android/VmLauncherApp/Android.bp
+++ b/android/VmLauncherApp/Android.bp
@@ -11,6 +11,7 @@
         "android.system.virtualizationservice_internal-java",
         // TODO(b/331708504): will be removed when AVF framework handles surface
         "libcrosvm_android_display_service-java",
+        "gson",
     ],
     libs: [
         "framework-virtualization.impl",
@@ -22,7 +23,7 @@
         "com.android.virt",
     ],
     optimize: {
-        optimize: true,
+        proguard_flags_files: ["proguard.flags"],
         shrink_resources: true,
     },
 }
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/MainActivity.java b/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/MainActivity.java
index d837c04..160140a 100644
--- a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/MainActivity.java
+++ b/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/MainActivity.java
@@ -17,7 +17,6 @@
 package com.android.virtualization.vmlauncher;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.system.virtualmachine.VirtualMachineConfig.CPU_TOPOLOGY_MATCH_HOST;
 
 import android.Manifest.permission;
 import android.app.Activity;
@@ -26,7 +25,6 @@
 import android.content.Intent;
 import android.crosvm.ICrosvmAndroidDisplayService;
 import android.graphics.PixelFormat;
-import android.graphics.Rect;
 import android.hardware.input.InputManager;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -36,13 +34,8 @@
 import android.system.virtualmachine.VirtualMachine;
 import android.system.virtualmachine.VirtualMachineCallback;
 import android.system.virtualmachine.VirtualMachineConfig;
-import android.system.virtualmachine.VirtualMachineCustomImageConfig;
-import android.system.virtualmachine.VirtualMachineCustomImageConfig.AudioConfig;
-import android.system.virtualmachine.VirtualMachineCustomImageConfig.DisplayConfig;
-import android.system.virtualmachine.VirtualMachineCustomImageConfig.GpuConfig;
 import android.system.virtualmachine.VirtualMachineException;
 import android.system.virtualmachine.VirtualMachineManager;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.InputDevice;
 import android.view.KeyEvent;
@@ -53,14 +46,9 @@
 import android.view.WindowInsets;
 import android.view.WindowInsetsController;
 import android.view.WindowManager;
-import android.view.WindowMetrics;
 
 import libcore.io.IoBridge;
 
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
 import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
 import java.io.FileInputStream;
@@ -72,10 +60,6 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -94,164 +78,6 @@
     private CursorHandler mCursorHandler;
     private ClipboardManager mClipboardManager;
 
-    private VirtualMachineConfig createVirtualMachineConfig(String jsonPath) {
-        VirtualMachineConfig.Builder configBuilder =
-                new VirtualMachineConfig.Builder(getApplication());
-        configBuilder.setCpuTopology(CPU_TOPOLOGY_MATCH_HOST);
-
-        configBuilder.setProtectedVm(false);
-        if (DEBUG) {
-            configBuilder.setDebugLevel(VirtualMachineConfig.DEBUG_LEVEL_FULL);
-            configBuilder.setVmOutputCaptured(true);
-            configBuilder.setConnectVmConsole(true);
-        }
-        VirtualMachineCustomImageConfig.Builder customImageConfigBuilder =
-                new VirtualMachineCustomImageConfig.Builder();
-        try {
-            String rawJson = new String(Files.readAllBytes(Path.of(jsonPath)));
-            JSONObject json = new JSONObject(rawJson);
-            customImageConfigBuilder.setName(json.optString("name", ""));
-            if (json.has("kernel")) {
-                customImageConfigBuilder.setKernelPath(json.getString("kernel"));
-            }
-            if (json.has("initrd")) {
-                customImageConfigBuilder.setInitrdPath(json.getString("initrd"));
-            }
-            if (json.has("params")) {
-                Arrays.stream(json.getString("params").split(" "))
-                        .forEach(customImageConfigBuilder::addParam);
-            }
-            if (json.has("bootloader")) {
-                customImageConfigBuilder.setBootloaderPath(json.getString("bootloader"));
-            }
-            if (json.has("disks")) {
-                JSONArray diskArr = json.getJSONArray("disks");
-                for (int i = 0; i < diskArr.length(); i++) {
-                    JSONObject item = diskArr.getJSONObject(i);
-                    if (item.has("image")) {
-                        if (item.optBoolean("writable", false)) {
-                            customImageConfigBuilder.addDisk(
-                                    VirtualMachineCustomImageConfig.Disk.RWDisk(
-                                            item.getString("image")));
-                        } else {
-                            customImageConfigBuilder.addDisk(
-                                    VirtualMachineCustomImageConfig.Disk.RODisk(
-                                            item.getString("image")));
-                        }
-                    } else if (item.has("partitions")) {
-                        boolean diskWritable = item.optBoolean("writable", false);
-                        VirtualMachineCustomImageConfig.Disk disk =
-                                diskWritable
-                                        ? VirtualMachineCustomImageConfig.Disk.RWDisk(null)
-                                        : VirtualMachineCustomImageConfig.Disk.RODisk(null);
-                        JSONArray partitions = item.getJSONArray("partitions");
-                        for (int j = 0; j < partitions.length(); j++) {
-                            JSONObject partition = partitions.getJSONObject(j);
-                            String label = partition.getString("label");
-                            String path = partition.getString("path");
-                            boolean partitionWritable =
-                                    diskWritable && partition.optBoolean("writable", false);
-                            String guid = partition.optString("guid");
-                            VirtualMachineCustomImageConfig.Partition p =
-                                    new VirtualMachineCustomImageConfig.Partition(
-                                            label, path, partitionWritable, guid);
-                            disk.addPartition(p);
-                        }
-                        customImageConfigBuilder.addDisk(disk);
-                    }
-                }
-            }
-            if (json.has("console_input_device")) {
-                configBuilder.setConsoleInputDevice(json.getString("console_input_device"));
-            }
-            if (json.has("gpu")) {
-                JSONObject gpuJson = json.getJSONObject("gpu");
-
-                GpuConfig.Builder gpuConfigBuilder = new GpuConfig.Builder();
-
-                if (gpuJson.has("backend")) {
-                    gpuConfigBuilder.setBackend(gpuJson.getString("backend"));
-                }
-                if (gpuJson.has("context_types")) {
-                    ArrayList<String> contextTypes = new ArrayList<String>();
-                    JSONArray contextTypesJson = gpuJson.getJSONArray("context_types");
-                    for (int i = 0; i < contextTypesJson.length(); i++) {
-                        contextTypes.add(contextTypesJson.getString(i));
-                    }
-                    gpuConfigBuilder.setContextTypes(contextTypes.toArray(new String[0]));
-                }
-                if (gpuJson.has("pci_address")) {
-                    gpuConfigBuilder.setPciAddress(gpuJson.getString("pci_address"));
-                }
-                if (gpuJson.has("renderer_features")) {
-                    gpuConfigBuilder.setRendererFeatures(gpuJson.getString("renderer_features"));
-                }
-                if (gpuJson.has("renderer_use_egl")) {
-                    gpuConfigBuilder.setRendererUseEgl(gpuJson.getBoolean("renderer_use_egl"));
-                }
-                if (gpuJson.has("renderer_use_gles")) {
-                    gpuConfigBuilder.setRendererUseGles(gpuJson.getBoolean("renderer_use_gles"));
-                }
-                if (gpuJson.has("renderer_use_glx")) {
-                    gpuConfigBuilder.setRendererUseGlx(gpuJson.getBoolean("renderer_use_glx"));
-                }
-                if (gpuJson.has("renderer_use_surfaceless")) {
-                    gpuConfigBuilder.setRendererUseSurfaceless(
-                            gpuJson.getBoolean("renderer_use_surfaceless"));
-                }
-                if (gpuJson.has("renderer_use_vulkan")) {
-                    gpuConfigBuilder.setRendererUseVulkan(
-                            gpuJson.getBoolean("renderer_use_vulkan"));
-                }
-                customImageConfigBuilder.setGpuConfig(gpuConfigBuilder.build());
-            }
-
-            long memoryMib = 1024; // 1GB by default
-            if (json.has("memory_mib")) {
-                memoryMib = json.getLong("memory_mib");
-            }
-            configBuilder.setMemoryBytes(memoryMib * 1024 * 1024);
-
-            WindowMetrics windowMetrics = getWindowManager().getCurrentWindowMetrics();
-            float dpi = DisplayMetrics.DENSITY_DEFAULT * windowMetrics.getDensity();
-            int refreshRate = (int) getDisplay().getRefreshRate();
-            if (json.has("display")) {
-                JSONObject display = json.getJSONObject("display");
-                if (display.has("scale")) {
-                    dpi *= (float) display.getDouble("scale");
-                }
-                if (display.has("refresh_rate")) {
-                    refreshRate = display.getInt("refresh_rate");
-                }
-            }
-            int dpiInt = (int) dpi;
-            DisplayConfig.Builder displayConfigBuilder = new DisplayConfig.Builder();
-            Rect windowSize = windowMetrics.getBounds();
-            displayConfigBuilder.setWidth(windowSize.right);
-            displayConfigBuilder.setHeight(windowSize.bottom);
-            displayConfigBuilder.setHorizontalDpi(dpiInt);
-            displayConfigBuilder.setVerticalDpi(dpiInt);
-            displayConfigBuilder.setRefreshRate(refreshRate);
-
-            customImageConfigBuilder.setDisplayConfig(displayConfigBuilder.build());
-            customImageConfigBuilder.useTouch(true);
-            customImageConfigBuilder.useKeyboard(true);
-            customImageConfigBuilder.useMouse(true);
-            customImageConfigBuilder.useSwitches(true);
-            customImageConfigBuilder.useTrackpad(true);
-            customImageConfigBuilder.useNetwork(true);
-
-            AudioConfig.Builder audioConfigBuilder = new AudioConfig.Builder();
-            audioConfigBuilder.setUseMicrophone(true);
-            audioConfigBuilder.setUseSpeaker(true);
-            customImageConfigBuilder.setAudioConfig(audioConfigBuilder.build());
-            configBuilder.setCustomImageConfig(customImageConfigBuilder.build());
-
-        } catch (JSONException | IOException e) {
-            throw new IllegalStateException("malformed input", e);
-        }
-        return configBuilder.build();
-    }
 
     private static boolean isVolumeKey(int keyCode) {
         return keyCode == KeyEvent.KEYCODE_VOLUME_UP
@@ -386,7 +212,7 @@
 
         try {
             VirtualMachineConfig config =
-                    createVirtualMachineConfig("/data/local/tmp/vm_config.json");
+                    VmConfigJson.from("/data/local/tmp/vm_config.json").toConfig(this);
             VirtualMachineManager vmm =
                     getApplication().getSystemService(VirtualMachineManager.class);
             if (vmm == null) {
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmConfigJson.java b/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmConfigJson.java
new file mode 100644
index 0000000..332b9f5
--- /dev/null
+++ b/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmConfigJson.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2024 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.virtualization.vmlauncher;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.system.virtualmachine.VirtualMachineConfig;
+import android.system.virtualmachine.VirtualMachineCustomImageConfig;
+import android.system.virtualmachine.VirtualMachineCustomImageConfig.AudioConfig;
+import android.system.virtualmachine.VirtualMachineCustomImageConfig.Disk;
+import android.system.virtualmachine.VirtualMachineCustomImageConfig.DisplayConfig;
+import android.system.virtualmachine.VirtualMachineCustomImageConfig.GpuConfig;
+import android.system.virtualmachine.VirtualMachineCustomImageConfig.Partition;
+import android.util.DisplayMetrics;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import com.google.gson.Gson;
+import com.google.gson.annotations.SerializedName;
+
+import java.io.FileReader;
+import java.util.Arrays;
+
+/** This class and its inner classes model vm_config.json. */
+class VmConfigJson {
+    private static final boolean DEBUG = true;
+
+    private VmConfigJson() {}
+
+    @SerializedName("protected")
+    private boolean isProtected;
+
+    private String name;
+    private String cpu_topology;
+    private String platform_version;
+    private int memory_mib = 1024;
+    private String console_input_device;
+    private String bootloader;
+    private String kernel;
+    private String initrd;
+    private String params;
+    private DiskJson[] disks;
+    private DisplayJson display;
+    private GpuJson gpu;
+
+    /** Parses JSON file at jsonPath */
+    static VmConfigJson from(String jsonPath) {
+        try (FileReader r = new FileReader(jsonPath)) {
+            return new Gson().fromJson(r, VmConfigJson.class);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to parse " + jsonPath, e);
+        }
+    }
+
+    private int getCpuTopology() {
+        switch (cpu_topology) {
+            case "one_cpu":
+                return VirtualMachineConfig.CPU_TOPOLOGY_ONE_CPU;
+            case "match_host":
+                return VirtualMachineConfig.CPU_TOPOLOGY_MATCH_HOST;
+            default:
+                throw new RuntimeException("invalid cpu topology: " + cpu_topology);
+        }
+    }
+
+    /** Converts this parsed JSON into VirtualMachieConfig */
+    VirtualMachineConfig toConfig(Context context) {
+        VirtualMachineConfig.Builder builder = new VirtualMachineConfig.Builder(context);
+        builder.setProtectedVm(isProtected)
+                .setMemoryBytes((long) memory_mib * 1024 * 1024)
+                .setConsoleInputDevice(console_input_device)
+                .setCpuTopology(getCpuTopology())
+                .setCustomImageConfig(toCustomImageConfig(context));
+
+        // TODO: make these configurable via json
+        if (DEBUG) {
+            builder.setDebugLevel(VirtualMachineConfig.DEBUG_LEVEL_FULL)
+                    .setVmOutputCaptured(true)
+                    .setConnectVmConsole(true);
+        }
+
+        return builder.build();
+    }
+
+    private VirtualMachineCustomImageConfig toCustomImageConfig(Context context) {
+        VirtualMachineCustomImageConfig.Builder builder =
+                new VirtualMachineCustomImageConfig.Builder();
+
+        builder.setName(name)
+                .setBootloaderPath(bootloader)
+                .setKernelPath(kernel)
+                .setInitrdPath(initrd);
+        if (params != null) {
+            Arrays.stream(params.split(" ")).forEach(builder::addParam);
+        }
+
+        // TODO: make these configurable via json
+        builder.useTouch(true)
+                .useKeyboard(true)
+                .useMouse(true)
+                .useSwitches(true)
+                .useTrackpad(true)
+                .useNetwork(true)
+                .setAudioConfig(
+                        new AudioConfig.Builder()
+                                .setUseMicrophone(true)
+                                .setUseSpeaker(true)
+                                .build());
+
+        for (DiskJson d : disks) {
+            builder.addDisk(d.toConfig());
+        }
+        builder.setDisplayConfig(display.toConfig(context)).setGpuConfig(gpu.toConfig());
+
+        return builder.build();
+    }
+
+    private static class DiskJson {
+        private DiskJson() {}
+
+        private boolean writable;
+        private String image;
+        private PartitionJson[] partitions;
+
+        private Disk toConfig() {
+            Disk d = writable ? Disk.RWDisk(image) : Disk.RODisk(image);
+            for (PartitionJson pj : partitions) {
+                boolean writable = this.writable && pj.writable;
+                d.addPartition(new Partition(pj.label, pj.path, writable, pj.guid));
+            }
+            return d;
+        }
+    }
+
+    private static class PartitionJson {
+        private PartitionJson() {}
+
+        private boolean writable;
+        private String label;
+        private String path;
+        private String guid;
+    }
+
+    private static class DisplayJson {
+        private DisplayJson() {}
+
+        private float scale;
+        private int refresh_rate;
+
+        private DisplayConfig toConfig(Context context) {
+            WindowManager wm = context.getSystemService(WindowManager.class);
+            WindowMetrics metrics = wm.getCurrentWindowMetrics();
+            Rect dispBounds = metrics.getBounds();
+
+            // TODO: make this overridable by json
+            int width = dispBounds.right;
+            int height = dispBounds.bottom;
+
+            int dpi = (int) (DisplayMetrics.DENSITY_DEFAULT * metrics.getDensity());
+            if (scale > 0.0f) {
+                dpi = (int) (dpi * scale);
+            }
+
+            int refreshRate = (int) context.getDisplay().getRefreshRate();
+            if (this.refresh_rate != 0) {
+                refreshRate = this.refresh_rate;
+            }
+
+            return new DisplayConfig.Builder()
+                    .setWidth(width)
+                    .setHeight(height)
+                    .setHorizontalDpi(dpi)
+                    .setVerticalDpi(dpi)
+                    .setRefreshRate(refreshRate)
+                    .build();
+        }
+    }
+
+    private static class GpuJson {
+        private GpuJson() {}
+
+        private String backend;
+        private String pci_address;
+        private String renderer_features;
+        private boolean renderer_use_egl = true;
+        private boolean renderer_use_gles = true;
+        private boolean renderer_use_glx = false;
+        private boolean renderer_use_surfaceless = true;
+        private boolean renderer_use_vulkan = false;
+        private String[] context_types;
+
+        private GpuConfig toConfig() {
+            return new GpuConfig.Builder()
+                    .setBackend(backend)
+                    .setPciAddress(pci_address)
+                    .setRendererFeatures(renderer_features)
+                    .setRendererUseEgl(renderer_use_egl)
+                    .setRendererUseGles(renderer_use_gles)
+                    .setRendererUseGlx(renderer_use_glx)
+                    .setRendererUseSurfaceless(renderer_use_surfaceless)
+                    .setRendererUseVulkan(renderer_use_vulkan)
+                    .setContextTypes(context_types)
+                    .build();
+        }
+    }
+}
diff --git a/android/VmLauncherApp/proguard.flags b/android/VmLauncherApp/proguard.flags
new file mode 100644
index 0000000..5e05ecf
--- /dev/null
+++ b/android/VmLauncherApp/proguard.flags
@@ -0,0 +1,7 @@
+# Keep the no-args constructor of the deserialized class
+-keepclassmembers class com.android.virtualization.vmlauncher.VmConfigJson {
+  <init>();
+}
+-keepclassmembers class com.android.virtualization.vmlauncher.VmConfigJson$* {
+  <init>();
+}
diff --git a/build/microdroid/Android.bp b/build/microdroid/Android.bp
index 3b0f122..27d0246 100644
--- a/build/microdroid/Android.bp
+++ b/build/microdroid/Android.bp
@@ -201,15 +201,15 @@
     name: "microdroid_build_prop_gen_x86_64",
     srcs: [
         "build.prop",
-        ":buildinfo.prop",
+        ":system-build.prop",
     ],
     out: ["build.prop.out"],
-    cmd: "(echo '# build properties from buildinfo.prop module' && " +
-        "grep ro\\.build\\.version\\.codename= $(location :buildinfo.prop) && " +
-        "grep ro\\.build\\.version\\.release= $(location :buildinfo.prop) && " +
-        "grep ro\\.build\\.version\\.sdk= $(location :buildinfo.prop) && " +
-        "grep ro\\.build\\.version\\.security_patch= $(location :buildinfo.prop) && " +
-        "grep ro\\.build\\.version\\.known_codenames= $(location :buildinfo.prop) && " +
+    cmd: "(echo '# build properties from system/build.prop' && " +
+        "grep ro\\.build\\.version\\.codename= $(location :system-build.prop) && " +
+        "grep ro\\.build\\.version\\.release= $(location :system-build.prop) && " +
+        "grep ro\\.build\\.version\\.sdk= $(location :system-build.prop) && " +
+        "grep ro\\.build\\.version\\.security_patch= $(location :system-build.prop) && " +
+        "grep ro\\.build\\.version\\.known_codenames= $(location :system-build.prop) && " +
         "cat $(location build.prop) && " +
         "echo ro.product.cpu.abilist=x86_64 && " +
         "echo ro.product.cpu.abi=x86_64) > $(out)",
@@ -219,15 +219,15 @@
     name: "microdroid_build_prop_gen_arm64",
     srcs: [
         "build.prop",
-        ":buildinfo.prop",
+        ":system-build.prop",
     ],
     out: ["build.prop.out"],
-    cmd: "(echo '# build properties from buildinfo.prop module' && " +
-        "grep ro\\.build\\.version\\.codename= $(location :buildinfo.prop) && " +
-        "grep ro\\.build\\.version\\.release= $(location :buildinfo.prop) && " +
-        "grep ro\\.build\\.version\\.sdk= $(location :buildinfo.prop) && " +
-        "grep ro\\.build\\.version\\.security_patch= $(location :buildinfo.prop) && " +
-        "grep ro\\.build\\.version\\.known_codenames= $(location :buildinfo.prop) && " +
+    cmd: "(echo '# build properties from system/build.prop' && " +
+        "grep ro\\.build\\.version\\.codename= $(location :system-build.prop) && " +
+        "grep ro\\.build\\.version\\.release= $(location :system-build.prop) && " +
+        "grep ro\\.build\\.version\\.sdk= $(location :system-build.prop) && " +
+        "grep ro\\.build\\.version\\.security_patch= $(location :system-build.prop) && " +
+        "grep ro\\.build\\.version\\.known_codenames= $(location :system-build.prop) && " +
         "cat $(location build.prop) && " +
         "echo ro.product.cpu.abilist=arm64-v8a && " +
         "echo ro.product.cpu.abi=arm64-v8a) > $(out)",
diff --git a/guest/pvmfw/src/entry.rs b/guest/pvmfw/src/entry.rs
index 0ff7270..ce04317 100644
--- a/guest/pvmfw/src/entry.rs
+++ b/guest/pvmfw/src/entry.rs
@@ -256,7 +256,7 @@
     )?;
 
     // This wrapper allows main() to be blissfully ignorant of platform details.
-    let next_bcc = crate::main(
+    let (next_bcc, debuggable_payload) = crate::main(
         slices.fdt,
         slices.kernel,
         slices.ramdisk,
@@ -274,11 +274,15 @@
     })?;
     // Call unshare_all_memory here (instead of relying on the dtor) while UART is still mapped.
     MEMORY.lock().as_mut().unwrap().unshare_all_memory();
+
     if let Some(mmio_guard) = get_mmio_guard() {
-        mmio_guard.unmap(UART_PAGE_ADDR).map_err(|e| {
-            error!("Failed to unshare the UART: {e}");
-            RebootReason::InternalError
-        })?;
+        // Keep UART MMIO_GUARD-ed for debuggable payloads, to enable earlycon.
+        if !debuggable_payload {
+            mmio_guard.unmap(UART_PAGE_ADDR).map_err(|e| {
+                error!("Failed to unshare the UART: {e}");
+                RebootReason::InternalError
+            })?;
+        }
     }
 
     // Drop MemoryTracker and deactivate page table.
diff --git a/guest/pvmfw/src/fdt.rs b/guest/pvmfw/src/fdt.rs
index 939a4ea..953fdae 100644
--- a/guest/pvmfw/src/fdt.rs
+++ b/guest/pvmfw/src/fdt.rs
@@ -593,7 +593,6 @@
 ) -> Result<(), RebootReason> {
     let mem_flags = PciMemoryFlags(range.addr.0);
     let range_type = mem_flags.range_type();
-    let prefetchable = mem_flags.prefetchable();
     let bus_addr = range.addr.1;
     let cpu_addr = range.parent_addr;
     let size = range.size;
@@ -602,10 +601,6 @@
         error!("Invalid range type {:?} for bus address {:#x} in PCI node", range_type, bus_addr);
         return Err(RebootReason::InvalidFdt);
     }
-    if prefetchable {
-        error!("PCI bus address {:#x} in PCI node is prefetchable", bus_addr);
-        return Err(RebootReason::InvalidFdt);
-    }
     // Enforce ID bus-to-cpu mappings, as used by crosvm.
     if bus_addr != cpu_addr {
         error!("PCI bus address: {:#x} is different from CPU address: {:#x}", bus_addr, cpu_addr);
diff --git a/guest/pvmfw/src/main.rs b/guest/pvmfw/src/main.rs
index 247aa6a..10f8549 100644
--- a/guest/pvmfw/src/main.rs
+++ b/guest/pvmfw/src/main.rs
@@ -67,7 +67,7 @@
     ramdisk: Option<&[u8]>,
     current_bcc_handover: &[u8],
     mut debug_policy: Option<&[u8]>,
-) -> Result<Range<usize>, RebootReason> {
+) -> Result<(Range<usize>, bool), RebootReason> {
     info!("pVM firmware");
     debug!("FDT: {:?}", fdt.as_ptr());
     debug!("Signed kernel: {:?} ({:#x} bytes)", signed_kernel.as_ptr(), signed_kernel.len());
@@ -240,7 +240,7 @@
         (r.start as usize)..(r.end as usize)
     };
 
-    Ok(bcc_range)
+    Ok((bcc_range, debuggable))
 }
 
 fn check_dice_measurements_match_entry(
diff --git a/libs/fdtpci/src/lib.rs b/libs/fdtpci/src/lib.rs
index 602f736..bdd904f 100644
--- a/libs/fdtpci/src/lib.rs
+++ b/libs/fdtpci/src/lib.rs
@@ -206,7 +206,7 @@
 impl PciMemoryFlags {
     /// Returns whether this PCI range is prefetchable
     pub fn prefetchable(self) -> bool {
-        self.0 & 0x80000000 != 0
+        self.0 & 0x40000000 != 0
     }
 
     /// Returns the type of this PCI range