blob: 085a6202fb7c00ee9b9f9da321fc5601f34c1de1 [file] [log] [blame]
Andrew Walbran94bbf2f2022-05-12 18:35:42 +00001// Copyright 2022, 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//! Integration test for VM bootloader.
16
17use android_system_virtualizationservice::{
18 aidl::android::system::virtualizationservice::{
David Brazdil7d1e5ec2023-02-06 17:56:29 +000019 CpuTopology::CpuTopology, DiskImage::DiskImage, VirtualMachineConfig::VirtualMachineConfig,
Andrew Walbran94bbf2f2022-05-12 18:35:42 +000020 VirtualMachineRawConfig::VirtualMachineRawConfig,
21 },
22 binder::{ParcelFileDescriptor, ProcessState},
23};
24use anyhow::{Context, Error};
25use log::info;
26use std::{
Jakob Vukalovicef996292023-04-13 14:28:34 +000027 collections::{HashSet, VecDeque},
Andrew Walbran94bbf2f2022-05-12 18:35:42 +000028 fs::File,
Andrew Walbran8d05dae2023-03-22 16:42:55 +000029 io::{self, BufRead, BufReader, Read, Write},
Pierre-Clément Tosi0d1aed02022-11-17 17:06:28 +000030 os::unix::io::FromRawFd,
31 panic, thread,
Andrew Walbran94bbf2f2022-05-12 18:35:42 +000032};
33use vmclient::{DeathReason, VmInstance};
34
35const VMBASE_EXAMPLE_PATH: &str =
36 "/data/local/tmp/vmbase_example.integration_test/arm64/vmbase_example.bin";
Andrew Walbranb713baa2022-12-07 14:34:49 +000037const TEST_DISK_IMAGE_PATH: &str = "/data/local/tmp/vmbase_example.integration_test/test_disk.img";
Andrew Walbran6ac174e2023-06-23 14:58:51 +000038const EMPTY_DISK_IMAGE_PATH: &str =
39 "/data/local/tmp/vmbase_example.integration_test/empty_disk.img";
Andrew Walbran94bbf2f2022-05-12 18:35:42 +000040
41/// Runs the vmbase_example VM as an unprotected VM via VirtualizationService.
42#[test]
43fn test_run_example_vm() -> Result<(), Error> {
Pierre-Clément Tosi0d1aed02022-11-17 17:06:28 +000044 android_logger::init_once(
45 android_logger::Config::default().with_tag("vmbase").with_min_level(log::Level::Debug),
46 );
47
48 // Redirect panic messages to logcat.
49 panic::set_hook(Box::new(|panic_info| {
50 log::error!("{}", panic_info);
51 }));
Andrew Walbran94bbf2f2022-05-12 18:35:42 +000052
53 // We need to start the thread pool for Binder to work properly, especially link_to_death.
54 ProcessState::start_thread_pool();
55
David Brazdil4b4c5102022-12-19 22:56:20 +000056 let virtmgr =
57 vmclient::VirtualizationService::new().context("Failed to spawn VirtualizationService")?;
58 let service = virtmgr.connect().context("Failed to connect to VirtualizationService")?;
Andrew Walbran94bbf2f2022-05-12 18:35:42 +000059
60 // Start example VM.
61 let bootloader = ParcelFileDescriptor::new(
62 File::open(VMBASE_EXAMPLE_PATH)
63 .with_context(|| format!("Failed to open VM image {}", VMBASE_EXAMPLE_PATH))?,
64 );
Seungjae Yoo62085c02022-08-12 04:44:52 +000065
Andrew Walbranb713baa2022-12-07 14:34:49 +000066 // Make file for test disk image.
67 let mut test_image = File::options()
68 .create(true)
69 .read(true)
70 .write(true)
71 .truncate(true)
72 .open(TEST_DISK_IMAGE_PATH)
73 .with_context(|| format!("Failed to open test disk image {}", TEST_DISK_IMAGE_PATH))?;
74 // Write 4 sectors worth of 4-byte numbers counting up.
75 for i in 0u32..512 {
76 test_image.write_all(&i.to_le_bytes())?;
77 }
78 let test_image = ParcelFileDescriptor::new(test_image);
79 let disk_image = DiskImage { image: Some(test_image), writable: false, partitions: vec![] };
80
Andrew Walbran6ac174e2023-06-23 14:58:51 +000081 // Make file for empty test disk image.
82 let empty_image = File::options()
83 .create(true)
84 .read(true)
85 .write(true)
86 .truncate(true)
87 .open(EMPTY_DISK_IMAGE_PATH)
88 .with_context(|| format!("Failed to open empty disk image {}", EMPTY_DISK_IMAGE_PATH))?;
89 let empty_image = ParcelFileDescriptor::new(empty_image);
90 let empty_disk_image =
91 DiskImage { image: Some(empty_image), writable: false, partitions: vec![] };
92
Andrew Walbran94bbf2f2022-05-12 18:35:42 +000093 let config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
Seungjae Yoo62085c02022-08-12 04:44:52 +000094 name: String::from("VmBaseTest"),
Andrew Walbran94bbf2f2022-05-12 18:35:42 +000095 kernel: None,
96 initrd: None,
97 params: None,
98 bootloader: Some(bootloader),
Andrew Walbran6ac174e2023-06-23 14:58:51 +000099 disks: vec![disk_image, empty_disk_image],
Andrew Walbran94bbf2f2022-05-12 18:35:42 +0000100 protectedVm: false,
101 memoryMib: 300,
David Brazdil7d1e5ec2023-02-06 17:56:29 +0000102 cpuTopology: CpuTopology::ONE_CPU,
Andrew Walbran94bbf2f2022-05-12 18:35:42 +0000103 platformVersion: "~1.0".to_string(),
104 taskProfiles: vec![],
Nikita Ioffe5776f082023-02-10 21:38:26 +0000105 gdbPort: 0, // no gdb
Andrew Walbran94bbf2f2022-05-12 18:35:42 +0000106 });
Jakob Vukalovicef996292023-04-13 14:28:34 +0000107 let (handle, console) = android_log_fd()?;
Andrew Walbran8d05dae2023-03-22 16:42:55 +0000108 let (mut log_reader, log_writer) = pipe()?;
109 let vm = VmInstance::create(service.as_ref(), &config, Some(console), Some(log_writer), None)
Andrew Walbran94bbf2f2022-05-12 18:35:42 +0000110 .context("Failed to create VM")?;
111 vm.start().context("Failed to start VM")?;
112 info!("Started example VM.");
113
114 // Wait for VM to finish, and check that it shut down cleanly.
115 let death_reason = vm.wait_for_death();
116 assert_eq!(death_reason, DeathReason::Shutdown);
Jakob Vukalovicef996292023-04-13 14:28:34 +0000117 handle.join().unwrap();
Andrew Walbran94bbf2f2022-05-12 18:35:42 +0000118
Andrew Walbran8d05dae2023-03-22 16:42:55 +0000119 // Check that the expected string was written to the log VirtIO console device.
120 let expected = "Hello VirtIO console\n";
121 let mut log_output = String::new();
122 assert_eq!(log_reader.read_to_string(&mut log_output)?, expected.len());
123 assert_eq!(log_output, expected);
124
Andrew Walbran94bbf2f2022-05-12 18:35:42 +0000125 Ok(())
126}
127
Jakob Vukalovicef996292023-04-13 14:28:34 +0000128fn android_log_fd() -> Result<(thread::JoinHandle<()>, File), io::Error> {
Andrew Walbran8d05dae2023-03-22 16:42:55 +0000129 let (reader, writer) = pipe()?;
Jakob Vukalovicef996292023-04-13 14:28:34 +0000130 let handle = thread::spawn(|| VmLogProcessor::new(reader).run().unwrap());
131 Ok((handle, writer))
Andrew Walbran94bbf2f2022-05-12 18:35:42 +0000132}
Andrew Walbran8d05dae2023-03-22 16:42:55 +0000133
134fn pipe() -> io::Result<(File, File)> {
135 let (reader_fd, writer_fd) = nix::unistd::pipe()?;
136
137 // SAFETY: These are new FDs with no previous owner.
138 let reader = unsafe { File::from_raw_fd(reader_fd) };
139 let writer = unsafe { File::from_raw_fd(writer_fd) };
140
141 Ok((reader, writer))
142}
Jakob Vukalovicef996292023-04-13 14:28:34 +0000143
144struct VmLogProcessor {
145 reader: Option<File>,
146 expected: VecDeque<String>,
147 unexpected: HashSet<String>,
148 had_unexpected: bool,
149}
150
151impl VmLogProcessor {
152 fn messages() -> (VecDeque<String>, HashSet<String>) {
153 let mut expected = VecDeque::new();
154 let mut unexpected = HashSet::new();
155 for log_lvl in ["[ERROR]", "[WARN]", "[INFO]", "[DEBUG]"] {
156 expected.push_back(format!("{log_lvl} Unsuppressed message"));
157 unexpected.insert(format!("{log_lvl} Suppressed message"));
158 }
159 (expected, unexpected)
160 }
161
162 fn new(reader: File) -> Self {
163 let (expected, unexpected) = Self::messages();
164 Self { reader: Some(reader), expected, unexpected, had_unexpected: false }
165 }
166
167 fn verify(&mut self, msg: &str) {
168 if self.expected.front() == Some(&msg.to_owned()) {
169 self.expected.pop_front();
170 }
171 if !self.had_unexpected && self.unexpected.contains(msg) {
172 self.had_unexpected = true;
173 }
174 }
175
176 fn run(mut self) -> Result<(), &'static str> {
177 for line in BufReader::new(self.reader.take().unwrap()).lines() {
178 let msg = line.unwrap();
179 info!("{msg}");
180 self.verify(&msg);
181 }
182 if !self.expected.is_empty() {
183 Err("missing expected log message")
184 } else if self.had_unexpected {
185 Err("unexpected log message")
186 } else {
187 Ok(())
188 }
189 }
190}