1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! A client for trusty security VMs during early boot.
16 
17 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
18     CpuTopology::CpuTopology, IVirtualizationService::IVirtualizationService,
19     VirtualMachineConfig::VirtualMachineConfig, VirtualMachineRawConfig::VirtualMachineRawConfig,
20 };
21 use android_system_virtualizationservice::binder::{ParcelFileDescriptor, Strong};
22 use anyhow::{Context, Result};
23 use clap::Parser;
24 use std::fs::File;
25 use std::path::PathBuf;
26 use vmclient::VmInstance;
27 
28 #[derive(Parser)]
29 /// Collection of CLI for trusty_security_vm_launcher
30 pub struct Args {
31     /// Path to the trusty kernel image.
32     #[arg(long)]
33     kernel: PathBuf,
34 
35     /// Whether the VM is protected or not.
36     #[arg(long)]
37     protected: bool,
38 
39     /// Name of the VM. Used to pull correct config from early_vms.xml
40     #[arg(long, default_value = "trusty_security_vm_launcher")]
41     name: String,
42 
43     /// Memory size of the VM in MiB
44     #[arg(long, default_value_t = 128)]
45     memory_size_mib: i32,
46 
47     /// CPU Topology exposed to the VM <one-cpu|match-host>
48     #[arg(long, default_value = "one-cpu", value_parser = parse_cpu_topology)]
49     cpu_topology: CpuTopology,
50 }
51 
get_service() -> Result<Strong<dyn IVirtualizationService>>52 fn get_service() -> Result<Strong<dyn IVirtualizationService>> {
53     let virtmgr = vmclient::VirtualizationService::new_early()
54         .context("Failed to spawn VirtualizationService")?;
55     virtmgr.connect().context("Failed to connect to VirtualizationService")
56 }
57 
parse_cpu_topology(s: &str) -> Result<CpuTopology, String>58 fn parse_cpu_topology(s: &str) -> Result<CpuTopology, String> {
59     match s {
60         "one-cpu" => Ok(CpuTopology::ONE_CPU),
61         "match-host" => Ok(CpuTopology::MATCH_HOST),
62         _ => Err(format!("Invalid cpu topology {}", s)),
63     }
64 }
65 
main() -> Result<()>66 fn main() -> Result<()> {
67     let args = Args::parse();
68 
69     let service = get_service()?;
70 
71     let kernel =
72         File::open(&args.kernel).with_context(|| format!("Failed to open {:?}", &args.kernel))?;
73 
74     let vm_config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
75         name: args.name.to_owned(),
76         kernel: Some(ParcelFileDescriptor::new(kernel)),
77         protectedVm: args.protected,
78         memoryMib: args.memory_size_mib,
79         cpuTopology: args.cpu_topology,
80         platformVersion: "~1.0".to_owned(),
81         // TODO: add instanceId
82         ..Default::default()
83     });
84 
85     println!("creating VM");
86     let vm = VmInstance::create(
87         service.as_ref(),
88         &vm_config,
89         // console_in, console_out, and log will be redirected to the kernel log by virtmgr
90         None, // console_in
91         None, // console_out
92         None, // log
93         None, // dump_dt
94         None, // callback
95     )
96     .context("Failed to create VM")?;
97     vm.start().context("Failed to start VM")?;
98 
99     println!("started trusty_security_vm_launcher VM");
100     let death_reason = vm.wait_for_death();
101     eprintln!("trusty_security_vm_launcher ended: {:?}", death_reason);
102 
103     // TODO(b/331320802): we may want to use android logger instead of stdio_to_kmsg?
104 
105     Ok(())
106 }
107