blob: c27570c6fcc02674779cf913374b4e6c50556f66 [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 Wanga4486592023-09-05 08:25:59 +000027use anyhow::{ensure, Context, Result};
28use log::{info, warn};
29use service_vm_comm::{Request, Response, VmType};
Alice Wangc206b9b2023-08-28 14:13:51 +000030use std::fs::{File, OpenOptions};
Alice Wang977b64b2023-09-07 14:04:26 +000031use std::io::{self, BufRead, BufReader, BufWriter, Write};
32use std::os::unix::io::FromRawFd;
Alice Wang17dc76e2023-09-06 09:43:52 +000033use std::path::{Path, PathBuf};
Alice Wang977b64b2023-09-07 14:04:26 +000034use std::thread;
Alice Wanga4486592023-09-05 08:25:59 +000035use std::time::Duration;
Alice Wangc206b9b2023-08-28 14:13:51 +000036use vmclient::VmInstance;
Alice Wanga4486592023-09-05 08:25:59 +000037use vsock::{VsockListener, VsockStream, VMADDR_CID_HOST};
Alice Wangc206b9b2023-08-28 14:13:51 +000038
39const VIRT_DATA_DIR: &str = "/data/misc/apexdata/com.android.virt";
40const RIALTO_PATH: &str = "/apex/com.android.virt/etc/rialto.bin";
41const INSTANCE_IMG_NAME: &str = "service_vm_instance.img";
42const INSTANCE_IMG_SIZE_BYTES: i64 = 1 << 20; // 1MB
43const MEMORY_MB: i32 = 300;
Alice Wanga4486592023-09-05 08:25:59 +000044const WRITE_BUFFER_CAPACITY: usize = 512;
45const READ_TIMEOUT: Duration = Duration::from_secs(10);
46const WRITE_TIMEOUT: Duration = Duration::from_secs(10);
Alice Wangc206b9b2023-08-28 14:13:51 +000047
Alice Wanga4486592023-09-05 08:25:59 +000048/// Service VM.
49pub struct ServiceVm {
50 vsock_stream: VsockStream,
51 /// VmInstance will be dropped when ServiceVm goes out of scope, which will kill the VM.
52 vm: VmInstance,
53}
Alice Wangc206b9b2023-08-28 14:13:51 +000054
Alice Wanga4486592023-09-05 08:25:59 +000055impl ServiceVm {
56 /// Starts the service VM and returns its instance.
57 /// The same instance image is used for different VMs.
58 /// TODO(b/278858244): Allow only one service VM running at each time.
59 pub fn start() -> Result<Self> {
Alice Wanga6357692023-09-07 14:59:37 +000060 let instance_img_path = Path::new(VIRT_DATA_DIR).join(INSTANCE_IMG_NAME);
61 let vm = protected_vm_instance(instance_img_path)?;
Alice Wang17dc76e2023-09-06 09:43:52 +000062 Self::start_vm(vm, VmType::ProtectedVm)
63 }
64
65 /// Starts the given VM instance and sets up the vsock connection with it.
66 /// Returns a `ServiceVm` instance.
67 /// This function is exposed for testing.
68 pub fn start_vm(vm: VmInstance, vm_type: VmType) -> Result<Self> {
Alice Wanga4486592023-09-05 08:25:59 +000069 // Sets up the vsock server on the host.
Alice Wang17dc76e2023-09-06 09:43:52 +000070 let vsock_listener = VsockListener::bind_with_cid_port(VMADDR_CID_HOST, vm_type.port())?;
Alice Wangc206b9b2023-08-28 14:13:51 +000071
Alice Wanga4486592023-09-05 08:25:59 +000072 // Starts the service VM.
Alice Wanga4486592023-09-05 08:25:59 +000073 vm.start().context("Failed to start service VM")?;
74 info!("Service VM started");
75
76 // Accepts the connection from the service VM.
77 // TODO(b/299427101): Introduce a timeout for the accept.
78 let (vsock_stream, peer_addr) = vsock_listener.accept().context("Failed to accept")?;
79 info!("Accepted connection {:?}", vsock_stream);
80 ensure!(
81 peer_addr.cid() == u32::try_from(vm.cid()).unwrap(),
82 "The CID of the peer address {} doesn't match the service VM CID {}",
83 peer_addr,
84 vm.cid()
85 );
86 vsock_stream.set_read_timeout(Some(READ_TIMEOUT))?;
87 vsock_stream.set_write_timeout(Some(WRITE_TIMEOUT))?;
88
89 Ok(Self { vsock_stream, vm })
90 }
91
92 /// Processes the request in the service VM.
93 pub fn process_request(&mut self, request: &Request) -> Result<Response> {
94 self.write_request(request)?;
95 self.read_response()
96 }
97
98 /// Sends the request to the service VM.
99 fn write_request(&mut self, request: &Request) -> Result<()> {
100 let mut buffer = BufWriter::with_capacity(WRITE_BUFFER_CAPACITY, &mut self.vsock_stream);
101 ciborium::into_writer(request, &mut buffer)?;
102 buffer.flush().context("Failed to flush the buffer")?;
103 info!("Sent request to the service VM.");
104 Ok(())
105 }
106
107 /// Reads the response from the service VM.
108 fn read_response(&mut self) -> Result<Response> {
109 let response: Response = ciborium::from_reader(&mut self.vsock_stream)
110 .context("Failed to read the response from the service VM")?;
111 info!("Received response from the service VM.");
112 Ok(response)
113 }
114}
115
116impl Drop for ServiceVm {
117 fn drop(&mut self) {
118 // Wait till the service VM finishes releasing all the resources.
119 match self.vm.wait_for_death_with_timeout(Duration::from_secs(10)) {
120 Some(e) => info!("Exit the service VM: {e:?}"),
121 None => warn!("Timed out waiting for service VM exit"),
122 }
123 }
Alice Wangc206b9b2023-08-28 14:13:51 +0000124}
125
Alice Wanga6357692023-09-07 14:59:37 +0000126/// Returns a `VmInstance` of a protected VM with the instance image from the given path.
127pub fn protected_vm_instance(instance_img_path: PathBuf) -> Result<VmInstance> {
Alice Wang17dc76e2023-09-06 09:43:52 +0000128 let virtmgr = vmclient::VirtualizationService::new().context("Failed to spawn VirtMgr")?;
129 let service = virtmgr.connect().context("Failed to connect to VirtMgr")?;
130 info!("Connected to VirtMgr for service VM");
131
Alice Wang17dc76e2023-09-06 09:43:52 +0000132 let instance_img = instance_img(service.as_ref(), instance_img_path)?;
Alice Wangc206b9b2023-08-28 14:13:51 +0000133 let writable_partitions = vec![Partition {
134 label: "vm-instance".to_owned(),
135 image: Some(instance_img),
136 writable: true,
137 }];
138 let rialto = File::open(RIALTO_PATH).context("Failed to open Rialto kernel binary")?;
139 let config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
140 name: String::from("Service VM"),
141 bootloader: Some(ParcelFileDescriptor::new(rialto)),
142 disks: vec![DiskImage { image: None, partitions: writable_partitions, writable: true }],
143 protectedVm: true,
144 memoryMib: MEMORY_MB,
145 cpuTopology: CpuTopology::ONE_CPU,
146 platformVersion: "~1.0".to_string(),
147 gdbPort: 0, // No gdb
148 ..Default::default()
149 });
Alice Wang977b64b2023-09-07 14:04:26 +0000150 let console_out = Some(android_log_fd()?);
Alice Wangc206b9b2023-08-28 14:13:51 +0000151 let console_in = None;
Alice Wang977b64b2023-09-07 14:04:26 +0000152 let log = Some(android_log_fd()?);
Alice Wangc206b9b2023-08-28 14:13:51 +0000153 let callback = None;
Alice Wang17dc76e2023-09-06 09:43:52 +0000154 VmInstance::create(service.as_ref(), &config, console_out, console_in, log, callback)
Alice Wangc206b9b2023-08-28 14:13:51 +0000155 .context("Failed to create service VM")
156}
157
Alice Wang17dc76e2023-09-06 09:43:52 +0000158/// Returns the file descriptor of the instance image at the given path.
Alice Wanga6357692023-09-07 14:59:37 +0000159fn instance_img(
Alice Wang17dc76e2023-09-06 09:43:52 +0000160 service: &dyn IVirtualizationService,
161 instance_img_path: PathBuf,
162) -> Result<ParcelFileDescriptor> {
Alice Wangc206b9b2023-08-28 14:13:51 +0000163 if instance_img_path.exists() {
164 // TODO(b/298174584): Try to recover if the service VM is triggered by rkpd.
165 return Ok(OpenOptions::new()
166 .read(true)
167 .write(true)
168 .open(instance_img_path)
169 .map(ParcelFileDescriptor::new)?);
170 }
171 let instance_img = OpenOptions::new()
172 .create(true)
173 .read(true)
174 .write(true)
175 .open(instance_img_path)
176 .map(ParcelFileDescriptor::new)?;
177 service.initializeWritablePartition(
178 &instance_img,
179 INSTANCE_IMG_SIZE_BYTES,
180 PartitionType::ANDROID_VM_INSTANCE,
181 )?;
182 Ok(instance_img)
183}
Alice Wang977b64b2023-09-07 14:04:26 +0000184
185/// This function is only exposed for testing.
186pub fn android_log_fd() -> io::Result<File> {
187 let (reader_fd, writer_fd) = nix::unistd::pipe()?;
188
189 // SAFETY: These are new FDs with no previous owner.
190 let reader = unsafe { File::from_raw_fd(reader_fd) };
191 // SAFETY: These are new FDs with no previous owner.
192 let writer = unsafe { File::from_raw_fd(writer_fd) };
193
194 thread::spawn(|| {
195 for line in BufReader::new(reader).lines() {
196 match line {
197 Ok(l) => info!("{}", l),
198 Err(e) => {
199 warn!("Failed to read line: {e:?}");
200 break;
201 }
202 }
203 }
204 });
205 Ok(writer)
206}