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 17 //! Responsible for validating and starting an existing instance of the CompOS VM, or creating and 18 //! starting a new instance if necessary. 19 20 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{ 21 IVirtualizationService::IVirtualizationService, PartitionType::PartitionType, 22 }; 23 use anyhow::{anyhow, Context, Result}; 24 use binder::{LazyServiceGuard, ParcelFileDescriptor, Strong}; 25 use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService; 26 use compos_common::compos_client::{ComposClient, VmParameters}; 27 use compos_common::{ 28 COMPOS_DATA_ROOT, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE, IDSIG_MANIFEST_EXT_APK_FILE, 29 INSTANCE_ID_FILE, INSTANCE_IMAGE_FILE, 30 }; 31 use log::info; 32 use std::fs; 33 use std::path::{Path, PathBuf}; 34 use std::sync::Arc; 35 36 pub struct CompOsInstance { 37 service: Strong<dyn ICompOsService>, 38 #[allow(dead_code)] // Keeps VirtualizationService & the VM alive 39 vm_instance: ComposClient, 40 #[allow(dead_code)] // Keeps composd process alive 41 lazy_service_guard: LazyServiceGuard, 42 // Keep this alive as long as we are 43 instance_tracker: Arc<()>, 44 } 45 46 impl CompOsInstance { get_service(&self) -> Strong<dyn ICompOsService>47 pub fn get_service(&self) -> Strong<dyn ICompOsService> { 48 self.service.clone() 49 } 50 51 /// Returns an Arc that this instance holds a strong reference to as long as it exists. This 52 /// can be used to determine when the instance has been dropped. get_instance_tracker(&self) -> &Arc<()>53 pub fn get_instance_tracker(&self) -> &Arc<()> { 54 &self.instance_tracker 55 } 56 57 /// Attempt to shut down the VM cleanly, giving time for any relevant logs to be written. shutdown(self) -> LazyServiceGuard58 pub fn shutdown(self) -> LazyServiceGuard { 59 self.vm_instance.shutdown(self.service); 60 // Return the guard to the caller, since we might be terminated at any point after it is 61 // dropped, and there might still be things to do. 62 self.lazy_service_guard 63 } 64 } 65 66 pub struct InstanceStarter { 67 instance_name: String, 68 instance_root: PathBuf, 69 instance_id_file: PathBuf, 70 instance_image: PathBuf, 71 idsig: PathBuf, 72 idsig_manifest_apk: PathBuf, 73 idsig_manifest_ext_apk: PathBuf, 74 vm_parameters: VmParameters, 75 } 76 77 impl InstanceStarter { new(instance_name: &str, vm_parameters: VmParameters) -> Self78 pub fn new(instance_name: &str, vm_parameters: VmParameters) -> Self { 79 let instance_root = Path::new(COMPOS_DATA_ROOT).join(instance_name); 80 let instance_root_path = instance_root.as_path(); 81 let instance_id_file = instance_root_path.join(INSTANCE_ID_FILE); 82 let instance_image = instance_root_path.join(INSTANCE_IMAGE_FILE); 83 let idsig = instance_root_path.join(IDSIG_FILE); 84 let idsig_manifest_apk = instance_root_path.join(IDSIG_MANIFEST_APK_FILE); 85 let idsig_manifest_ext_apk = instance_root_path.join(IDSIG_MANIFEST_EXT_APK_FILE); 86 Self { 87 instance_name: instance_name.to_owned(), 88 instance_root, 89 instance_id_file, 90 instance_image, 91 idsig, 92 idsig_manifest_apk, 93 idsig_manifest_ext_apk, 94 vm_parameters, 95 } 96 } 97 start_new_instance( &self, virtualization_service: &dyn IVirtualizationService, ) -> Result<CompOsInstance>98 pub fn start_new_instance( 99 &self, 100 virtualization_service: &dyn IVirtualizationService, 101 ) -> Result<CompOsInstance> { 102 info!("Creating {} CompOs instance", self.instance_name); 103 104 fs::create_dir_all(&self.instance_root)?; 105 106 // Overwrite any existing instance - it's unlikely to be valid with the current set 107 // of APEXes, and finding out it isn't is much more expensive than creating a new one. 108 self.create_instance_image(virtualization_service)?; 109 // TODO(b/294177871): Ping VS to delete the old instance's secret. 110 if cfg!(llpvm_changes) { 111 self.allocate_instance_id(virtualization_service)?; 112 } 113 // Delete existing idsig files. Ignore error in case idsig doesn't exist. 114 let _ignored1 = fs::remove_file(&self.idsig); 115 let _ignored2 = fs::remove_file(&self.idsig_manifest_apk); 116 let _ignored3 = fs::remove_file(&self.idsig_manifest_ext_apk); 117 118 let instance = self.start_vm(virtualization_service)?; 119 120 // Retrieve the VM's attestation chain as a BCC and save it in the instance directory. 121 let bcc = instance.service.getAttestationChain().context("Getting attestation chain")?; 122 fs::write(self.instance_root.join("bcc"), bcc).context("Writing BCC")?; 123 124 Ok(instance) 125 } 126 start_vm( &self, virtualization_service: &dyn IVirtualizationService, ) -> Result<CompOsInstance>127 fn start_vm( 128 &self, 129 virtualization_service: &dyn IVirtualizationService, 130 ) -> Result<CompOsInstance> { 131 let instance_id: [u8; 64] = if cfg!(llpvm_changes) { 132 fs::read(&self.instance_id_file)? 133 .try_into() 134 .map_err(|_| anyhow!("Failed to get instance_id"))? 135 } else { 136 [0u8; 64] 137 }; 138 139 let instance_image = fs::OpenOptions::new() 140 .read(true) 141 .write(true) 142 .open(&self.instance_image) 143 .context("Failed to open instance image")?; 144 let vm_instance = ComposClient::start( 145 virtualization_service, 146 instance_id, 147 instance_image, 148 &self.idsig, 149 &self.idsig_manifest_apk, 150 &self.idsig_manifest_ext_apk, 151 &self.vm_parameters, 152 ) 153 .context("Starting VM")?; 154 let service = vm_instance.connect_service().context("Connecting to CompOS")?; 155 Ok(CompOsInstance { 156 vm_instance, 157 service, 158 lazy_service_guard: Default::default(), 159 instance_tracker: Default::default(), 160 }) 161 } 162 create_instance_image( &self, virtualization_service: &dyn IVirtualizationService, ) -> Result<()>163 fn create_instance_image( 164 &self, 165 virtualization_service: &dyn IVirtualizationService, 166 ) -> Result<()> { 167 let instance_image = fs::OpenOptions::new() 168 .create(true) 169 .truncate(true) 170 .read(true) 171 .write(true) 172 .open(&self.instance_image) 173 .context("Creating instance image file")?; 174 let instance_image = ParcelFileDescriptor::new(instance_image); 175 // TODO: Where does this number come from? 176 let size = 10 * 1024 * 1024; 177 virtualization_service 178 .initializeWritablePartition(&instance_image, size, PartitionType::ANDROID_VM_INSTANCE) 179 .context("Writing instance image file")?; 180 Ok(()) 181 } 182 allocate_instance_id( &self, virtualization_service: &dyn IVirtualizationService, ) -> Result<()>183 fn allocate_instance_id( 184 &self, 185 virtualization_service: &dyn IVirtualizationService, 186 ) -> Result<()> { 187 let id = virtualization_service.allocateInstanceId().context("Allocating Instance Id")?; 188 fs::write(&self.instance_id_file, id)?; 189 Ok(()) 190 } 191 } 192