• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2025 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 //! Tests running a VM with older images
16 
17 use anyhow::{bail, ensure, Context, Result};
18 use log::info;
19 use std::ffi::CStr;
20 use std::fs::File;
21 use std::io::{self, BufWriter, Write};
22 use std::os::fd::IntoRawFd;
23 use std::time::{Duration, Instant};
24 use vsock::{VsockListener, VsockStream, VMADDR_CID_HOST};
25 
26 use avf_bindgen::*;
27 use service_vm_comm::{Request, Response, ServiceVmRequest, VmType};
28 
29 const VM_MEMORY_MB: i32 = 16;
30 const WRITE_BUFFER_CAPACITY: usize = 512;
31 
32 const LISTEN_TIMEOUT: Duration = Duration::from_secs(10);
33 const READ_TIMEOUT: Duration = Duration::from_secs(10);
34 const WRITE_TIMEOUT: Duration = Duration::from_secs(10);
35 const STOP_TIMEOUT: timespec = timespec { tv_sec: 10, tv_nsec: 0 };
36 
37 /// Processes the request in the service VM.
process_request(vsock_stream: &mut VsockStream, request: Request) -> Result<Response>38 fn process_request(vsock_stream: &mut VsockStream, request: Request) -> Result<Response> {
39     write_request(vsock_stream, &ServiceVmRequest::Process(request))?;
40     read_response(vsock_stream)
41 }
42 
43 /// Sends the request to the service VM.
write_request(vsock_stream: &mut VsockStream, request: &ServiceVmRequest) -> Result<()>44 fn write_request(vsock_stream: &mut VsockStream, request: &ServiceVmRequest) -> Result<()> {
45     let mut buffer = BufWriter::with_capacity(WRITE_BUFFER_CAPACITY, vsock_stream);
46     ciborium::into_writer(request, &mut buffer)?;
47     buffer.flush().context("Failed to flush the buffer")?;
48     Ok(())
49 }
50 
51 /// Reads the response from the service VM.
read_response(vsock_stream: &mut VsockStream) -> Result<Response>52 fn read_response(vsock_stream: &mut VsockStream) -> Result<Response> {
53     let response: Response = ciborium::from_reader(vsock_stream)
54         .context("Failed to read the response from the service VM")?;
55     Ok(response)
56 }
57 
listen_from_guest(port: u32) -> Result<VsockStream>58 fn listen_from_guest(port: u32) -> Result<VsockStream> {
59     let vsock_listener =
60         VsockListener::bind_with_cid_port(VMADDR_CID_HOST, port).context("Failed to bind vsock")?;
61     vsock_listener.set_nonblocking(true).context("Failed to set nonblocking")?;
62     let start_time = Instant::now();
63     loop {
64         if start_time.elapsed() >= LISTEN_TIMEOUT {
65             bail!("Timeout while listening");
66         }
67         match vsock_listener.accept() {
68             Ok((vsock_stream, _peer_addr)) => return Ok(vsock_stream),
69             Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
70                 std::thread::sleep(Duration::from_millis(100));
71             }
72             Err(e) => bail!("Failed to listen: {e:?}"),
73         }
74     }
75 }
76 
run_vm(image_path: &str, test_name: &CStr, protected_vm: bool) -> Result<()>77 fn run_vm(image_path: &str, test_name: &CStr, protected_vm: bool) -> Result<()> {
78     let kernel_file = File::open(image_path).context("Failed to open kernel file")?;
79     let kernel_fd = kernel_file.into_raw_fd();
80 
81     // SAFETY: AVirtualMachineRawConfig_create() isn't unsafe but rust_bindgen forces it to be seen
82     // as unsafe
83     let config = unsafe { AVirtualMachineRawConfig_create() };
84 
85     info!("raw config created");
86 
87     // SAFETY: config is the only reference to a valid object
88     unsafe {
89         AVirtualMachineRawConfig_setName(config, test_name.as_ptr());
90         AVirtualMachineRawConfig_setKernel(config, kernel_fd);
91         AVirtualMachineRawConfig_setProtectedVm(config, protected_vm);
92         AVirtualMachineRawConfig_setMemoryMiB(config, VM_MEMORY_MB);
93     }
94 
95     let mut vm = std::ptr::null_mut();
96     let mut service = std::ptr::null_mut();
97 
98     ensure!(
99         // SAFETY: &mut service is a valid pointer to *AVirtualizationService
100         unsafe { AVirtualizationService_create(&mut service, false) } == 0,
101         "AVirtualizationService_create failed"
102     );
103 
104     scopeguard::defer! {
105         // SAFETY: service is a valid pointer to AVirtualizationService
106         unsafe { AVirtualizationService_destroy(service); }
107     }
108 
109     ensure!(
110         // SAFETY: &mut vm is a valid pointer to *AVirtualMachine
111         unsafe {
112             AVirtualMachine_createRaw(
113                 service, config, -1, // console_in
114                 -1, // console_out
115                 -1, // log
116                 &mut vm,
117             )
118         } == 0,
119         "AVirtualMachine_createRaw failed"
120     );
121 
122     scopeguard::defer! {
123         // SAFETY: vm is a valid pointer to AVirtualMachine
124         unsafe { AVirtualMachine_destroy(vm); }
125     }
126 
127     info!("vm created");
128 
129     let vm_type = if protected_vm { VmType::ProtectedVm } else { VmType::NonProtectedVm };
130 
131     let listener_thread = std::thread::spawn(move || listen_from_guest(vm_type.port()));
132 
133     // SAFETY: vm is the only reference to a valid object
134     unsafe {
135         AVirtualMachine_start(vm);
136     }
137 
138     info!("VM started");
139 
140     let mut vsock_stream = listener_thread.join().unwrap()?;
141     vsock_stream.set_read_timeout(Some(READ_TIMEOUT))?;
142     vsock_stream.set_write_timeout(Some(WRITE_TIMEOUT))?;
143 
144     info!("client connected");
145 
146     let request_data = vec![1, 2, 3, 4, 5];
147     let expected_data = vec![5, 4, 3, 2, 1];
148     let response = process_request(&mut vsock_stream, Request::Reverse(request_data))
149         .context("Failed to process request")?;
150     let Response::Reverse(reversed_data) = response else {
151         bail!("Expected Response::Reverse but was {response:?}");
152     };
153     ensure!(reversed_data == expected_data, "Expected {expected_data:?} but was {reversed_data:?}");
154 
155     info!("request processed");
156 
157     write_request(&mut vsock_stream, &ServiceVmRequest::Shutdown)
158         .context("Failed to send shutdown")?;
159 
160     info!("shutdown sent");
161 
162     let mut stop_reason = AVirtualMachineStopReason::AVIRTUAL_MACHINE_UNRECOGNISED;
163     ensure!(
164         // SAFETY: vm is the only reference to a valid object
165         unsafe { AVirtualMachine_waitForStop(vm, &STOP_TIMEOUT, &mut stop_reason) },
166         "AVirtualMachine_waitForStop failed"
167     );
168     assert_eq!(stop_reason, AVirtualMachineStopReason::AVIRTUAL_MACHINE_SHUTDOWN);
169 
170     info!("stopped");
171 
172     Ok(())
173 }
174 
175 #[test]
test_run_rialto_non_protected() -> Result<()>176 fn test_run_rialto_non_protected() -> Result<()> {
177     if hypervisor_props::is_vm_supported()? {
178         run_vm(
179             "/data/local/tmp/rialto.bin", /* image_path */
180             c"test_rialto",               /* test_name */
181             false,                        /* protected_vm */
182         )
183     } else {
184         info!("VMs are not supported on device. skipping test");
185         Ok(())
186     }
187 }
188 
189 #[test]
test_run_android16_rialto_non_protected() -> Result<()>190 fn test_run_android16_rialto_non_protected() -> Result<()> {
191     if hypervisor_props::is_vm_supported()? {
192         run_vm(
193             "/data/local/tmp/android16_rialto.bin", /* image_path */
194             c"android16_test_rialto",               /* test_name */
195             false,                                  /* protected_vm */
196         )
197     } else {
198         info!("VMs are not supported on device. skipping test");
199         Ok(())
200     }
201 }
202