• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&param_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