• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.microdroid.test;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 import static com.google.common.truth.TruthJUnit.assume;
20 
21 import static org.junit.Assume.assumeNoException;
22 
23 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
24 
25 import android.content.Context;
26 import android.os.Build;
27 import android.os.ParcelFileDescriptor;
28 import android.os.SystemProperties;
29 import android.sysprop.HypervisorProperties;
30 import android.system.virtualmachine.VirtualMachine;
31 import android.system.virtualmachine.VirtualMachineCallback;
32 import android.system.virtualmachine.VirtualMachineConfig;
33 import android.system.virtualmachine.VirtualMachineConfig.DebugLevel;
34 import android.system.virtualmachine.VirtualMachineException;
35 import android.system.virtualmachine.VirtualMachineManager;
36 
37 import androidx.annotation.CallSuper;
38 import androidx.test.core.app.ApplicationProvider;
39 
40 import com.android.microdroid.testservice.ITestService;
41 
42 import org.junit.After;
43 import org.junit.Before;
44 import org.junit.Rule;
45 import org.junit.Test;
46 import org.junit.rules.Timeout;
47 import org.junit.runner.RunWith;
48 import org.junit.runners.Parameterized;
49 
50 import java.io.ByteArrayInputStream;
51 import java.io.File;
52 import java.io.IOException;
53 import java.io.RandomAccessFile;
54 import java.nio.file.Files;
55 import java.util.List;
56 import java.util.OptionalLong;
57 import java.util.UUID;
58 import java.util.concurrent.CompletableFuture;
59 import java.util.concurrent.ExecutorService;
60 import java.util.concurrent.Executors;
61 import java.util.concurrent.TimeUnit;
62 
63 import co.nstant.in.cbor.CborDecoder;
64 import co.nstant.in.cbor.CborException;
65 import co.nstant.in.cbor.model.Array;
66 import co.nstant.in.cbor.model.DataItem;
67 import co.nstant.in.cbor.model.MajorType;
68 
69 @RunWith(Parameterized.class)
70 public class MicrodroidTests {
71     @Rule public Timeout globalTimeout = Timeout.seconds(300);
72 
73     private static final String KERNEL_VERSION = SystemProperties.get("ro.kernel.version");
74     private static final String PRODUCT_NAME = SystemProperties.get("ro.product.name");
75 
76     private static class Inner {
77         public boolean mProtectedVm;
78         public Context mContext;
79         public VirtualMachineManager mVmm;
80         public VirtualMachine mVm;
81 
Inner(boolean protectedVm)82         Inner(boolean protectedVm) {
83             mProtectedVm = protectedVm;
84         }
85 
86         /** Create a new VirtualMachineConfig.Builder with the parameterized protection mode. */
newVmConfigBuilder(String payloadConfigPath)87         public VirtualMachineConfig.Builder newVmConfigBuilder(String payloadConfigPath) {
88             return new VirtualMachineConfig.Builder(mContext, payloadConfigPath)
89                             .protectedVm(mProtectedVm);
90         }
91     }
92 
93     @Parameterized.Parameters(name = "protectedVm={0}")
protectedVmConfigs()94     public static Object[] protectedVmConfigs() {
95         return new Object[] { false, true };
96     }
97 
98     @Parameterized.Parameter
99     public boolean mProtectedVm;
100 
101     private boolean mPkvmSupported = false;
102     private Inner mInner;
103 
104     @Before
setup()105     public void setup() {
106         // In case when the virt APEX doesn't exist on the device, classes in the
107         // android.system.virtualmachine package can't be loaded. Therefore, before using the
108         // classes, check the existence of a class in the package and skip this test if not exist.
109         try {
110             Class.forName("android.system.virtualmachine.VirtualMachineManager");
111             mPkvmSupported = true;
112         } catch (ClassNotFoundException e) {
113             assumeNoException(e);
114             return;
115         }
116         if (mProtectedVm) {
117             assume()
118                 .withMessage("Skip where protected VMs aren't support")
119                 .that(HypervisorProperties.hypervisor_protected_vm_supported().orElse(false))
120                 .isTrue();
121         } else {
122             assume()
123                 .withMessage("Skip where VMs aren't support")
124                 .that(HypervisorProperties.hypervisor_vm_supported().orElse(false))
125                 .isTrue();
126         }
127         mInner = new Inner(mProtectedVm);
128         mInner.mContext = ApplicationProvider.getApplicationContext();
129         mInner.mVmm = VirtualMachineManager.getInstance(mInner.mContext);
130     }
131 
132     @After
cleanup()133     public void cleanup() throws VirtualMachineException {
134         if (!mPkvmSupported) {
135             return;
136         }
137         if (mInner == null) {
138             return;
139         }
140         if (mInner.mVm == null) {
141             return;
142         }
143         mInner.mVm.stop();
144         mInner.mVm.delete();
145     }
146 
isCuttlefish()147     private boolean isCuttlefish() {
148         return (null != PRODUCT_NAME)
149                && (PRODUCT_NAME.startsWith("aosp_cf_x86")
150                        || PRODUCT_NAME.startsWith("aosp_cf_arm")
151                        || PRODUCT_NAME.startsWith("cf_x86")
152                        || PRODUCT_NAME.startsWith("cf_arm"));
153     }
154 
155     private abstract static class VmEventListener implements VirtualMachineCallback {
156         private ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
157 
runToFinish(VirtualMachine vm)158         void runToFinish(VirtualMachine vm) throws VirtualMachineException, InterruptedException {
159             vm.setCallback(mExecutorService, this);
160             vm.run();
161             mExecutorService.awaitTermination(300, TimeUnit.SECONDS);
162         }
163 
forceStop(VirtualMachine vm)164         void forceStop(VirtualMachine vm) {
165             try {
166                 vm.clearCallback();
167                 vm.stop();
168                 mExecutorService.shutdown();
169             } catch (VirtualMachineException e) {
170                 throw new RuntimeException(e);
171             }
172         }
173 
174         @Override
onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream)175         public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {}
176 
177         @Override
onPayloadReady(VirtualMachine vm)178         public void onPayloadReady(VirtualMachine vm) {}
179 
180         @Override
onPayloadFinished(VirtualMachine vm, int exitCode)181         public void onPayloadFinished(VirtualMachine vm, int exitCode) {}
182 
183         @Override
onError(VirtualMachine vm, int errorCode, String message)184         public void onError(VirtualMachine vm, int errorCode, String message) {}
185 
186         @Override
187         @CallSuper
onDied(VirtualMachine vm, @DeathReason int reason)188         public void onDied(VirtualMachine vm, @DeathReason int reason) {
189             mExecutorService.shutdown();
190         }
191     }
192 
193     private static final int MIN_MEM_ARM64 = 145;
194     private static final int MIN_MEM_X86_64 = 196;
195 
196     @Test
connectToVmService()197     public void connectToVmService() throws VirtualMachineException, InterruptedException {
198         assume()
199             .withMessage("SKip on 5.4 kernel. b/218303240")
200             .that(KERNEL_VERSION)
201             .isNotEqualTo("5.4");
202 
203         VirtualMachineConfig.Builder builder =
204                 mInner.newVmConfigBuilder("assets/vm_config_extra_apk.json");
205         if (Build.SUPPORTED_ABIS.length > 0) {
206             String primaryAbi = Build.SUPPORTED_ABIS[0];
207             switch(primaryAbi) {
208                 case "x86_64":
209                     builder.memoryMib(MIN_MEM_X86_64);
210                     break;
211                 case "arm64-v8a":
212                     builder.memoryMib(MIN_MEM_ARM64);
213                     break;
214             }
215         }
216         VirtualMachineConfig config = builder.build();
217 
218         mInner.mVm = mInner.mVmm.getOrCreate("test_vm_extra_apk", config);
219 
220         class TestResults {
221             Exception mException;
222             Integer mAddInteger;
223             String mAppRunProp;
224             String mSublibRunProp;
225             String mExtraApkTestProp;
226         }
227         final CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>();
228         final CompletableFuture<Boolean> payloadReady = new CompletableFuture<>();
229         final TestResults testResults = new TestResults();
230         VmEventListener listener =
231                 new VmEventListener() {
232                     private void testVMService(VirtualMachine vm) {
233                         try {
234                             ITestService testService = ITestService.Stub.asInterface(
235                                     vm.connectToVsockServer(ITestService.SERVICE_PORT).get());
236                             testResults.mAddInteger = testService.addInteger(123, 456);
237                             testResults.mAppRunProp =
238                                     testService.readProperty("debug.microdroid.app.run");
239                             testResults.mSublibRunProp =
240                                     testService.readProperty("debug.microdroid.app.sublib.run");
241                             testResults.mExtraApkTestProp =
242                                     testService.readProperty("debug.microdroid.test.extra_apk");
243                         } catch (Exception e) {
244                             testResults.mException = e;
245                         }
246                     }
247 
248                     @Override
249                     public void onPayloadReady(VirtualMachine vm) {
250                         payloadReady.complete(true);
251                         testVMService(vm);
252                         forceStop(vm);
253                     }
254 
255                     @Override
256                     public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {
257                         payloadStarted.complete(true);
258                     }
259                 };
260         listener.runToFinish(mInner.mVm);
261         assertThat(payloadStarted.getNow(false)).isTrue();
262         assertThat(payloadReady.getNow(false)).isTrue();
263         assertThat(testResults.mException).isNull();
264         assertThat(testResults.mAddInteger).isEqualTo(123 + 456);
265         assertThat(testResults.mAppRunProp).isEqualTo("true");
266         assertThat(testResults.mSublibRunProp).isEqualTo("true");
267         assertThat(testResults.mExtraApkTestProp).isEqualTo("PASS");
268     }
269 
270     @Test
changingDebugLevelInvalidatesVmIdentity()271     public void changingDebugLevelInvalidatesVmIdentity()
272             throws VirtualMachineException, InterruptedException, IOException {
273         assume()
274             .withMessage("SKip on 5.4 kernel. b/218303240")
275             .that(KERNEL_VERSION)
276             .isNotEqualTo("5.4");
277 
278         VirtualMachineConfig.Builder builder = mInner.newVmConfigBuilder("assets/vm_config.json");
279         VirtualMachineConfig normalConfig = builder.debugLevel(DebugLevel.NONE).build();
280         mInner.mVm = mInner.mVmm.getOrCreate("test_vm", normalConfig);
281         VmEventListener listener =
282                 new VmEventListener() {
283                     @Override
284                     public void onPayloadReady(VirtualMachine vm) {
285                         forceStop(vm);
286                     }
287                 };
288         listener.runToFinish(mInner.mVm);
289 
290         // Launch the same VM with different debug level. The Java API prohibits this (thankfully).
291         // For testing, we do that by creating another VM with debug level, and copy the config file
292         // from the new VM directory to the old VM directory.
293         VirtualMachineConfig debugConfig = builder.debugLevel(DebugLevel.FULL).build();
294         VirtualMachine newVm  = mInner.mVmm.getOrCreate("test_debug_vm", debugConfig);
295         File vmRoot = new File(mInner.mContext.getFilesDir(), "vm");
296         File newVmConfig = new File(new File(vmRoot, "test_debug_vm"), "config.xml");
297         File oldVmConfig = new File(new File(vmRoot, "test_vm"), "config.xml");
298         Files.copy(newVmConfig.toPath(), oldVmConfig.toPath(), REPLACE_EXISTING);
299         newVm.delete();
300         mInner.mVm = mInner.mVmm.get("test_vm"); // re-load with the copied-in config file.
301         final CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>();
302         listener =
303                 new VmEventListener() {
304                     @Override
305                     public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {
306                         payloadStarted.complete(true);
307                         forceStop(vm);
308                     }
309                 };
310         listener.runToFinish(mInner.mVm);
311         assertThat(payloadStarted.getNow(false)).isFalse();
312     }
313 
314     private class VmCdis {
315         public byte[] cdiAttest;
316         public byte[] cdiSeal;
317     }
318 
launchVmAndGetCdis(String instanceName)319     private VmCdis launchVmAndGetCdis(String instanceName)
320             throws VirtualMachineException, InterruptedException {
321         VirtualMachineConfig normalConfig = mInner.newVmConfigBuilder("assets/vm_config.json")
322                 .debugLevel(DebugLevel.NONE)
323                 .build();
324         mInner.mVm = mInner.mVmm.getOrCreate(instanceName, normalConfig);
325         final VmCdis vmCdis = new VmCdis();
326         final CompletableFuture<Exception> exception = new CompletableFuture<>();
327         VmEventListener listener =
328                 new VmEventListener() {
329                     @Override
330                     public void onPayloadReady(VirtualMachine vm) {
331                         try {
332                             ITestService testService = ITestService.Stub.asInterface(
333                                     vm.connectToVsockServer(ITestService.SERVICE_PORT).get());
334                             vmCdis.cdiAttest = testService.insecurelyExposeAttestationCdi();
335                             vmCdis.cdiSeal = testService.insecurelyExposeSealingCdi();
336                             forceStop(vm);
337                         } catch (Exception e) {
338                             exception.complete(e);
339                         }
340                     }
341                 };
342         listener.runToFinish(mInner.mVm);
343         assertThat(exception.getNow(null)).isNull();
344         return vmCdis;
345     }
346 
347     @Test
instancesOfSameVmHaveDifferentCdis()348     public void instancesOfSameVmHaveDifferentCdis()
349             throws VirtualMachineException, InterruptedException {
350         assume()
351             .withMessage("SKip on 5.4 kernel. b/218303240")
352             .that(KERNEL_VERSION)
353             .isNotEqualTo("5.4");
354 
355         VmCdis vm_a_cdis = launchVmAndGetCdis("test_vm_a");
356         VmCdis vm_b_cdis = launchVmAndGetCdis("test_vm_b");
357         assertThat(vm_a_cdis.cdiAttest).isNotNull();
358         assertThat(vm_b_cdis.cdiAttest).isNotNull();
359         assertThat(vm_a_cdis.cdiAttest).isNotEqualTo(vm_b_cdis.cdiAttest);
360         assertThat(vm_a_cdis.cdiSeal).isNotNull();
361         assertThat(vm_b_cdis.cdiSeal).isNotNull();
362         assertThat(vm_a_cdis.cdiSeal).isNotEqualTo(vm_b_cdis.cdiSeal);
363         assertThat(vm_a_cdis.cdiAttest).isNotEqualTo(vm_b_cdis.cdiSeal);
364     }
365 
366     @Test
sameInstanceKeepsSameCdis()367     public void sameInstanceKeepsSameCdis()
368             throws VirtualMachineException, InterruptedException {
369         assume()
370             .withMessage("SKip on 5.4 kernel. b/218303240")
371             .that(KERNEL_VERSION)
372             .isNotEqualTo("5.4");
373         assume().withMessage("Skip on CF. Too Slow. b/257270529").that(isCuttlefish()).isFalse();
374 
375         VmCdis first_boot_cdis = launchVmAndGetCdis("test_vm");
376         VmCdis second_boot_cdis = launchVmAndGetCdis("test_vm");
377         // The attestation CDI isn't specified to be stable, though it might be
378         assertThat(first_boot_cdis.cdiSeal).isNotNull();
379         assertThat(second_boot_cdis.cdiSeal).isNotNull();
380         assertThat(first_boot_cdis.cdiSeal).isEqualTo(second_boot_cdis.cdiSeal);
381     }
382 
383     @Test
bccIsSuperficiallyWellFormed()384     public void bccIsSuperficiallyWellFormed()
385             throws VirtualMachineException, InterruptedException, CborException {
386         assume()
387             .withMessage("SKip on 5.4 kernel. b/218303240")
388             .that(KERNEL_VERSION)
389             .isNotEqualTo("5.4");
390 
391         VirtualMachineConfig normalConfig = mInner.newVmConfigBuilder("assets/vm_config.json")
392                 .debugLevel(DebugLevel.NONE)
393                 .build();
394         mInner.mVm = mInner.mVmm.getOrCreate("bcc_vm", normalConfig);
395         final VmCdis vmCdis = new VmCdis();
396         final CompletableFuture<byte[]> bcc = new CompletableFuture<>();
397         final CompletableFuture<Exception> exception = new CompletableFuture<>();
398         VmEventListener listener =
399                 new VmEventListener() {
400                     @Override
401                     public void onPayloadReady(VirtualMachine vm) {
402                         try {
403                             ITestService testService = ITestService.Stub.asInterface(
404                                     vm.connectToVsockServer(ITestService.SERVICE_PORT).get());
405                             bcc.complete(testService.getBcc());
406                             forceStop(vm);
407                         } catch (Exception e) {
408                             exception.complete(e);
409                         }
410                     }
411                 };
412         listener.runToFinish(mInner.mVm);
413         byte[] bccBytes = bcc.getNow(null);
414         assertThat(exception.getNow(null)).isNull();
415         assertThat(bccBytes).isNotNull();
416 
417         ByteArrayInputStream bais = new ByteArrayInputStream(bccBytes);
418         List<DataItem> dataItems = new CborDecoder(bais).decode();
419         assertThat(dataItems.size()).isEqualTo(1);
420         assertThat(dataItems.get(0).getMajorType()).isEqualTo(MajorType.ARRAY);
421         List<DataItem> rootArrayItems = ((Array) dataItems.get(0)).getDataItems();
422         assertThat(rootArrayItems.size()).isAtLeast(2); // Public key and one certificate
423         if (mProtectedVm) {
424             // When a true BCC is created, microdroid expects entries for at least: the root public
425             // key, pvmfw, u-boot, u-boot-env, microdroid, app payload and the service process.
426             assertThat(rootArrayItems.size()).isAtLeast(7);
427         }
428     }
429 
430     private static final UUID MICRODROID_PARTITION_UUID =
431             UUID.fromString("cf9afe9a-0662-11ec-a329-c32663a09d75");
432     private static final UUID U_BOOT_AVB_PARTITION_UUID =
433             UUID.fromString("7e8221e7-03e6-4969-948b-73a4c809a4f2");
434     private static final UUID U_BOOT_ENV_PARTITION_UUID =
435             UUID.fromString("0ab72d30-86ae-4d05-81b2-c1760be2b1f9");
436     private static final UUID PVM_FW_PARTITION_UUID =
437             UUID.fromString("90d2174a-038a-4bc6-adf3-824848fc5825");
438     private static final long BLOCK_SIZE = 512;
439 
440     // Find the starting offset which holds the data of a partition having UUID.
441     // This is a kind of hack; rather than parsing QCOW2 we exploit the fact that the cluster size
442     // is normally greater than 512. It implies that the partition data should exist at a block
443     // which follows the header block
findPartitionDataOffset(RandomAccessFile file, UUID uuid)444     private OptionalLong findPartitionDataOffset(RandomAccessFile file, UUID uuid)
445             throws IOException {
446         // For each 512-byte block in file, check header
447         long fileSize = file.length();
448 
449         for (long idx = 0; idx + BLOCK_SIZE < fileSize; idx += BLOCK_SIZE) {
450             file.seek(idx);
451             long high = file.readLong();
452             long low = file.readLong();
453             if (uuid.equals(new UUID(high, low))) return OptionalLong.of(idx + BLOCK_SIZE);
454         }
455         return OptionalLong.empty();
456     }
457 
flipBit(RandomAccessFile file, long offset)458     private void flipBit(RandomAccessFile file, long offset) throws IOException {
459         file.seek(offset);
460         int b = file.readByte();
461         file.seek(offset);
462         file.writeByte(b ^ 1);
463     }
464 
tryBootVm(String vmName)465     private boolean tryBootVm(String vmName)
466             throws VirtualMachineException, InterruptedException {
467         mInner.mVm = mInner.mVmm.get(vmName); // re-load the vm before running tests
468         final CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>();
469         VmEventListener listener =
470                 new VmEventListener() {
471                     @Override
472                     public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {
473                         payloadStarted.complete(true);
474                         forceStop(vm);
475                     }
476                 };
477         listener.runToFinish(mInner.mVm);
478         return payloadStarted.getNow(false);
479     }
480 
prepareInstanceImage(String vmName)481     private RandomAccessFile prepareInstanceImage(String vmName)
482             throws VirtualMachineException, InterruptedException, IOException {
483         VirtualMachineConfig config = mInner.newVmConfigBuilder("assets/vm_config.json")
484                 .debugLevel(DebugLevel.NONE)
485                 .build();
486 
487         // Remove any existing VM so we can start from scratch
488         VirtualMachine oldVm = mInner.mVmm.getOrCreate(vmName, config);
489         oldVm.delete();
490         mInner.mVmm.getOrCreate(vmName, config);
491 
492         assertThat(tryBootVm(vmName)).isTrue();
493 
494         File vmRoot = new File(mInner.mContext.getFilesDir(), "vm");
495         File vmDir = new File(vmRoot, vmName);
496         File instanceImgPath = new File(vmDir, "instance.img");
497         return new RandomAccessFile(instanceImgPath, "rw");
498 
499     }
500 
assertThatPartitionIsMissing(UUID partitionUuid)501     private void assertThatPartitionIsMissing(UUID partitionUuid)
502             throws VirtualMachineException, InterruptedException, IOException {
503         RandomAccessFile instanceFile = prepareInstanceImage("test_vm_integrity");
504         assertThat(findPartitionDataOffset(instanceFile, partitionUuid).isPresent())
505                 .isFalse();
506     }
507 
508     // Flips a bit of given partition, and then see if boot fails.
assertThatBootFailsAfterCompromisingPartition(UUID partitionUuid)509     private void assertThatBootFailsAfterCompromisingPartition(UUID partitionUuid)
510             throws VirtualMachineException, InterruptedException, IOException {
511         RandomAccessFile instanceFile = prepareInstanceImage("test_vm_integrity");
512         OptionalLong offset = findPartitionDataOffset(instanceFile, partitionUuid);
513         assertThat(offset.isPresent()).isTrue();
514 
515         flipBit(instanceFile, offset.getAsLong());
516         assertThat(tryBootVm("test_vm_integrity")).isFalse();
517     }
518 
519     @Test
bootFailsWhenMicrodroidDataIsCompromised()520     public void bootFailsWhenMicrodroidDataIsCompromised()
521             throws VirtualMachineException, InterruptedException, IOException {
522         assume().withMessage("Skip on CF. Too Slow. b/257270529").that(isCuttlefish()).isFalse();
523 
524         assertThatBootFailsAfterCompromisingPartition(MICRODROID_PARTITION_UUID);
525     }
526 
527     @Test
bootFailsWhenUBootAvbDataIsCompromised()528     public void bootFailsWhenUBootAvbDataIsCompromised()
529             throws VirtualMachineException, InterruptedException, IOException {
530         if (mProtectedVm) {
531             assertThatBootFailsAfterCompromisingPartition(U_BOOT_AVB_PARTITION_UUID);
532         } else {
533             // non-protected VM shouldn't have u-boot avb data
534             assertThatPartitionIsMissing(U_BOOT_AVB_PARTITION_UUID);
535         }
536     }
537 
538     @Test
bootFailsWhenUBootEnvDataIsCompromised()539     public void bootFailsWhenUBootEnvDataIsCompromised()
540             throws VirtualMachineException, InterruptedException, IOException {
541         if (mProtectedVm) {
542             assertThatBootFailsAfterCompromisingPartition(U_BOOT_ENV_PARTITION_UUID);
543         } else {
544             // non-protected VM shouldn't have u-boot env data
545             assertThatPartitionIsMissing(U_BOOT_ENV_PARTITION_UUID);
546         }
547     }
548 
549     @Test
bootFailsWhenPvmFwDataIsCompromised()550     public void bootFailsWhenPvmFwDataIsCompromised()
551             throws VirtualMachineException, InterruptedException, IOException {
552         if (mProtectedVm) {
553             assertThatBootFailsAfterCompromisingPartition(PVM_FW_PARTITION_UUID);
554         } else {
555             // non-protected VM shouldn't have pvmfw data
556             assertThatPartitionIsMissing(PVM_FW_PARTITION_UUID);
557         }
558     }
559 }
560