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