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(¶m_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