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