blob: 23eae9144dcad883bf6ab9ade88bc048d26dc9f8 [file] [log] [blame]
Alice Wangc206b9b2023-08-28 14:13:51 +00001// Copyright 2023, 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//! Service VM.
16
17use android_system_virtualizationservice::{
18 aidl::android::system::virtualizationservice::{
19 CpuTopology::CpuTopology, DiskImage::DiskImage,
20 IVirtualizationService::IVirtualizationService, Partition::Partition,
21 PartitionType::PartitionType, VirtualMachineConfig::VirtualMachineConfig,
22 VirtualMachineRawConfig::VirtualMachineRawConfig,
23 },
24 binder::ParcelFileDescriptor,
25};
Alice Wanga4486592023-09-05 08:25:59 +000026use anyhow::{ensure, Context, Result};
27use log::{info, warn};
28use service_vm_comm::{Request, Response, VmType};
Alice Wangc206b9b2023-08-28 14:13:51 +000029use std::fs::{File, OpenOptions};
Alice Wanga4486592023-09-05 08:25:59 +000030use std::io::{BufWriter, Write};
Alice Wangc206b9b2023-08-28 14:13:51 +000031use std::path::Path;
Alice Wanga4486592023-09-05 08:25:59 +000032use std::time::Duration;
Alice Wangc206b9b2023-08-28 14:13:51 +000033use vmclient::VmInstance;
Alice Wanga4486592023-09-05 08:25:59 +000034use vsock::{VsockListener, VsockStream, VMADDR_CID_HOST};
Alice Wangc206b9b2023-08-28 14:13:51 +000035
36const VIRT_DATA_DIR: &str = "/data/misc/apexdata/com.android.virt";
37const RIALTO_PATH: &str = "/apex/com.android.virt/etc/rialto.bin";
38const INSTANCE_IMG_NAME: &str = "service_vm_instance.img";
39const INSTANCE_IMG_SIZE_BYTES: i64 = 1 << 20; // 1MB
40const MEMORY_MB: i32 = 300;
Alice Wanga4486592023-09-05 08:25:59 +000041const WRITE_BUFFER_CAPACITY: usize = 512;
42const READ_TIMEOUT: Duration = Duration::from_secs(10);
43const WRITE_TIMEOUT: Duration = Duration::from_secs(10);
Alice Wangc206b9b2023-08-28 14:13:51 +000044
Alice Wanga4486592023-09-05 08:25:59 +000045/// Service VM.
46pub struct ServiceVm {
47 vsock_stream: VsockStream,
48 /// VmInstance will be dropped when ServiceVm goes out of scope, which will kill the VM.
49 vm: VmInstance,
50}
Alice Wangc206b9b2023-08-28 14:13:51 +000051
Alice Wanga4486592023-09-05 08:25:59 +000052impl ServiceVm {
53 /// Starts the service VM and returns its instance.
54 /// The same instance image is used for different VMs.
55 /// TODO(b/278858244): Allow only one service VM running at each time.
56 pub fn start() -> Result<Self> {
57 // Sets up the vsock server on the host.
58 let vsock_listener =
59 VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VmType::ProtectedVm.port())?;
Alice Wangc206b9b2023-08-28 14:13:51 +000060
Alice Wanga4486592023-09-05 08:25:59 +000061 // Starts the service VM.
62 let virtmgr = vmclient::VirtualizationService::new().context("Failed to spawn VirtMgr")?;
63 let service = virtmgr.connect().context("Failed to connect to VirtMgr")?;
64 info!("Connected to VirtMgr for service VM");
65
66 let vm = vm_instance(service.as_ref())?;
67 vm.start().context("Failed to start service VM")?;
68 info!("Service VM started");
69
70 // Accepts the connection from the service VM.
71 // TODO(b/299427101): Introduce a timeout for the accept.
72 let (vsock_stream, peer_addr) = vsock_listener.accept().context("Failed to accept")?;
73 info!("Accepted connection {:?}", vsock_stream);
74 ensure!(
75 peer_addr.cid() == u32::try_from(vm.cid()).unwrap(),
76 "The CID of the peer address {} doesn't match the service VM CID {}",
77 peer_addr,
78 vm.cid()
79 );
80 vsock_stream.set_read_timeout(Some(READ_TIMEOUT))?;
81 vsock_stream.set_write_timeout(Some(WRITE_TIMEOUT))?;
82
83 Ok(Self { vsock_stream, vm })
84 }
85
86 /// Processes the request in the service VM.
87 pub fn process_request(&mut self, request: &Request) -> Result<Response> {
88 self.write_request(request)?;
89 self.read_response()
90 }
91
92 /// Sends the request to the service VM.
93 fn write_request(&mut self, request: &Request) -> Result<()> {
94 let mut buffer = BufWriter::with_capacity(WRITE_BUFFER_CAPACITY, &mut self.vsock_stream);
95 ciborium::into_writer(request, &mut buffer)?;
96 buffer.flush().context("Failed to flush the buffer")?;
97 info!("Sent request to the service VM.");
98 Ok(())
99 }
100
101 /// Reads the response from the service VM.
102 fn read_response(&mut self) -> Result<Response> {
103 let response: Response = ciborium::from_reader(&mut self.vsock_stream)
104 .context("Failed to read the response from the service VM")?;
105 info!("Received response from the service VM.");
106 Ok(response)
107 }
108}
109
110impl Drop for ServiceVm {
111 fn drop(&mut self) {
112 // Wait till the service VM finishes releasing all the resources.
113 match self.vm.wait_for_death_with_timeout(Duration::from_secs(10)) {
114 Some(e) => info!("Exit the service VM: {e:?}"),
115 None => warn!("Timed out waiting for service VM exit"),
116 }
117 }
Alice Wangc206b9b2023-08-28 14:13:51 +0000118}
119
120fn vm_instance(service: &dyn IVirtualizationService) -> Result<VmInstance> {
121 let instance_img = instance_img(service)?;
122 let writable_partitions = vec![Partition {
123 label: "vm-instance".to_owned(),
124 image: Some(instance_img),
125 writable: true,
126 }];
127 let rialto = File::open(RIALTO_PATH).context("Failed to open Rialto kernel binary")?;
128 let config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
129 name: String::from("Service VM"),
130 bootloader: Some(ParcelFileDescriptor::new(rialto)),
131 disks: vec![DiskImage { image: None, partitions: writable_partitions, writable: true }],
132 protectedVm: true,
133 memoryMib: MEMORY_MB,
134 cpuTopology: CpuTopology::ONE_CPU,
135 platformVersion: "~1.0".to_string(),
136 gdbPort: 0, // No gdb
137 ..Default::default()
138 });
139 let console_out = None;
140 let console_in = None;
141 let log = None;
142 let callback = None;
143 VmInstance::create(service, &config, console_out, console_in, log, callback)
144 .context("Failed to create service VM")
145}
146
147fn instance_img(service: &dyn IVirtualizationService) -> Result<ParcelFileDescriptor> {
148 let instance_img_path = Path::new(VIRT_DATA_DIR).join(INSTANCE_IMG_NAME);
149 if instance_img_path.exists() {
150 // TODO(b/298174584): Try to recover if the service VM is triggered by rkpd.
151 return Ok(OpenOptions::new()
152 .read(true)
153 .write(true)
154 .open(instance_img_path)
155 .map(ParcelFileDescriptor::new)?);
156 }
157 let instance_img = OpenOptions::new()
158 .create(true)
159 .read(true)
160 .write(true)
161 .open(instance_img_path)
162 .map(ParcelFileDescriptor::new)?;
163 service.initializeWritablePartition(
164 &instance_img,
165 INSTANCE_IMG_SIZE_BYTES,
166 PartitionType::ANDROID_VM_INSTANCE,
167 )?;
168 Ok(instance_img)
169}