• Home
Name Date Size #Lines LOC

..--

asm/aarch64/04-Jul-2025-7143

avb/04-Jul-2025-1,8861,455

src/04-Jul-2025-6,3394,877

testdata/04-Jul-2025-924787

Android.bpD04-Jul-202512.7 KiB442404

README.mdD04-Jul-202523.3 KiB568454

TEST_MAPPINGD04-Jul-2025402 1918

platform.dtsD04-Jul-202512 KiB492453

README.md

1# Protected Virtual Machine Firmware
2
3## Protected VM (_"pVM"_)
4
5In the context of the [Android Virtualization Framework][AVF], a hypervisor
6(_e.g._ [pKVM]) enforces full memory isolation between its virtual machines
7(VMs) and the host. As a result, such VMs are given strong guarantees regarding
8their confidentiality and integrity.
9
10A _protected VMs_ (“pVMs”) is a VM running in the non-secure or realm world,
11started dynamically by a _virtual machine manager_ (“VMM”) running as a process
12of the untrusted Android host (see [_Why AVF?_][why-avf]) and which is isolated
13from the host OS so that access to its memory is restricted, even in the event
14of a compromised Android host. pVMs support rich environments, including
15Linux-based distributions.
16
17The pVM concept is not Google-exclusive. Partner-defined VMs (SoC/OEM) meeting
18isolation/memory access restrictions are also pVMs.
19
20## Protected VM Root-of-Trust: pvmfw
21
22As pVMs are managed by a VMM running on the untrusted host, the virtual machine
23it configures can't be trusted either. Furthermore, even though the isolation
24mentioned above allows pVMs to protect their secrets from the host, it does not
25help with provisioning them during boot. In particular, the threat model would
26prohibit the host from ever having access to those secrets, preventing the VMM
27from passing them to the pVM.
28
29To address these concerns the hypervisor securely loads the pVM firmware
30(“pvmfw”) in the pVM from a protected memory region (this prevents the host or
31any pVM from tampering with it), setting it as the entry point of the virtual
32machine. As a result, pvmfw becomes the very first code that gets executed in
33the pVM, allowing it to validate the environment and abort the boot sequence if
34necessary. This process takes place whenever the VMM places a VM in protected
35mode and can’t be prevented by the host.
36
37Given the threat model, pvmfw is not allowed to trust the devices or device
38layout provided by the virtual platform it is running on as those are configured
39by the VMM. Instead, it performs all the necessary checks to ensure that the pVM
40was set up as expected. For functional purposes, the interface with the
41hypervisor, although trusted, is also validated.
42
43Once it has been determined that the platform can be trusted, pvmfw derives
44unique secrets for the guest through the [_DICE Chain_][android-dice] (see
45[Open Profile for DICE][open-dice]) that can be used to prove the identity of
46the pVM to local and remote actors. If any operation or check fails, or in case
47of a missing prerequisite, pvmfw will abort the boot process of the pVM,
48effectively preventing non-compliant pVMs and/or guests from running.
49Otherwise, it hands over the pVM to the guest kernel by jumping to its first
50instruction, similarly to a bootloader.
51
52pvmfw currently only supports AArch64.
53
54[AVF]: https://source.android.com/docs/core/virtualization
55[why-avf]: https://source.android.com/docs/core/virtualization/whyavf
56[android-dice]: https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/android.md
57[pKVM]: https://source.android.com/docs/core/virtualization/architecture#hypervisor
58[open-dice]: https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md
59
60## Integration
61
62### pvmfw Loading
63
64When running pKVM, the physical memory from which the hypervisor loads pvmfw
65into guest address space is not initially populated by the hypervisor itself.
66Instead, it receives a pre-loaded memory region from a trusted pvmfw loader and
67only then becomes responsible for protecting it. As a result, the hypervisor is
68kept generic (beyond AVF) and small as it is not expected (nor necessary) for it
69to know how to interpret or obtain the content of that region.
70
71#### Android Bootloader (ABL) Support
72
73Starting in Android T, the `PRODUCT_BUILD_PVMFW_IMAGE` build variable controls
74the generation of `pvmfw.img`, a new [ABL partition][ABL-part] containing the
75pvmfw binary (sometimes called "`pvmfw.bin`") and following the internal format
76of the [`boot`][boot-img] partition, intended to be verified and loaded by ABL
77on AVF-compatible devices.
78
79Once ABL has verified the `pvmfw.img` chained static partition, the contained
80[`boot.img` header][boot-img] may be used to obtain the size of the `pvmfw.bin`
81image (recorded in the `kernel_size` field), as it already does for the kernel
82itself. In accordance with the header format, the `kernel_size` bytes of the
83partition following the header will be the `pvmfw.bin` image.
84
85Note that when it gets executed in the context of a pVM, `pvmfw` expects to have
86been loaded at 4KiB-aligned intermediate physical address (IPA) so if ABL loads
87the `pvmfw.bin` image without respecting this alignment, it is the
88responsibility of the hypervisor to either reject the image or copy it into
89guest address space with the right alignment.
90
91To support pKVM, ABL is expected to describe the region using a reserved memory
92device tree node where both address and size have been properly aligned to the
93page size used by the hypervisor. This single region must include both the pvmfw
94binary image and its configuration data (see below). For example, the following
95node describes a region of size `0x40000` at address `0x80000000`:
96```
97reserved-memory {
98    ...
99    pkvm_guest_firmware {
100        compatible = "linux,pkvm-guest-firmware-memory";
101        reg = <0x0 0x80000000 0x40000>;
102        no-map;
103    }
104}
105```
106
107[ABL-part]: https://source.android.com/docs/core/architecture/bootloader/partitions
108[boot-img]: https://source.android.com/docs/core/architecture/bootloader/boot-image-header
109
110### Configuration Data
111
112As part of the process of loading pvmfw, the loader (typically the Android
113Bootloader, "ABL") is expected to pass device-specific pvmfw configuration data
114by appending it to the pvmfw binary and including it in the region passed to the
115hypervisor. As a result, the hypervisor will give the same protection to this
116data as it does to pvmfw and will transparently load it in guest memory, making
117it available to pvmfw at runtime. This enables pvmfw to be kept device-agnostic,
118simplifying its adoption and distribution as a centralized signed binary, while
119also being able to support device-specific details.
120
121The configuration data will be read by pvmfw at the next 4KiB boundary from the
122end of its loaded binary. Even if the pvmfw is position-independent, it will be
123expected for it to also have been loaded at a 4-KiB boundary. As a result, the
124location of the configuration data is implicitly passed to pvmfw and known to it
125at build time.
126
127#### Configuration Data Format
128
129The configuration data is described using the following [header]:
130
131```
132+===============================+
133|          pvmfw.bin            |
134+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
135|  (Padding to 4KiB alignment)  |
136+===============================+ <-- HEAD
137|      Magic (= 0x666d7670)     |
138+-------------------------------+
139|           Version             |
140+-------------------------------+
141|   Total Size = (TAIL - HEAD)  |
142+-------------------------------+
143|            Flags              |
144+-------------------------------+
145|           [Entry 0]           |
146|  offset = (FIRST - HEAD)      |
147|  size = (FIRST_END - FIRST)   |
148+-------------------------------+
149|           [Entry 1]           |
150|  offset = (SECOND - HEAD)     |
151|  size = (SECOND_END - SECOND) |
152+-------------------------------+
153|           [Entry 2]           | <-- Entry 2 is present since version 1.1
154|  offset = (THIRD - HEAD)      |
155|  size = (THIRD_END - THIRD)   |
156+-------------------------------+
157|           [Entry 3]           | <-- Entry 3 is present since version 1.2
158|  offset = (FOURTH - HEAD)     |
159|  size = (FOURTH_END - FOURTH) |
160+-------------------------------+
161|           [Entry 4]           | <-- Entry 4 is present since version 1.3
162|  offset = (FIFTH - HEAD)      |
163|  size = (FIFTH_END - FIFTH)   |
164+-------------------------------+
165|              ...              |
166+-------------------------------+
167|           [Entry n]           |
168+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
169| (Padding to 8-byte alignment) |
170+===============================+ <-- FIRST
171|   {First blob: DICE chain}    |
172+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ <-- FIRST_END
173| (Padding to 8-byte alignment) |
174+===============================+ <-- SECOND
175|       {Second blob: DP}       |
176+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ <-- SECOND_END
177| (Padding to 8-byte alignment) |
178+===============================+ <-- THIRD
179|     {Third blob: VM DTBO}     |
180+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ <-- THIRD_END
181| (Padding to 8-byte alignment) |
182+===============================+ <-- FOURTH
183| {Fourth blob: VM reference DT}|
184+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ <-- FOURTH_END
185| (Padding to 8-byte alignment) |
186+===============================+ <-- FIFTH
187| {Fifth blob: Reserved Memory} |
188+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ <-- FIFTH_END
189| (Padding to 8-byte alignment) |
190+===============================+ <-- FIFTH
191|              ...              |
192+===============================+ <-- TAIL
193```
194
195Where the version number is encoded using a "`major.minor`" as follows
196
197```
198((major << 16) | (minor & 0xffff))
199```
200
201and defines the format of the header (which may change between major versions),
202its size and, in particular, the expected number of appended blobs. Each blob is
203referred to by its offset in the entry array and may be mandatory or optional
204(as defined by this specification), where missing entries are denoted by a zero
205size. It is therefore not allowed to trim missing optional entries from the end
206of the array. The header uses the endianness of the virtual machine.
207
208The header format itself is agnostic of the internal format of the individual
209blos it refers to.
210
211##### Version 1.0 {#pvmfw-data-v1-0}
212
213In version 1.0, it describes two blobs:
214
215- entry 0 must point to a valid DICE chain handover (see below)
216- entry 1 may point to a [DTBO] to be applied to the pVM device tree. See
217  [debug policy][debug_policy] for an example.
218
219##### Version 1.1 {#pvmfw-data-v1-1}
220
221In version 1.1, a third blob is added.
222
223- entry 2 may point to a [DTBO] that describes VM DA DTBO for
224  [device assignment][device_assignment].
225  pvmfw will provision assigned devices with the VM DTBO.
226
227#### Version 1.2 {#pvmfw-data-v1-2}
228
229In version 1.2, a fourth blob is added.
230
231- entry 3 if present contains the VM reference DT. This defines properties that
232  may be included in the device tree passed to a protected VM. pvmfw validates
233  that if any of these properties is included in the VM's device tree, the
234  property value exactly matches what is in the VM reference DT.
235
236  The bootloader should ensure that the same properties, with the same values,
237  are added under the "/avf/reference" node in the host Android device tree.
238
239  This provides a mechanism to allow configuration information to be securely
240  passed to the VM via the host. pvmfw does not interpret the content of VM
241  reference DT, nor does it apply it to the VM's device tree, it just ensures
242  that if matching properties are present in the VM device tree they contain the
243  correct values.
244
245  Use-cases of VM reference DT include:
246
247  - Passing the [public key of the Secretkeeper][secretkeeper_key] HAL
248    implementation to each VM.
249
250  - Passing the [vendor hashtree digest][vendor_hashtree_digest] to run
251    Microdroid with verified vendor image.
252
253[header]: src/config.rs
254[DTBO]: https://android.googlesource.com/platform/external/dtc/+/refs/heads/main/Documentation/dt-object-internal.txt
255[debug_policy]: ../../docs/debug/README.md#debug-policy
256[device_assignment]: ../../docs/device_assignment.md
257[secretkeeper_key]: https://android.googlesource.com/platform/system/secretkeeper/+/refs/heads/main/README.md#secretkeeper-public-key
258[vendor_hashtree_digest]: ../../build/microdroid/README.md#verification-of-vendor-image
259
260#### Version 1.3 {#pvmfw-data-v1-3}
261
262In version 1.3, a fifth blob is added.
263
264- entry 4, if present, contains potentially confidential data to be passed to
265  specific guests identified from their VM name. If the data is confidential,
266  this feature should only be used with guests using a fixed rollback
267  protection mechanism to prevent rollback attacks from a malicious host. Data
268  is passed as a reserved-memory region through the device tree with the
269  provided properties at an address which is implementation defined. Multiple
270  regions may be passed to the same guest. The format is as follows.
271
272  ```rust
273  #[repr(C)]
274  struct ReservedMemConfigEntry<const N: usize> {
275    /// The number of headers contained in this blob.
276    count: u32,
277    /// The [reserved memory headers](src/reserved_mem.rs) describing the passed data.
278    headers: [RMemHeader; N]
279    /// The actual data being passed. The reserved memory headers point to
280    /// offsets within this array.
281    data: [u8],
282  }
283  ```
284
285#### Virtual Platform DICE Chain Handover
286
287The format of the DICE chain entry mentioned above, compatible with the
288[`AndroidDiceHandover`][AndroidDiceHandover] defined by the Open Profile for
289DICE reference implementation, is described by the following [CDDL][CDDL]:
290```
291PvmfwDiceHandover = {
292  1 : bstr .size 32,     ; CDI_Attest
293  2 : bstr .size 32,     ; CDI_Seal
294  3 : DiceCertChain,     ; Android DICE chain
295}
296```
297
298It contains the _Compound Device Identifiers_ (CDIs), used for deriving the
299next-stage secret, and a certificate chain, necessary for building the full
300[pVM DICE chain][pvm-dice-chain] required by features like
301[pVM remote attestation][vm-attestation].
302
303Note that it differs from the `AndroidDiceHandover` defined by the specification
304in that its `DiceCertChain` field is mandatory (while optional in the original).
305
306Devices that fully implement DICE should provide a certificate rooted at the
307Unique Device Secret (UDS) in a boot stage preceding the pvmfw loader (typically
308ABL), in such a way that it would receive a valid `AndroidDiceHandover`, that
309can be passed to [`DiceAndroidHandoverMainFlow`][DiceAndroidHandoverMainFlow] along with
310the inputs described below.
311
312The recommended DICE inputs at this stage are:
313
314- **Code**: hash of the pvmfw image, hypervisor (`boot.img`), and other target
315  code relevant to the secure execution of pvmfw (_e.g._ `vendor_boot.img`)
316- **Configuration Data**: any extra input relevant to pvmfw security
317- **Authority Data**: must cover all the public keys used to sign and verify the
318  code contributing to the **Code** input
319- **Mode Decision**: Set according to the [specification][dice-mode]. In
320  particular, should only be `Normal` if secure boot is being properly enforced
321  (_e.g._ locked device in [Android Verified Boot][AVB])
322- **Hidden Inputs**: Factory Reset Secret (FRS, stored in a tamper evident
323  storage and changes during every factory reset) or similar that changes as
324  part of the device lifecycle (_e.g._ reset)
325
326The resulting `AndroidDiceHandover` is then used by pvmfw in a similar way to
327derive another [DICE layer][Layering], passed to the guest through a
328`/reserved-memory` device tree node marked as
329[`compatible=”google,open-dice”`][dice-dt].
330
331[AVB]: https://source.android.com/docs/security/features/verifiedboot/boot-flow
332[AndroidDiceHandover]: https://pigweed.googlesource.com/open-dice/+/42ae7760023/src/android.c#212
333[DiceAndroidHandoverMainFlow]: https://pigweed.googlesource.com/open-dice/+/42ae7760023/src/android.c#221
334[CDDL]: https://datatracker.ietf.org/doc/rfc8610
335[dice-mode]: https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md#Mode-Value-Details
336[dice-dt]: https://www.kernel.org/doc/Documentation/devicetree/bindings/reserved-memory/google%2Copen-dice.yaml
337[Layering]: https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md#layering-details
338[pvm-dice-chain]: ../../docs/pvm_dice_chain.md
339[vm-attestation]: ../../docs/vm_remote_attestation.md
340
341### Platform Requirements
342
343pvmfw is intended to run in a virtualized environment according to the `crosvm`
344[memory layout][crosvm-mem] for protected VMs and so it expects to have been
345loaded at address `0x7fc0_0000` and uses the 2MiB region at address
346`0x7fe0_0000` as scratch memory. It makes use of the virtual PCI bus to obtain a
347virtio interface to the host and prints its logs through the 16550 UART (address
348`0x3f8`).
349
350At boot, pvmfw discovers the running hypervisor in order to select the
351appropriate hypervisor calls to share/unshare memory, mark IPA regions as MMIO,
352obtain trusted true entropy, and reboot the virtual machine. In particular, it
353makes use of the following hypervisor calls:
354
355- Arm [SMC Calling Convention][smccc] v1.1 or above:
356
357    - `SMCCC_VERSION`
358    - Vendor Specific Hypervisor Service Call UID Query
359
360- Arm [Power State Coordination Interface][psci] v1.0 or above:
361
362    - `PSCI_VERSION`
363    - `PSCI_FEATURES`
364    - `PSCI_SYSTEM_RESET`
365    - `PSCI_SYSTEM_SHUTDOWN`
366
367- Arm [True Random Number Generator Firmware Interface][smccc-trng] v1.0:
368
369    - `TRNG_VERSION`
370    - `TRNG_FEATURES`
371    - `TRNG_RND`
372
373- When running under KVM, the pKVM-specific hypervisor interface must provide:
374
375    - `MEMINFO` (function ID `0xc6000002`)
376    - `MEM_SHARE` (function ID `0xc6000003`)
377    - `MEM_UNSHARE` (function ID `0xc6000004`)
378    - `MMIO_GUARD_INFO` (function ID `0xc6000005`)
379    - `MMIO_GUARD_ENROLL` (function ID `0xc6000006`)
380    - `MMIO_GUARD_MAP` (function ID `0xc6000007`)
381    - `MMIO_GUARD_UNMAP` (function ID `0xc6000008`)
382
383[crosvm-mem]: https://crosvm.dev/book/appendix/memory_layout.html
384[psci]: https://developer.arm.com/documentation/den0022
385[smccc]: https://developer.arm.com/documentation/den0028
386[smccc-trng]: https://developer.arm.com/documentation/den0098
387
388## Booting Protected Virtual Machines
389
390### Boot Protocol
391
392As the hypervisor makes pvmfw the entry point of the VM, the initial value of
393the registers it receives is configured by the VMM and is expected to follow the
394[Linux ABI] _i.e._
395
396- x0 = physical address of device tree blob (dtb) in system RAM.
397- x1 = 0 (reserved for future use)
398- x2 = 0 (reserved for future use)
399- x3 = 0 (reserved for future use)
400
401Images to be verified, which have been loaded to guest memory by the VMM prior
402to booting the VM, are described to pvmfw using the device tree (x0):
403
404- the kernel in the `/config` DT node _e.g._
405
406    ```
407    / {
408        config {
409            kernel-address = <0x80200000>;
410            kernel-size = <0x1000000>;
411        };
412    };
413    ````
414
415- the (optional) ramdisk in the standard `/chosen` node _e.g._
416
417    ```
418    / {
419        chosen {
420            linux,initrd-start = <0x82000000>;
421            linux,initrd-end = <0x82800000>;
422        };
423    };
424    ```
425
426[Linux ABI]: https://www.kernel.org/doc/Documentation/arm64/booting.txt
427
428### Handover ABI
429
430After verifying the guest kernel, pvmfw boots it using the Linux ABI described
431above. It uses the device tree to pass [AVF-specific properties][dt.md] and the
432DICE chain:
433
434```
435/ {
436    reserved-memory {
437        #address-cells = <0x02>;
438        #size-cells = <0x02>;
439        ranges;
440        dice {
441            compatible = "google,open-dice";
442            no-map;
443            reg = <0x0 0x7fe0000>, <0x0 0x1000>;
444        };
445    };
446};
447```
448
449[dt.md]: ../../docs/device_trees.md#avf_specific-properties-and-nodes
450
451### Guest Image Signing
452
453pvmfw verifies the guest kernel image (loaded by the VMM) by re-using tools and
454formats introduced by the Android Verified Boot. In particular, it expects the
455kernel region (see `/config/kernel-{address,size}` described above) to contain
456an appended VBMeta structure, which can be generated as follows:
457
458```
459avbtool add_hash_footer --image <kernel.bin> \
460    --partition_name boot \
461    --dynamic_partition_size \
462    --key $KEY
463```
464
465In cases where a ramdisk is required by the guest, pvmfw must also verify it. To
466do so, it must be covered by a hash descriptor in the VBMeta of the kernel:
467
468```
469cp <initrd.bin> /tmp/
470avbtool add_hash_footer --image /tmp/<initrd.bin> \
471    --partition_name $INITRD_NAME \
472    --dynamic_partition_size \
473    --key $KEY
474avbtool add_hash_footer --image <kernel.bin> \
475    --partition_name boot \
476    --dynamic_partition_size \
477    --include_descriptor_from_image /tmp/<initrd.bin> \
478    --key $KEY
479```
480
481Note that the `/tmp/<initrd.bin>` file is only created to temporarily hold the
482hash descriptor to be added to the kernel footer and that the unsigned
483`<initrd.bin>` should be passed to the VMM when booting a pVM.
484
485The name of the AVB "partition" for the ramdisk (`$INITRD_NAME`) can be used by
486the signer to specify if pvmfw must consider the guest to be debuggable
487(`initrd_debug`) or not (`initrd_normal`), which will be reflected in the
488certificate of the guest and will affect the secrets being provisioned.
489
490If pVM guest kernels are built and/or packaged using the Android Build system,
491the signing described above is recommended to be done through an
492`avb_add_hash_footer` Soong module (see [how we sign the Microdroid
493kernel][soong-udroid]).
494
495[soong-udroid]: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Virtualization/microdroid/Android.bp;l=425;drc=b94a5cf516307c4279f6c16a63803527a8affc6d
496
497#### VBMeta Properties
498
499AVF defines special keys for AVB VBMeta descriptor properties that pvmfw
500recognizes, allowing VM owners to ensure that pvmfw performs its role in a way
501that is compatible with their guest kernel. These are:
502
503- `"com.android.virt.cap"`: a `|`-separated list of "capabilities" from
504  - `remote_attest`: pvmfw uses a hard-coded index for rollback protection
505  - `secretkeeper_protection`: pvmfw defers rollback protection to the guest
506  - `supports_uefi_boot`: pvmfw boots the VM as a EFI payload (experimental)
507  - `trusty_security_vm`: pvmfw skips rollback protection
508- `"com.android.virt.page_size"`: (optional) the guest page size in KiB, defaults to 4
509- `"com.android.virt.name"`: (optional) VM name, used as the
510  [`component_name`][dice-comp-name] (defaults to `"vm_entry"`) in the guest
511  DICE certificate and to identify special VMs
512
513[dice-comp-name]: https://cs.android.com/android/platform/superproject/main/+/main:external/open-dice/docs/android.md;l=81;drc=6d511e9533eac05d64d47fcd78ac5d881e72c3de
514
515## Development
516
517For faster iteration, you can build pvmfw, adb-push it to the device, and use
518it directly for a new pVM, without having to flash it to the physical
519partition. To do that, the binary image composition performed by ABL described
520above must be replicated to produce a single file containing the pvmfw binary
521and its configuration data.
522
523As a quick prototyping solution, a valid DICE chain (such as this [test
524file][dice.dat]) can be appended to the `pvmfw.bin` image with `pvmfw-tool`.
525
526```shell
527m pvmfw-tool pvmfw_bin
528PVMFW_BIN=${ANDROID_PRODUCT_OUT}/system/etc/pvmfw.bin
529DICE=${ANDROID_BUILD_TOP}/packages/modules/Virtualization/tests/pvmfw/assets/dice.dat
530
531pvmfw-tool custom_pvmfw ${PVMFW_BIN} ${DICE}
532```
533
534The result can then be pushed to the device. Pointing the system property
535`hypervisor.pvmfw.path` to it will cause AVF to use that image as pvmfw:
536
537```shell
538adb push custom_pvmfw /data/local/tmp/pvmfw
539adb root
540adb shell setprop hypervisor.pvmfw.path /data/local/tmp/pvmfw
541```
542
543Then run a protected VM, for example:
544
545```shell
546adb shell /apex/com.android.virt/bin/vm run-microdroid --protected
547```
548
549Note: `adb root` is required to set the system property.
550
551[dice.dat]: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Virtualization/tests/pvmfw/assets/dice.dat
552
553### Running pVM without pvmfw
554
555Sometimes, it might be useful to start a pVM without pvmfw, e.g. when debugging
556early pVM boot issues. You can achieve that by setting `hypervisor.pvmfw.path`
557propety to the value `none`:
558
559```shell
560adb shell 'setprop hypervisor.pvmfw.path "none"'
561```
562
563Then run a protected VM:
564
565```shell
566adb shell /apex/com.android.virt/bin/vm run-microdroid --protected
567```
568