Don't fall back to unprotected mode if protected is not supported.

Checking the capability on /dev/kvm doesn't work, and in any case if a
client requests a protected VM but it isn't supported we should fail
with an error, not fall back to running it in unprotected mode.

Microdroid clients now need to explicitly choose whether to run in
protected mode or not, the same as raw VM clients.

Test: atest VirtualizationTestCases
Change-Id: If05b871bf83062c1619729ec17eb3342ce5ced30
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index f4b3440..6a35fb0 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -122,6 +122,7 @@
             configPath: config_path.to_owned(),
             debugLevel: debug_level,
             extraIdsigs: vec![idsig_manifest_apk_fd],
+            protectedVm: false,
             memoryMib: VM_MEMORY_MIB,
             numCpus: parameters.cpus.map_or(1, NonZeroU32::get) as i32,
             cpuAffinity: parameters.cpu_set.clone(),
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index 7d1f9b0..d082911 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -51,6 +51,7 @@
     private static final String KEY_APKPATH = "apkPath";
     private static final String KEY_PAYLOADCONFIGPATH = "payloadConfigPath";
     private static final String KEY_DEBUGLEVEL = "debugLevel";
+    private static final String KEY_PROTECTED_VM = "protectedVm";
     private static final String KEY_MEMORY_MIB = "memoryMib";
     private static final String KEY_NUM_CPUS = "numCpus";
     private static final String KEY_CPU_AFFINITY = "cpuAffinity";
@@ -83,6 +84,11 @@
     private final DebugLevel mDebugLevel;
 
     /**
+     * Whether to run the VM in protected mode, so the host can't access its memory.
+     */
+    private final boolean mProtectedVm;
+
+    /**
      * The amount of RAM to give the VM, in MiB. If this is 0 or negative the default will be used.
      */
     private final int mMemoryMib;
@@ -111,6 +117,7 @@
             @NonNull Signature[] certs,
             @NonNull String payloadConfigPath,
             DebugLevel debugLevel,
+            boolean protectedVm,
             int memoryMib,
             int numCpus,
             String cpuAffinity) {
@@ -118,6 +125,7 @@
         mCerts = certs;
         mPayloadConfigPath = payloadConfigPath;
         mDebugLevel = debugLevel;
+        mProtectedVm = protectedVm;
         mMemoryMib = memoryMib;
         mNumCpus = numCpus;
         mCpuAffinity = cpuAffinity;
@@ -149,11 +157,12 @@
             throw new VirtualMachineException("No payloadConfigPath");
         }
         final DebugLevel debugLevel = DebugLevel.values()[b.getInt(KEY_DEBUGLEVEL)];
+        final boolean protectedVm = b.getBoolean(KEY_PROTECTED_VM);
         final int memoryMib = b.getInt(KEY_MEMORY_MIB);
         final int numCpus = b.getInt(KEY_NUM_CPUS);
         final String cpuAffinity = b.getString(KEY_CPU_AFFINITY);
-        return new VirtualMachineConfig(apkPath, certs, payloadConfigPath, debugLevel, memoryMib,
-                numCpus, cpuAffinity);
+        return new VirtualMachineConfig(apkPath, certs, payloadConfigPath, debugLevel, protectedVm,
+                memoryMib, numCpus, cpuAffinity);
     }
 
     /** Persists this config to a stream, for example a file. */
@@ -169,6 +178,7 @@
         b.putStringArray(KEY_CERTS, certs);
         b.putString(KEY_PAYLOADCONFIGPATH, mPayloadConfigPath);
         b.putInt(KEY_DEBUGLEVEL, mDebugLevel.ordinal());
+        b.putBoolean(KEY_PROTECTED_VM, mProtectedVm);
         if (mMemoryMib > 0) {
             b.putInt(KEY_MEMORY_MIB, mMemoryMib);
         }
@@ -219,6 +229,7 @@
                 parcel.debugLevel = VirtualMachineAppConfig.DebugLevel.FULL;
                 break;
         }
+        parcel.protectedVm = mProtectedVm;
         parcel.memoryMib = mMemoryMib;
         parcel.numCpus = mNumCpus;
         parcel.cpuAffinity = mCpuAffinity;
@@ -230,6 +241,7 @@
         private Context mContext;
         private String mPayloadConfigPath;
         private DebugLevel mDebugLevel;
+        private boolean mProtectedVm;
         private int mMemoryMib;
         private int mNumCpus;
         private String mCpuAffinity;
@@ -240,6 +252,7 @@
             mContext = context;
             mPayloadConfigPath = payloadConfigPath;
             mDebugLevel = DebugLevel.NONE;
+            mProtectedVm = false;
             mNumCpus = 1;
             mCpuAffinity = null;
         }
@@ -250,6 +263,12 @@
             return this;
         }
 
+        /** Sets whether to protect the VM memory from the host. Defaults to false. */
+        public Builder protectedVm(boolean protectedVm) {
+            mProtectedVm = protectedVm;
+            return this;
+        }
+
         /**
          * Sets the amount of RAM to give the VM. If this is zero or negative then the default will
          * be used.
@@ -309,8 +328,8 @@
             }
 
             return new VirtualMachineConfig(
-                    apkPath, certs, mPayloadConfigPath, mDebugLevel, mMemoryMib, mNumCpus,
-                    mCpuAffinity);
+                    apkPath, certs, mPayloadConfigPath, mDebugLevel, mProtectedVm, mMemoryMib,
+                    mNumCpus, mCpuAffinity);
         }
     }
 }
diff --git a/microdroid/microdroid.json b/microdroid/microdroid.json
index 0c294e9..cb27a24 100644
--- a/microdroid/microdroid.json
+++ b/microdroid/microdroid.json
@@ -37,6 +37,5 @@
       "writable": true
     }
   ],
-  "memory_mib": 256,
-  "protected": false
+  "memory_mib": 256
 }
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 653524e..b82064b 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -31,7 +31,6 @@
         "libcommand_fds",
         "libdisk",
         "libidsig",
-        "libkvm",
         "liblog_rust",
         "libmicrodroid_metadata",
         "libmicrodroid_payload_config",
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index 8265f96..c36e561 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -47,6 +47,9 @@
     /** Debug level of the VM */
     DebugLevel debugLevel;
 
+    /** Whether the VM should be a protected VM. */
+    boolean protectedVm;
+
     /**
      * The amount of RAM to give the VM, in MiB. If this is 0 or negative then it will default to
      * the value in microdroid.json, if any, or the crosvm default.
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 42eb1e6..5b0c9b7 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -50,7 +50,6 @@
 use binder_common::{lazy_service::LazyServiceGuard, new_binder_exception};
 use disk::QcowFile;
 use idsig::{HashAlgorithm, V4Signature};
-use kvm::{Kvm, Cap};
 use log::{debug, error, info, warn};
 use microdroid_payload_config::VmPayloadConfig;
 use rustutils::system_properties;
@@ -190,9 +189,7 @@
             VirtualMachineConfig::AppConfig(config) => BorrowedOrOwned::Owned(
                 load_app_config(config, &temporary_directory).map_err(|e| {
                     error!("Failed to load app config from {}: {}", &config.configPath, e);
-                    // At this point, we do not know the protected status of Vm
-                    // setting it to false, though this may not be correct.
-                    write_vm_creation_stats(false, false);
+                    write_vm_creation_stats(config.protectedVm, false);
                     new_binder_exception(
                         ExceptionCode::SERVICE_SPECIFIC,
                         format!("Failed to load app config from {}: {}", &config.configPath, e),
@@ -202,7 +199,15 @@
             VirtualMachineConfig::RawConfig(config) => BorrowedOrOwned::Borrowed(config),
         };
         let config = config.as_ref();
-        let protected_vm = config.protectedVm;
+        let protected = config.protectedVm;
+
+        // Debug level FULL is only supported for non-protected VMs.
+        if is_debug_level_full && protected {
+            return Err(new_binder_exception(
+                ExceptionCode::SERVICE_SPECIFIC,
+                "FULL debug level not supported for protected VMs.",
+            ));
+        };
 
         // Check if partition images are labeled incorrectly. This is to prevent random images
         // which are not protected by the Android Verified Boot (e.g. bits downloaded by apps) from
@@ -226,7 +231,7 @@
         let zero_filler_path = temporary_directory.join("zero.img");
         write_zero_filler(&zero_filler_path).map_err(|e| {
             error!("Failed to make composite image: {}", e);
-            write_vm_creation_stats(protected_vm, false);
+            write_vm_creation_stats(protected, false);
             new_binder_exception(
                 ExceptionCode::SERVICE_SPECIFIC,
                 format!("Failed to make composite image: {}", e),
@@ -248,24 +253,6 @@
             })
             .collect::<Result<Vec<DiskFile>, _>>()?;
 
-        let protected_vm_supported = Kvm::new()
-            .map_err(|e| new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, e.to_string()))?
-            .check_extension(Cap::ArmProtectedVm);
-        let protected = config.protectedVm && protected_vm_supported;
-        if config.protectedVm && !protected_vm_supported {
-            warn!("Protected VM was requested, but it isn't supported on this machine. Ignored.");
-        }
-
-        // And force run in non-protected mode when debug level is FULL
-        let protected = if is_debug_level_full {
-            if protected {
-                warn!("VM will run in FULL debug level. Running in non-protected mode");
-            }
-            false
-        } else {
-            protected
-        };
-
         // Actually start the VM.
         let crosvm_config = CrosvmConfig {
             cid,
@@ -292,7 +279,7 @@
             )
             .map_err(|e| {
                 error!("Failed to create VM with config {:?}: {}", config, e);
-                write_vm_creation_stats(protected_vm, false);
+                write_vm_creation_stats(protected, false);
                 new_binder_exception(
                     ExceptionCode::SERVICE_SPECIFIC,
                     format!("Failed to create VM: {}", e),
@@ -300,7 +287,7 @@
             })?,
         );
         state.add_vm(Arc::downgrade(&instance));
-        write_vm_creation_stats(protected_vm, true);
+        write_vm_creation_stats(protected, true);
         Ok(VirtualMachine::create(instance))
     }
 
@@ -587,6 +574,7 @@
         vm_config.memoryMib = config.memoryMib;
     }
 
+    vm_config.protectedVm = config.protectedVm;
     vm_config.numCpus = config.numCpus;
     vm_config.cpuAffinity = config.cpuAffinity.clone();
 
diff --git a/vm/src/main.rs b/vm/src/main.rs
index ad8c201..25f9bfb 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -72,6 +72,10 @@
         #[structopt(long, default_value = "none", parse(try_from_str=parse_debug_level))]
         debug: DebugLevel,
 
+        /// Run VM in protected mode.
+        #[structopt(short, long)]
+        protected: bool,
+
         /// Memory size (in MiB) of the VM. If unspecified, defaults to the value of `memory_mib`
         /// in the VM config file.
         #[structopt(short, long)]
@@ -174,6 +178,7 @@
             console,
             log,
             debug,
+            protected,
             mem,
             cpus,
             cpu_affinity,
@@ -188,6 +193,7 @@
             console.as_deref(),
             log.as_deref(),
             debug,
+            protected,
             mem,
             cpus,
             cpu_affinity,
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 8583fe2..d558add 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -50,6 +50,7 @@
     console_path: Option<&Path>,
     log_path: Option<&Path>,
     debug_level: DebugLevel,
+    protected: bool,
     mem: Option<u32>,
     cpus: Option<u32>,
     cpu_affinity: Option<String>,
@@ -100,6 +101,7 @@
         instanceImage: open_parcel_file(instance, true /* writable */)?.into(),
         configPath: config_path.to_owned(),
         debugLevel: debug_level,
+        protectedVm: protected,
         memoryMib: mem.unwrap_or(0) as i32, // 0 means use the VM default
         numCpus: cpus.unwrap_or(1) as i32,
         cpuAffinity: cpu_affinity,