1
0

whpx.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  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. #![cfg(target_arch = "x86_64")]
  5. use base::EventWaitResult;
  6. use base::Tube;
  7. use devices::Bus;
  8. use devices::BusType;
  9. use devices::CrosvmDeviceId;
  10. use devices::DeviceId;
  11. use devices::IrqChip;
  12. use devices::IrqChipX86_64;
  13. use devices::IrqEdgeEvent;
  14. use devices::IrqEventSource;
  15. use devices::IrqLevelEvent;
  16. use devices::WhpxSplitIrqChip;
  17. use devices::IOAPIC_BASE_ADDRESS;
  18. use hypervisor::whpx::Whpx;
  19. use hypervisor::whpx::WhpxFeature;
  20. use hypervisor::whpx::WhpxVm;
  21. use hypervisor::CpuId;
  22. use hypervisor::IoapicRedirectionTableEntry;
  23. use hypervisor::IrqRoute;
  24. use hypervisor::IrqSource;
  25. use hypervisor::PicSelect;
  26. use hypervisor::PitRWMode;
  27. use hypervisor::TriggerMode;
  28. use hypervisor::Vm;
  29. use hypervisor::VmX86_64;
  30. use resources::AddressRange;
  31. use resources::SystemAllocator;
  32. use resources::SystemAllocatorConfig;
  33. use vm_memory::GuestAddress;
  34. use vm_memory::GuestMemory;
  35. use crate::x86_64::test_get_ioapic;
  36. use crate::x86_64::test_get_pit;
  37. use crate::x86_64::test_route_irq;
  38. use crate::x86_64::test_set_ioapic;
  39. use crate::x86_64::test_set_pic;
  40. use crate::x86_64::test_set_pit;
  41. fn split_supported() -> bool {
  42. Whpx::check_whpx_feature(WhpxFeature::LocalApicEmulation).expect("failed to get whpx features")
  43. }
  44. /// Helper function for setting up a WhpxSplitIrqChip.
  45. fn get_chip(num_vcpus: usize) -> WhpxSplitIrqChip {
  46. let whpx = Whpx::new().expect("failed to instantiate Whpx");
  47. let mem = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap();
  48. let vm = WhpxVm::new(&whpx, num_vcpus, mem, CpuId::new(0), true, None)
  49. .expect("failed to instantiate vm");
  50. let (_, irq_tube) = Tube::pair().expect("failed to create irq tube");
  51. let mut chip =
  52. WhpxSplitIrqChip::new(vm.try_clone().expect("failed to clone vm"), irq_tube, None)
  53. .expect("failed to instantiate WhpxSplitIrqChip");
  54. for i in 0..num_vcpus {
  55. let vcpu = vm.create_vcpu(i).expect("failed to instantiate vcpu");
  56. chip.add_vcpu(i, vcpu.as_vcpu())
  57. .expect("failed to add vcpu");
  58. }
  59. chip
  60. }
  61. #[test]
  62. fn set_pic() {
  63. if !split_supported() {
  64. return;
  65. }
  66. test_set_pic(get_chip(1));
  67. }
  68. #[test]
  69. fn get_ioapic() {
  70. if !split_supported() {
  71. return;
  72. }
  73. test_get_ioapic(get_chip(1));
  74. }
  75. #[test]
  76. fn set_ioapic() {
  77. if !split_supported() {
  78. return;
  79. }
  80. test_set_ioapic(get_chip(1));
  81. }
  82. #[test]
  83. fn get_pit() {
  84. if !split_supported() {
  85. return;
  86. }
  87. test_get_pit(get_chip(1));
  88. }
  89. #[test]
  90. fn set_pit() {
  91. if !split_supported() {
  92. return;
  93. }
  94. test_set_pit(get_chip(1));
  95. }
  96. #[test]
  97. fn route_irq() {
  98. if !split_supported() {
  99. return;
  100. }
  101. test_route_irq(get_chip(1));
  102. }
  103. #[test]
  104. fn pit_uses_speaker_port() {
  105. if !split_supported() {
  106. return;
  107. }
  108. let chip = get_chip(1);
  109. assert!(chip.pit_uses_speaker_port());
  110. }
  111. #[test]
  112. fn routes_conflict() {
  113. if !split_supported() {
  114. return;
  115. }
  116. let mut chip = get_chip(1);
  117. chip.route_irq(IrqRoute {
  118. gsi: 32,
  119. source: IrqSource::Msi {
  120. address: 4276092928,
  121. data: 0,
  122. },
  123. })
  124. .expect("failed to set msi route");
  125. // this second route should replace the first
  126. chip.route_irq(IrqRoute {
  127. gsi: 32,
  128. source: IrqSource::Msi {
  129. address: 4276092928,
  130. data: 32801,
  131. },
  132. })
  133. .expect("failed to set msi route");
  134. }
  135. #[test]
  136. fn irq_event_tokens() {
  137. if !split_supported() {
  138. return;
  139. }
  140. let mut chip = get_chip(1);
  141. let tokens = chip
  142. .irq_event_tokens()
  143. .expect("could not get irq_event_tokens");
  144. // there should be one token on a fresh split irqchip, for the pit
  145. assert_eq!(tokens.len(), 1);
  146. assert_eq!(tokens[0].1.device_name, "userspace PIT");
  147. // register another irq event
  148. let evt = IrqEdgeEvent::new().expect("failed to create event");
  149. let source = IrqEventSource {
  150. device_id: CrosvmDeviceId::DebugConsole.into(),
  151. queue_id: 0,
  152. device_name: "test".to_owned(),
  153. };
  154. chip.register_edge_irq_event(6, &evt, source)
  155. .expect("failed to register irq event");
  156. let tokens = chip
  157. .irq_event_tokens()
  158. .expect("could not get irq_event_tokens");
  159. // now there should be two tokens
  160. assert_eq!(tokens.len(), 2);
  161. assert_eq!(tokens[0].1.device_name, "userspace PIT");
  162. assert_eq!(
  163. tokens[1].1.device_id,
  164. DeviceId::PlatformDeviceId(CrosvmDeviceId::DebugConsole)
  165. );
  166. assert_eq!(tokens[1].2, evt.get_trigger().try_clone().unwrap());
  167. }
  168. // TODO(srichman): Factor out of UserspaceIrqChip and KvmSplitIrqChip and WhpxSplitIrqChip.
  169. #[test]
  170. fn finalize_devices() {
  171. if !split_supported() {
  172. return;
  173. }
  174. let mut chip = get_chip(1);
  175. let mmio_bus = Bus::new(BusType::Mmio);
  176. let io_bus = Bus::new(BusType::Io);
  177. let mut resources = SystemAllocator::new(
  178. SystemAllocatorConfig {
  179. io: Some(AddressRange {
  180. start: 0xc000,
  181. end: 0xFFFF,
  182. }),
  183. low_mmio: AddressRange {
  184. start: 0,
  185. end: 2048,
  186. },
  187. high_mmio: AddressRange {
  188. start: 2048,
  189. end: 6143,
  190. },
  191. platform_mmio: None,
  192. first_irq: 5,
  193. },
  194. None,
  195. &[],
  196. )
  197. .expect("failed to create SystemAllocator");
  198. // setup an event for irq line 1
  199. let evt = IrqLevelEvent::new().expect("failed to create event");
  200. let source = IrqEventSource {
  201. device_id: CrosvmDeviceId::DebugConsole.into(),
  202. device_name: "test".to_owned(),
  203. queue_id: 0,
  204. };
  205. let evt_index = chip
  206. .register_level_irq_event(1, &evt, source)
  207. .expect("failed to register_level_irq_event")
  208. .expect("register_level_irq_event should not return None");
  209. // Once we finalize devices, the pic/pit/ioapic should be attached to io and mmio busses
  210. chip.finalize_devices(&mut resources, &io_bus, &mmio_bus)
  211. .expect("failed to finalize devices");
  212. // Should not be able to allocate an irq < 24 now
  213. assert!(resources.allocate_irq().expect("failed to allocate irq") >= 24);
  214. // set PIT counter 2 to "SquareWaveGen"(aka 3) mode and "Both" access mode
  215. io_bus.write(0x43, &[0b10110110]);
  216. let state = chip.get_pit().expect("failed to get pit state");
  217. assert_eq!(state.channels[2].mode, 3);
  218. assert_eq!(state.channels[2].rw_mode, PitRWMode::Both);
  219. // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
  220. // ICW2 0x08: Interrupt vector base address 0x08.
  221. // ICW3 0xff: Value written does not matter.
  222. // ICW4 0x13: Special fully nested mode, auto EOI.
  223. io_bus.write(0x20, &[0x11]);
  224. io_bus.write(0x21, &[0x08]);
  225. io_bus.write(0x21, &[0xff]);
  226. io_bus.write(0x21, &[0x13]);
  227. let state = chip
  228. .get_pic_state(PicSelect::Primary)
  229. .expect("failed to get pic state");
  230. // auto eoi and special fully nested mode should be turned on
  231. assert!(state.auto_eoi);
  232. assert!(state.special_fully_nested_mode);
  233. // Need to write to the irq event before servicing it
  234. evt.trigger().expect("failed to write to event");
  235. // if we assert irq line one, and then get the resulting interrupt, an auto-eoi should
  236. // occur and cause the resample_event to be written to
  237. chip.service_irq_event(evt_index)
  238. .expect("failed to service irq");
  239. assert!(chip.interrupt_requested(0));
  240. assert_eq!(
  241. chip.get_external_interrupt(0)
  242. .expect("failed to get external interrupt"),
  243. // Vector is 9 because the interrupt vector base address is 0x08 and this is irq
  244. // line 1 and 8+1 = 9
  245. Some(0x9)
  246. );
  247. assert_eq!(
  248. evt.get_resample()
  249. .wait_timeout(std::time::Duration::from_secs(1))
  250. .expect("failed to read_timeout"),
  251. EventWaitResult::Signaled
  252. );
  253. // setup a ioapic redirection table entry 14
  254. let mut entry = IoapicRedirectionTableEntry::default();
  255. entry.set_vector(44);
  256. let irq_14_offset = 0x10 + 14 * 2;
  257. mmio_bus.write(IOAPIC_BASE_ADDRESS, &[irq_14_offset]);
  258. mmio_bus.write(
  259. IOAPIC_BASE_ADDRESS + 0x10,
  260. &(entry.get(0, 32) as u32).to_ne_bytes(),
  261. );
  262. mmio_bus.write(IOAPIC_BASE_ADDRESS, &[irq_14_offset + 1]);
  263. mmio_bus.write(
  264. IOAPIC_BASE_ADDRESS + 0x10,
  265. &(entry.get(32, 32) as u32).to_ne_bytes(),
  266. );
  267. let state = chip.get_ioapic_state().expect("failed to get ioapic state");
  268. // redirection table entry 14 should have a vector of 44
  269. assert_eq!(state.redirect_table[14].get_vector(), 44);
  270. }
  271. // TODO(srichman): Factor out of UserspaceIrqChip and KvmSplitIrqChip and WhpxSplitIrqChip.
  272. #[test]
  273. fn broadcast_eoi() {
  274. if !split_supported() {
  275. return;
  276. }
  277. let mut chip = get_chip(1);
  278. let mmio_bus = Bus::new(BusType::Mmio);
  279. let io_bus = Bus::new(BusType::Io);
  280. let mut resources = SystemAllocator::new(
  281. SystemAllocatorConfig {
  282. io: Some(AddressRange {
  283. start: 0xc000,
  284. end: 0xFFFF,
  285. }),
  286. low_mmio: AddressRange {
  287. start: 0,
  288. end: 2048,
  289. },
  290. high_mmio: AddressRange {
  291. start: 2048,
  292. end: 6143,
  293. },
  294. platform_mmio: None,
  295. first_irq: 5,
  296. },
  297. None,
  298. &[],
  299. )
  300. .expect("failed to create SystemAllocator");
  301. // setup an event for irq line 1
  302. let evt = IrqLevelEvent::new().expect("failed to create event");
  303. let source = IrqEventSource {
  304. device_id: CrosvmDeviceId::DebugConsole.into(),
  305. device_name: "test".to_owned(),
  306. queue_id: 0,
  307. };
  308. chip.register_level_irq_event(1, &evt, source)
  309. .expect("failed to register_level_irq_event");
  310. // Once we finalize devices, the pic/pit/ioapic should be attached to io and mmio busses
  311. chip.finalize_devices(&mut resources, &io_bus, &mmio_bus)
  312. .expect("failed to finalize devices");
  313. // setup a ioapic redirection table entry 1 with a vector of 123
  314. let mut entry = IoapicRedirectionTableEntry::default();
  315. entry.set_vector(123);
  316. entry.set_trigger_mode(TriggerMode::Level);
  317. let irq_write_offset = 0x10 + 1 * 2;
  318. mmio_bus.write(IOAPIC_BASE_ADDRESS, &[irq_write_offset]);
  319. mmio_bus.write(
  320. IOAPIC_BASE_ADDRESS + 0x10,
  321. &(entry.get(0, 32) as u32).to_ne_bytes(),
  322. );
  323. mmio_bus.write(IOAPIC_BASE_ADDRESS, &[irq_write_offset + 1]);
  324. mmio_bus.write(
  325. IOAPIC_BASE_ADDRESS + 0x10,
  326. &(entry.get(32, 32) as u32).to_ne_bytes(),
  327. );
  328. // Assert line 1
  329. chip.service_irq(1, true).expect("failed to service irq");
  330. // resample event should not be written to
  331. assert_eq!(
  332. evt.get_resample()
  333. .wait_timeout(std::time::Duration::from_millis(10))
  334. .expect("failed to read_timeout"),
  335. EventWaitResult::TimedOut
  336. );
  337. // irq line 1 should be asserted
  338. let state = chip.get_ioapic_state().expect("failed to get ioapic state");
  339. assert_eq!(state.current_interrupt_level_bitmap, 1 << 1);
  340. // Now broadcast an eoi for vector 123
  341. chip.broadcast_eoi(123).expect("failed to broadcast eoi");
  342. // irq line 1 should be deasserted
  343. let state = chip.get_ioapic_state().expect("failed to get ioapic state");
  344. assert_eq!(state.current_interrupt_level_bitmap, 0);
  345. // resample event should be written to by ioapic
  346. assert_eq!(
  347. evt.get_resample()
  348. .wait_timeout(std::time::Duration::from_millis(10))
  349. .expect("failed to read_timeout"),
  350. EventWaitResult::Signaled
  351. );
  352. }