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