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