1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
6
7 use arch::LinuxArch;
8 use devices::IrqChipX86_64;
9 use hypervisor::{HypervisorX86_64, ProtectionType, VcpuExit, VcpuX86_64, VmX86_64};
10 use resources::SystemAllocator;
11 use vm_memory::{GuestAddress, GuestMemory};
12
13 use super::cpuid::setup_cpuid;
14 use super::interrupts::set_lint;
15 use super::regs::{setup_fpu, setup_msrs, setup_regs, setup_sregs};
16 use super::X8664arch;
17 use super::{acpi, arch_memory_regions, bootparam, mptable, smbios};
18 use super::{
19 BOOT_STACK_POINTER, END_ADDR_BEFORE_32BITS, KERNEL_64BIT_ENTRY_OFFSET, KERNEL_START_OFFSET,
20 PCIE_CFG_MMIO_SIZE, PCIE_CFG_MMIO_START, X86_64_SCI_IRQ, ZERO_PAGE_OFFSET,
21 };
22
23 use base::{Event, Tube};
24
25 use std::collections::BTreeMap;
26 use std::ffi::CString;
27 use std::sync::Arc;
28 use std::thread;
29 use sync::Mutex;
30
31 use devices::PciConfigIo;
32
33 enum TaggedControlTube {
34 VmMemory(Tube),
35 VmIrq(Tube),
36 }
37
38 #[test]
simple_kvm_kernel_irqchip_test()39 fn simple_kvm_kernel_irqchip_test() {
40 use devices::KvmKernelIrqChip;
41 use hypervisor::kvm::*;
42 simple_vm_test::<_, _, KvmVcpu, _, _, _>(
43 |guest_mem| {
44 let kvm = Kvm::new().expect("failed to create kvm");
45 let vm = KvmVm::new(&kvm, guest_mem, ProtectionType::Unprotected)
46 .expect("failed to create kvm vm");
47 (kvm, vm)
48 },
49 |vm, vcpu_count, _| {
50 KvmKernelIrqChip::new(vm, vcpu_count).expect("failed to create KvmKernelIrqChip")
51 },
52 );
53 }
54
55 #[test]
simple_kvm_split_irqchip_test()56 fn simple_kvm_split_irqchip_test() {
57 use devices::KvmSplitIrqChip;
58 use hypervisor::kvm::*;
59 simple_vm_test::<_, _, KvmVcpu, _, _, _>(
60 |guest_mem| {
61 let kvm = Kvm::new().expect("failed to create kvm");
62 let vm = KvmVm::new(&kvm, guest_mem, ProtectionType::Unprotected)
63 .expect("failed to create kvm vm");
64 (kvm, vm)
65 },
66 |vm, vcpu_count, device_tube| {
67 KvmSplitIrqChip::new(vm, vcpu_count, device_tube, None)
68 .expect("failed to create KvmSplitIrqChip")
69 },
70 );
71 }
72
73 /// Tests the integration of x86_64 with some hypervisor and devices setup. This test can help
74 /// narrow down whether boot issues are caused by the interaction between hypervisor and devices
75 /// and x86_64, or if they are caused by an invalid kernel or image. You can also swap in parts
76 /// of this function to load a real kernel and/or ramdisk.
simple_vm_test<H, V, Vcpu, I, FV, FI>(create_vm: FV, create_irq_chip: FI) where H: HypervisorX86_64 + 'static, V: VmX86_64 + 'static, Vcpu: VcpuX86_64 + 'static, I: IrqChipX86_64 + 'static, FV: FnOnce(GuestMemory) -> (H, V), FI: FnOnce(V, usize, Tube) -> I,77 fn simple_vm_test<H, V, Vcpu, I, FV, FI>(create_vm: FV, create_irq_chip: FI)
78 where
79 H: HypervisorX86_64 + 'static,
80 V: VmX86_64 + 'static,
81 Vcpu: VcpuX86_64 + 'static,
82 I: IrqChipX86_64 + 'static,
83 FV: FnOnce(GuestMemory) -> (H, V),
84 FI: FnOnce(V, /* vcpu_count: */ usize, Tube) -> I,
85 {
86 /*
87 0x0000000000000000: 67 89 18 mov dword ptr [eax], ebx
88 0x0000000000000003: 89 D9 mov ecx, ebx
89 0x0000000000000005: 89 C8 mov eax, ecx
90 0x0000000000000007: E6 FF out 0xff, al
91 */
92 let code = [0x67, 0x89, 0x18, 0x89, 0xd9, 0x89, 0xc8, 0xe6, 0xff];
93
94 // 2GB memory
95 let memory_size = 0x80000000u64;
96 let start_addr = GuestAddress(KERNEL_START_OFFSET + KERNEL_64BIT_ENTRY_OFFSET);
97
98 // write to 4th page
99 let write_addr = GuestAddress(0x4000);
100
101 // guest mem is 400 pages
102 let arch_mem_regions = arch_memory_regions(memory_size, None);
103 let guest_mem = GuestMemory::new(&arch_mem_regions).unwrap();
104
105 let (hyp, mut vm) = create_vm(guest_mem.clone());
106 let mut resources =
107 SystemAllocator::new(X8664arch::get_system_allocator_config(&vm), None, &[])
108 .expect("failed to create system allocator");
109 let (irqchip_tube, device_tube) = Tube::pair().expect("failed to create irq tube");
110
111 let mut irq_chip = create_irq_chip(vm.try_clone().expect("failed to clone vm"), 1, device_tube);
112
113 let mmio_bus = Arc::new(devices::Bus::new());
114 let io_bus = Arc::new(devices::Bus::new());
115 let exit_evt = Event::new().unwrap();
116
117 let mut control_tubes = vec![TaggedControlTube::VmIrq(irqchip_tube)];
118 // Create one control socket per disk.
119 let mut disk_device_tubes = Vec::new();
120 let mut disk_host_tubes = Vec::new();
121 let disk_count = 0;
122 for _ in 0..disk_count {
123 let (disk_host_tube, disk_device_tube) = Tube::pair().unwrap();
124 disk_host_tubes.push(disk_host_tube);
125 disk_device_tubes.push(disk_device_tube);
126 }
127 let (gpu_host_tube, _gpu_device_tube) = Tube::pair().unwrap();
128
129 control_tubes.push(TaggedControlTube::VmMemory(gpu_host_tube));
130
131 let devices = vec![];
132
133 let (pci, pci_irqs, _pid_debug_label_map) = arch::generate_pci_root(
134 devices,
135 &mut irq_chip,
136 mmio_bus.clone(),
137 io_bus.clone(),
138 &mut resources,
139 &mut vm,
140 4,
141 )
142 .unwrap();
143 let pci = Arc::new(Mutex::new(pci));
144 let pci_bus = Arc::new(Mutex::new(PciConfigIo::new(pci, Event::new().unwrap())));
145 io_bus.insert(pci_bus, 0xcf8, 0x8).unwrap();
146
147 X8664arch::setup_legacy_devices(
148 &io_bus,
149 irq_chip.pit_uses_speaker_port(),
150 exit_evt.try_clone().unwrap(),
151 memory_size,
152 )
153 .unwrap();
154
155 let mut serial_params = BTreeMap::new();
156
157 arch::set_default_serial_parameters(&mut serial_params, false);
158
159 X8664arch::setup_serial_devices(
160 ProtectionType::Unprotected,
161 &mut irq_chip,
162 &io_bus,
163 &serial_params,
164 None,
165 )
166 .unwrap();
167
168 let param_args = "nokaslr acpi=noirq";
169
170 let mut cmdline = X8664arch::get_base_linux_cmdline();
171
172 cmdline.insert_str(¶m_args).unwrap();
173
174 let params = bootparam::boot_params::default();
175 // write our custom kernel code to start_addr
176 guest_mem.write_at_addr(&code[..], start_addr).unwrap();
177 let kernel_end = KERNEL_START_OFFSET + code.len() as u64;
178 let initrd_image = None;
179
180 // alternatively, load a real initrd and kernel from disk
181 // let initrd_image = Some(File::open("/mnt/host/source/src/avd/ramdisk.img").expect("failed to open ramdisk"));
182 // let mut kernel_image = File::open("/mnt/host/source/src/avd/vmlinux.uncompressed").expect("failed to open kernel");
183 // let (params, kernel_end) = X8664arch::load_kernel(&guest_mem, &mut kernel_image).expect("failed to load kernel");
184
185 let max_bus = (PCIE_CFG_MMIO_SIZE / 0x100000 - 1) as u8;
186 let suspend_evt = Event::new().unwrap();
187 let mut resume_notify_devices = Vec::new();
188 let acpi_dev_resource = X8664arch::setup_acpi_devices(
189 &guest_mem,
190 &io_bus,
191 &mut resources,
192 suspend_evt
193 .try_clone()
194 .expect("unable to clone suspend_evt"),
195 exit_evt.try_clone().expect("unable to clone exit_evt"),
196 Default::default(),
197 &mut irq_chip,
198 X86_64_SCI_IRQ,
199 (&None, None),
200 &mmio_bus,
201 max_bus,
202 &mut resume_notify_devices,
203 )
204 .unwrap();
205
206 X8664arch::setup_system_memory(
207 &guest_mem,
208 &CString::new(cmdline).expect("failed to create cmdline"),
209 initrd_image,
210 None,
211 kernel_end,
212 params,
213 )
214 .expect("failed to setup system_memory");
215
216 // Note that this puts the mptable at 0x9FC00 in guest physical memory.
217 mptable::setup_mptable(&guest_mem, 1, &pci_irqs).expect("failed to setup mptable");
218 smbios::setup_smbios(&guest_mem, None).expect("failed to setup smbios");
219
220 let mut apic_ids = Vec::new();
221 acpi::create_acpi_tables(
222 &guest_mem,
223 1,
224 X86_64_SCI_IRQ,
225 0xcf9,
226 6,
227 &acpi_dev_resource.0,
228 None,
229 &mut apic_ids,
230 &pci_irqs,
231 PCIE_CFG_MMIO_START,
232 max_bus,
233 false,
234 );
235
236 let guest_mem2 = guest_mem.clone();
237
238 let handle = thread::Builder::new()
239 .name("crosvm_simple_vm_vcpu".to_string())
240 .spawn(move || {
241 let vcpu = *vm
242 .create_vcpu(0)
243 .expect("failed to create vcpu")
244 .downcast::<Vcpu>()
245 .map_err(|_| ())
246 .expect("failed to downcast vcpu");
247
248 irq_chip
249 .add_vcpu(0, &vcpu)
250 .expect("failed to add vcpu to irqchip");
251
252 setup_cpuid(&hyp, &irq_chip, &vcpu, 0, 1, false, false).unwrap();
253 setup_msrs(&vm, &vcpu, END_ADDR_BEFORE_32BITS).unwrap();
254
255 setup_regs(
256 &vcpu,
257 start_addr.offset() as u64,
258 BOOT_STACK_POINTER as u64,
259 ZERO_PAGE_OFFSET as u64,
260 )
261 .unwrap();
262
263 let mut vcpu_regs = vcpu.get_regs().unwrap();
264 // instruction is
265 // mov [eax],ebx
266 // so we're writing 0x12 (the contents of ebx) to the address
267 // in eax (write_addr).
268 vcpu_regs.rax = write_addr.offset() as u64;
269 vcpu_regs.rbx = 0x12;
270 // ecx will contain 0, but after the second instruction it will
271 // also contain 0x12
272 vcpu_regs.rcx = 0x0;
273 vcpu.set_regs(&vcpu_regs).expect("set regs failed");
274
275 setup_fpu(&vcpu).unwrap();
276 setup_sregs(&guest_mem, &vcpu).unwrap();
277 set_lint(0, &mut irq_chip).unwrap();
278
279 let run_handle = vcpu.take_run_handle(None).unwrap();
280 loop {
281 match vcpu.run(&run_handle).expect("run failed") {
282 VcpuExit::IoOut {
283 port: 0xff,
284 size,
285 data,
286 } => {
287 // We consider this test to be done when this particular
288 // one-byte port-io to port 0xff with the value of 0x12, which was in
289 // register eax
290 assert_eq!(size, 1);
291 assert_eq!(data[0], 0x12);
292 break;
293 }
294 r => {
295 panic!("unexpected exit {:?}", r);
296 }
297 }
298 }
299 let regs = vcpu.get_regs().unwrap();
300 // ecx and eax should now contain 0x12
301 assert_eq!(regs.rcx, 0x12);
302 assert_eq!(regs.rax, 0x12);
303 })
304 .unwrap();
305
306 if let Err(e) = handle.join() {
307 panic!("failed to join vcpu thread: {:?}", e);
308 }
309
310 assert_eq!(
311 guest_mem2.read_obj_from_addr::<u64>(write_addr).unwrap(),
312 0x12
313 );
314 }
315