blob: 8dedec5a3df87c2a251594c81766593dbbdc122b [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
Alice Wang734801c2023-09-05 11:46:50 +000015//! This module contains the functions to start, stop and communicate with the
Alice Wangc206b9b2023-08-28 14:13:51 +000016//! Service VM.
17
18use android_system_virtualizationservice::{
19 aidl::android::system::virtualizationservice::{
20 CpuTopology::CpuTopology, DiskImage::DiskImage,
21 IVirtualizationService::IVirtualizationService, Partition::Partition,
22 PartitionType::PartitionType, VirtualMachineConfig::VirtualMachineConfig,
23 VirtualMachineRawConfig::VirtualMachineRawConfig,
24 },
25 binder::ParcelFileDescriptor,
26};
Alice Wangfbdc85b2023-09-07 12:56:46 +000027use anyhow::{anyhow, ensure, Context, Result};
Alice Wang19723ca2023-09-08 11:13:52 +000028use lazy_static::lazy_static;
Alice Wanga4486592023-09-05 08:25:59 +000029use log::{info, warn};
Alice Wangfbdc85b2023-09-07 12:56:46 +000030use service_vm_comm::{Request, Response, ServiceVmRequest, VmType};
Alice Wangc206b9b2023-08-28 14:13:51 +000031use std::fs::{File, OpenOptions};
Alice Wang977b64b2023-09-07 14:04:26 +000032use std::io::{self, BufRead, BufReader, BufWriter, Write};
33use std::os::unix::io::FromRawFd;
Alice Wang17dc76e2023-09-06 09:43:52 +000034use std::path::{Path, PathBuf};
Alice Wang19723ca2023-09-08 11:13:52 +000035use std::sync::{Condvar, Mutex, MutexGuard};
Alice Wang977b64b2023-09-07 14:04:26 +000036use std::thread;
Alice Wanga4486592023-09-05 08:25:59 +000037use std::time::Duration;
Alice Wangfbdc85b2023-09-07 12:56:46 +000038use vmclient::{DeathReason, VmInstance};
Alice Wanga4486592023-09-05 08:25:59 +000039use vsock::{VsockListener, VsockStream, VMADDR_CID_HOST};
Alice Wangc206b9b2023-08-28 14:13:51 +000040
41const VIRT_DATA_DIR: &str = "/data/misc/apexdata/com.android.virt";
42const RIALTO_PATH: &str = "/apex/com.android.virt/etc/rialto.bin";
43const INSTANCE_IMG_NAME: &str = "service_vm_instance.img";
44const INSTANCE_IMG_SIZE_BYTES: i64 = 1 << 20; // 1MB
45const MEMORY_MB: i32 = 300;
Alice Wanga4486592023-09-05 08:25:59 +000046const WRITE_BUFFER_CAPACITY: usize = 512;
47const READ_TIMEOUT: Duration = Duration::from_secs(10);
48const WRITE_TIMEOUT: Duration = Duration::from_secs(10);
Alice Wangc206b9b2023-08-28 14:13:51 +000049
Alice Wang19723ca2023-09-08 11:13:52 +000050lazy_static! {
51 static ref SERVICE_VM_STATE: State = State::default();
52}
53
54/// The running state of the Service VM.
55#[derive(Debug, Default)]
56struct State {
57 is_running: Mutex<bool>,
58 stopped: Condvar,
59}
60
61impl State {
62 fn wait_until_no_service_vm_running(&self) -> Result<MutexGuard<'_, bool>> {
63 // The real timeout can be longer than 10 seconds since the time to acquire
64 // is_running mutex is not counted in the 10 seconds.
65 let (guard, wait_result) = self
66 .stopped
67 .wait_timeout_while(
68 self.is_running.lock().unwrap(),
69 Duration::from_secs(10),
70 |&mut is_running| is_running,
71 )
72 .unwrap();
73 ensure!(
74 !wait_result.timed_out(),
75 "Timed out while waiting for the running service VM to stop."
76 );
77 Ok(guard)
78 }
79
80 fn notify_service_vm_shutdown(&self) {
81 let mut is_running_guard = self.is_running.lock().unwrap();
82 *is_running_guard = false;
83 self.stopped.notify_one();
84 }
85}
86
Alice Wanga4486592023-09-05 08:25:59 +000087/// Service VM.
88pub struct ServiceVm {
89 vsock_stream: VsockStream,
90 /// VmInstance will be dropped when ServiceVm goes out of scope, which will kill the VM.
91 vm: VmInstance,
92}
Alice Wangc206b9b2023-08-28 14:13:51 +000093
Alice Wanga4486592023-09-05 08:25:59 +000094impl ServiceVm {
95 /// Starts the service VM and returns its instance.
96 /// The same instance image is used for different VMs.
Alice Wang19723ca2023-09-08 11:13:52 +000097 /// At any given time, only one service should be running. If a service VM is
98 /// already running, this function will start the service VM once the running one
99 /// shuts down.
Alice Wanga4486592023-09-05 08:25:59 +0000100 pub fn start() -> Result<Self> {
Alice Wang19723ca2023-09-08 11:13:52 +0000101 let mut is_running_guard = SERVICE_VM_STATE.wait_until_no_service_vm_running()?;
102
Alice Wanga6357692023-09-07 14:59:37 +0000103 let instance_img_path = Path::new(VIRT_DATA_DIR).join(INSTANCE_IMG_NAME);
104 let vm = protected_vm_instance(instance_img_path)?;
Alice Wang19723ca2023-09-08 11:13:52 +0000105
106 let vm = Self::start_vm(vm, VmType::ProtectedVm)?;
107 *is_running_guard = true;
108 Ok(vm)
Alice Wang17dc76e2023-09-06 09:43:52 +0000109 }
110
111 /// Starts the given VM instance and sets up the vsock connection with it.
112 /// Returns a `ServiceVm` instance.
113 /// This function is exposed for testing.
114 pub fn start_vm(vm: VmInstance, vm_type: VmType) -> Result<Self> {
Alice Wanga4486592023-09-05 08:25:59 +0000115 // Sets up the vsock server on the host.
Alice Wang17dc76e2023-09-06 09:43:52 +0000116 let vsock_listener = VsockListener::bind_with_cid_port(VMADDR_CID_HOST, vm_type.port())?;
Alice Wangc206b9b2023-08-28 14:13:51 +0000117
Alice Wanga4486592023-09-05 08:25:59 +0000118 // Starts the service VM.
Alice Wanga4486592023-09-05 08:25:59 +0000119 vm.start().context("Failed to start service VM")?;
120 info!("Service VM started");
121
122 // Accepts the connection from the service VM.
123 // TODO(b/299427101): Introduce a timeout for the accept.
124 let (vsock_stream, peer_addr) = vsock_listener.accept().context("Failed to accept")?;
125 info!("Accepted connection {:?}", vsock_stream);
126 ensure!(
127 peer_addr.cid() == u32::try_from(vm.cid()).unwrap(),
128 "The CID of the peer address {} doesn't match the service VM CID {}",
129 peer_addr,
130 vm.cid()
131 );
132 vsock_stream.set_read_timeout(Some(READ_TIMEOUT))?;
133 vsock_stream.set_write_timeout(Some(WRITE_TIMEOUT))?;
134
135 Ok(Self { vsock_stream, vm })
136 }
137
138 /// Processes the request in the service VM.
Alice Wangfbdc85b2023-09-07 12:56:46 +0000139 pub fn process_request(&mut self, request: Request) -> Result<Response> {
140 self.write_request(&ServiceVmRequest::Process(request))?;
Alice Wanga4486592023-09-05 08:25:59 +0000141 self.read_response()
142 }
143
144 /// Sends the request to the service VM.
Alice Wangfbdc85b2023-09-07 12:56:46 +0000145 fn write_request(&mut self, request: &ServiceVmRequest) -> Result<()> {
Alice Wanga4486592023-09-05 08:25:59 +0000146 let mut buffer = BufWriter::with_capacity(WRITE_BUFFER_CAPACITY, &mut self.vsock_stream);
147 ciborium::into_writer(request, &mut buffer)?;
148 buffer.flush().context("Failed to flush the buffer")?;
149 info!("Sent request to the service VM.");
150 Ok(())
151 }
152
153 /// Reads the response from the service VM.
154 fn read_response(&mut self) -> Result<Response> {
155 let response: Response = ciborium::from_reader(&mut self.vsock_stream)
156 .context("Failed to read the response from the service VM")?;
157 info!("Received response from the service VM.");
158 Ok(response)
159 }
Alice Wangfbdc85b2023-09-07 12:56:46 +0000160
161 /// Shuts down the service VM.
162 fn shutdown(&mut self) -> Result<DeathReason> {
163 self.write_request(&ServiceVmRequest::Shutdown)?;
164 self.vm
165 .wait_for_death_with_timeout(Duration::from_secs(10))
166 .ok_or_else(|| anyhow!("Timed out to exit the service VM"))
167 }
Alice Wanga4486592023-09-05 08:25:59 +0000168}
169
170impl Drop for ServiceVm {
171 fn drop(&mut self) {
172 // Wait till the service VM finishes releasing all the resources.
Alice Wangfbdc85b2023-09-07 12:56:46 +0000173 match self.shutdown() {
174 Ok(reason) => info!("Exit the service VM successfully: {reason:?}"),
175 Err(e) => warn!("Service VM shutdown request failed '{e:?}', killing it."),
Alice Wanga4486592023-09-05 08:25:59 +0000176 }
Alice Wang19723ca2023-09-08 11:13:52 +0000177 SERVICE_VM_STATE.notify_service_vm_shutdown();
Alice Wanga4486592023-09-05 08:25:59 +0000178 }
Alice Wangc206b9b2023-08-28 14:13:51 +0000179}
180
Alice Wanga6357692023-09-07 14:59:37 +0000181/// Returns a `VmInstance` of a protected VM with the instance image from the given path.
182pub fn protected_vm_instance(instance_img_path: PathBuf) -> Result<VmInstance> {
Alice Wang17dc76e2023-09-06 09:43:52 +0000183 let virtmgr = vmclient::VirtualizationService::new().context("Failed to spawn VirtMgr")?;
184 let service = virtmgr.connect().context("Failed to connect to VirtMgr")?;
185 info!("Connected to VirtMgr for service VM");
186
Alice Wang17dc76e2023-09-06 09:43:52 +0000187 let instance_img = instance_img(service.as_ref(), instance_img_path)?;
Alice Wangc206b9b2023-08-28 14:13:51 +0000188 let writable_partitions = vec![Partition {
189 label: "vm-instance".to_owned(),
190 image: Some(instance_img),
191 writable: true,
192 }];
193 let rialto = File::open(RIALTO_PATH).context("Failed to open Rialto kernel binary")?;
194 let config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
195 name: String::from("Service VM"),
196 bootloader: Some(ParcelFileDescriptor::new(rialto)),
197 disks: vec![DiskImage { image: None, partitions: writable_partitions, writable: true }],
198 protectedVm: true,
199 memoryMib: MEMORY_MB,
200 cpuTopology: CpuTopology::ONE_CPU,
201 platformVersion: "~1.0".to_string(),
202 gdbPort: 0, // No gdb
203 ..Default::default()
204 });
Alice Wang977b64b2023-09-07 14:04:26 +0000205 let console_out = Some(android_log_fd()?);
Alice Wangc206b9b2023-08-28 14:13:51 +0000206 let console_in = None;
Alice Wang977b64b2023-09-07 14:04:26 +0000207 let log = Some(android_log_fd()?);
Alice Wangc206b9b2023-08-28 14:13:51 +0000208 let callback = None;
Alice Wang17dc76e2023-09-06 09:43:52 +0000209 VmInstance::create(service.as_ref(), &config, console_out, console_in, log, callback)
Alice Wangc206b9b2023-08-28 14:13:51 +0000210 .context("Failed to create service VM")
211}
212
Alice Wang17dc76e2023-09-06 09:43:52 +0000213/// Returns the file descriptor of the instance image at the given path.
Alice Wanga6357692023-09-07 14:59:37 +0000214fn instance_img(
Alice Wang17dc76e2023-09-06 09:43:52 +0000215 service: &dyn IVirtualizationService,
216 instance_img_path: PathBuf,
217) -> Result<ParcelFileDescriptor> {
Alice Wangc206b9b2023-08-28 14:13:51 +0000218 if instance_img_path.exists() {
219 // TODO(b/298174584): Try to recover if the service VM is triggered by rkpd.
220 return Ok(OpenOptions::new()
221 .read(true)
222 .write(true)
223 .open(instance_img_path)
224 .map(ParcelFileDescriptor::new)?);
225 }
226 let instance_img = OpenOptions::new()
227 .create(true)
228 .read(true)
229 .write(true)
230 .open(instance_img_path)
231 .map(ParcelFileDescriptor::new)?;
232 service.initializeWritablePartition(
233 &instance_img,
234 INSTANCE_IMG_SIZE_BYTES,
235 PartitionType::ANDROID_VM_INSTANCE,
236 )?;
237 Ok(instance_img)
238}
Alice Wang977b64b2023-09-07 14:04:26 +0000239
240/// This function is only exposed for testing.
241pub fn android_log_fd() -> io::Result<File> {
242 let (reader_fd, writer_fd) = nix::unistd::pipe()?;
243
244 // SAFETY: These are new FDs with no previous owner.
245 let reader = unsafe { File::from_raw_fd(reader_fd) };
246 // SAFETY: These are new FDs with no previous owner.
247 let writer = unsafe { File::from_raw_fd(writer_fd) };
248
249 thread::spawn(|| {
250 for line in BufReader::new(reader).lines() {
251 match line {
252 Ok(l) => info!("{}", l),
253 Err(e) => {
254 warn!("Failed to read line: {e:?}");
255 break;
256 }
257 }
258 }
259 });
260 Ok(writer)
261}