1use crate::ChipsetDeviceHandle;
8use crate::PowerEvent;
9use crate::chipset::ChipsetBuilder;
10use crate::chipset::backing::arc_mutex::device::AddDeviceError;
11use crate::chipset::backing::arc_mutex::services::ArcMutexChipsetServices;
12use chipset::*;
13use chipset_device::interrupt::LineInterruptTarget;
14use chipset_device_resources::BSP_LINT_LINE_SET;
15use chipset_device_resources::ConfigureChipsetDevice;
16use chipset_device_resources::GPE0_LINE_SET;
17use chipset_device_resources::IRQ_LINE_SET;
18use chipset_device_resources::ResolveChipsetDeviceHandleParams;
19use closeable_mutex::CloseableMutex;
20use cvm_tracing::CVM_ALLOWED;
21use firmware_uefi::UefiCommandSet;
22use framebuffer::Framebuffer;
23use framebuffer::FramebufferDevice;
24use framebuffer::FramebufferLocalControl;
25use guestmem::DoorbellRegistration;
26use guestmem::GuestMemory;
27use mesh::MeshPayload;
28use state_unit::StateUnits;
29use std::fmt::Debug;
30use std::sync::Arc;
31use thiserror::Error;
32use vm_resource::ResourceResolver;
33use vmcore::vm_task::VmTaskDriverSource;
34
35#[expect(missing_docs)] #[derive(Error, Debug)]
38pub enum BaseChipsetBuilderError {
39 #[error(transparent)]
42 AddDevice(#[from] AddDeviceError),
43 #[error("no valid interrupt controller")]
44 MissingInterruptController,
45 #[error("attempted to add feature-gated device (requires {0})")]
46 FeatureGatedDevice(&'static str),
47 #[error("no valid ISA DMA controller for floppy")]
48 NoDmaForFloppy,
49}
50
51#[expect(missing_docs)] pub struct BaseChipsetDeviceInterfaces {
58 pub framebuffer_local_control: Option<FramebufferLocalControl>,
59}
60
61pub struct BaseChipsetBuilderOutput<'a> {
63 pub chipset_builder: ChipsetBuilder<'a>,
65 pub device_interfaces: BaseChipsetDeviceInterfaces,
68}
69
70pub struct BaseChipsetBuilder<'a> {
76 foundation: options::BaseChipsetFoundation<'a>,
77 devices: options::BaseChipsetDevices,
78 device_handles: Vec<ChipsetDeviceHandle>,
79 expected_manifest: Option<options::BaseChipsetManifest>,
80 fallback_mmio_device: Option<Arc<CloseableMutex<dyn chipset_device::ChipsetDevice>>>,
81 flags: BaseChipsetBuilderFlags,
82}
83
84struct BaseChipsetBuilderFlags {
85 trace_unknown_pio: bool,
86 trace_unknown_mmio: bool,
87}
88
89impl<'a> BaseChipsetBuilder<'a> {
90 pub fn new(
92 foundation: options::BaseChipsetFoundation<'a>,
93 devices: options::BaseChipsetDevices,
94 ) -> Self {
95 BaseChipsetBuilder {
96 foundation,
97 devices,
98 device_handles: Vec::new(),
99 expected_manifest: None,
100 fallback_mmio_device: None,
101 flags: BaseChipsetBuilderFlags {
102 trace_unknown_pio: false,
117 trace_unknown_mmio: true,
118 },
119 }
120 }
121
122 pub fn with_expected_manifest(
125 mut self,
126 expected_manifest: options::BaseChipsetManifest,
127 ) -> Self {
128 self.expected_manifest = Some(expected_manifest);
129 self
130 }
131
132 pub fn with_device_handles(mut self, mut device_handles: Vec<ChipsetDeviceHandle>) -> Self {
134 self.device_handles.append(&mut device_handles);
135 self
136 }
137
138 pub fn with_trace_unknown_pio(mut self, active: bool) -> Self {
142 self.flags.trace_unknown_pio = active;
143 self
144 }
145
146 pub fn with_trace_unknown_mmio(mut self, active: bool) -> Self {
150 self.flags.trace_unknown_mmio = active;
151 self
152 }
153
154 pub fn with_fallback_mmio_device(
157 mut self,
158 fallback_mmio_device: Option<Arc<CloseableMutex<dyn chipset_device::ChipsetDevice>>>,
159 ) -> Self {
160 self.fallback_mmio_device = fallback_mmio_device;
161 self
162 }
163
164 pub async fn build(
169 self,
170 driver_source: &'a VmTaskDriverSource,
171 units: &'a StateUnits,
172 resolver: &ResourceResolver,
173 ) -> Result<BaseChipsetBuilderOutput<'a>, BaseChipsetBuilderError> {
174 let Self {
175 foundation,
176 devices,
177 device_handles,
178 expected_manifest,
179 fallback_mmio_device,
180 flags,
181 } = self;
182
183 let manifest = devices.to_manifest();
184 if let Some(expected_manifest) = expected_manifest {
185 assert_eq!(expected_manifest, manifest, "manifests do not match");
186 }
187
188 let mut device_interfaces = BaseChipsetDeviceInterfaces {
189 framebuffer_local_control: None,
190 };
191
192 let mut builder = ChipsetBuilder::new(
193 driver_source,
194 units,
195 foundation.debug_event_handler.clone(),
196 foundation.vmtime,
197 foundation.vmtime_unit,
198 flags.trace_unknown_pio,
199 flags.trace_unknown_mmio,
200 fallback_mmio_device,
201 );
202
203 let options::BaseChipsetDevices {
205 deps_generic_cmos_rtc,
206 deps_generic_ioapic,
207 deps_generic_isa_dma,
208 deps_generic_isa_floppy,
209 deps_generic_pci_bus,
210 deps_generic_pic,
211 deps_generic_pit,
212 deps_generic_psp: _, deps_hyperv_firmware_pcat,
214 deps_hyperv_firmware_uefi,
215 deps_hyperv_framebuffer,
216 deps_hyperv_guest_watchdog,
217 deps_hyperv_ide,
218 deps_hyperv_power_management,
219 deps_hyperv_vga,
220 deps_i440bx_host_pci_bridge,
221 deps_piix4_cmos_rtc,
222 deps_piix4_pci_bus,
223 deps_piix4_pci_isa_bridge,
224 deps_piix4_pci_usb_uhci_stub,
225 deps_piix4_power_management,
226 deps_underhill_vga_proxy,
227 deps_winbond_super_io_and_floppy_stub,
228 deps_winbond_super_io_and_floppy_full,
229 } = devices;
230
231 if let Some(options::dev::GenericPicDeps {}) = deps_generic_pic {
232 builder.arc_mutex_device("pic").add(|services| {
233 services.add_line_target(IRQ_LINE_SET, 1..=1, 1);
238 services.add_line_target(IRQ_LINE_SET, 2..=2, 0);
239 services.add_line_target(IRQ_LINE_SET, 3..=15, 3);
240
241 pic::DualPic::new(
243 services.new_line(BSP_LINT_LINE_SET, "ready", 0),
244 &mut services.register_pio(),
245 )
246 })?;
247 }
248
249 if let Some(options::dev::GenericIoApicDeps {
250 num_entries,
251 routing,
252 }) = deps_generic_ioapic
253 {
254 builder.arc_mutex_device("ioapic").add(|services| {
255 services.add_line_target(IRQ_LINE_SET, 0..=num_entries as u32 - 1, 0);
256 ioapic::IoApicDevice::new(num_entries, routing)
257 })?;
258 }
259
260 if let Some(options::dev::GenericPciBusDeps {
261 bus_id,
262 pio_addr,
263 pio_data,
264 }) = deps_generic_pci_bus
265 {
266 let pci = builder.arc_mutex_device("pci_bus").add(|services| {
267 pci_bus::GenericPciBus::new(&mut services.register_pio(), pio_addr, pio_data)
268 })?;
269
270 builder.register_weak_mutex_pci_bus(bus_id, Box::new(pci));
271 }
272
273 if let Some(options::dev::Piix4PciBusDeps { bus_id }) = deps_piix4_pci_bus {
274 let reset = {
276 let power = foundation.power_event_handler.clone();
277 Box::new(move || power.on_power_event(PowerEvent::Reset))
278 };
279
280 let pci = builder.arc_mutex_device("piix4-pci-bus").add(|services| {
281 chipset_legacy::piix4_pci_bus::Piix4PciBus::new(
282 &mut services.register_pio(),
283 reset.clone(),
284 )
285 })?;
286 builder.register_weak_mutex_pci_bus(bus_id, Box::new(pci));
287 }
288
289 if let Some(options::dev::I440BxHostPciBridgeDeps {
290 attached_to,
291 adjust_gpa_range,
292 }) = deps_i440bx_host_pci_bridge
293 {
294 builder
295 .arc_mutex_device("440bx-host-pci-bridge")
296 .on_pci_bus(attached_to)
297 .add(|_| {
298 chipset_legacy::i440bx_host_pci_bridge::HostPciBridge::new(
299 adjust_gpa_range,
300 foundation.is_restoring,
301 )
302 })?;
303 }
304
305 let dma = {
306 if let Some(options::dev::GenericIsaDmaDeps {}) = deps_generic_isa_dma {
307 let dma = builder
308 .arc_mutex_device("dma")
309 .add(|_| dma::DmaController::new())?;
310 Some(dma)
311 } else {
312 None
313 }
314 };
315
316 if let Some(options::dev::Piix4PciIsaBridgeDeps { attached_to }) = deps_piix4_pci_isa_bridge
317 {
318 let reset = {
320 let power = foundation.power_event_handler.clone();
321 Box::new(move || power.on_power_event(PowerEvent::Reset))
322 };
323
324 let set_a20_signal = Box::new(move |active| {
325 tracing::info!(CVM_ALLOWED, active, "setting stubbed A20 signal")
326 });
327
328 builder
329 .arc_mutex_device("piix4-pci-isa-bridge")
330 .on_pci_bus(attached_to)
331 .add(|_| {
332 chipset_legacy::piix4_pci_isa_bridge::PciIsaBridge::new(
333 reset.clone(),
334 set_a20_signal,
335 )
336 })?;
337 }
338
339 if let Some(options::dev::Piix4PciUsbUhciStubDeps { attached_to }) =
340 deps_piix4_pci_usb_uhci_stub
341 {
342 builder
343 .arc_mutex_device("piix4-usb-uhci-stub")
344 .on_pci_bus(attached_to)
345 .add(|_| chipset_legacy::piix4_uhci::Piix4UsbUhciStub::new())?;
346 }
347
348 if let Some(options::dev::GenericPitDeps {}) = deps_generic_pit {
349 builder.arc_mutex_device("pit").add(|services| {
351 pit::PitDevice::new(
352 services.new_line(IRQ_LINE_SET, "timer0", 2),
353 services.register_vmtime().access("pit"),
354 )
355 })?;
356 }
357
358 let _ = dma;
359 #[cfg(feature = "dev_generic_isa_floppy")]
360 if let Some(options::dev::GenericIsaFloppyDeps {
361 irq,
362 dma_channel: dma_chan,
363 pio_base,
364 drives,
365 }) = deps_generic_isa_floppy
366 {
367 if let Some(dma) = &dma {
368 let dma_channel = ArcMutexIsaDmaChannel::new(dma.clone(), dma_chan);
369
370 builder.arc_mutex_device("floppy").try_add(|services| {
371 let interrupt = services.new_line(IRQ_LINE_SET, "interrupt", irq);
372 floppy::FloppyDiskController::new(
373 foundation.untrusted_dma_memory.clone(),
374 interrupt,
375 &mut services.register_pio(),
376 pio_base,
377 drives,
378 Box::new(dma_channel),
379 )
380 })?;
381 } else {
382 return Err(BaseChipsetBuilderError::NoDmaForFloppy);
383 }
384 }
385
386 #[cfg(feature = "dev_winbond_super_io_and_floppy_full")]
387 if let Some(options::dev::WinbondSuperIoAndFloppyFullDeps {
388 primary_disk_drive,
389 secondary_disk_drive,
390 }) = deps_winbond_super_io_and_floppy_full
391 {
392 if let Some(dma) = &dma {
393 let primary_dma = Box::new(ArcMutexIsaDmaChannel::new(dma.clone(), 2));
396 let secondary_dma = Box::new(vmcore::isa_dma_channel::FloatingDmaChannel);
397
398 builder.arc_mutex_device("floppy-sio").try_add(|services| {
399 let interrupt = services.new_line(IRQ_LINE_SET, "interrupt", 6);
400 chipset_legacy::winbond83977_sio::Winbond83977FloppySioDevice::<
401 floppy::FloppyDiskController,
402 >::new(
403 foundation.untrusted_dma_memory.clone(),
404 interrupt,
405 &mut services.register_pio(),
406 primary_disk_drive,
407 secondary_disk_drive,
408 primary_dma,
409 secondary_dma,
410 )
411 })?;
412 } else {
413 return Err(BaseChipsetBuilderError::NoDmaForFloppy);
414 }
415 }
416
417 #[cfg(feature = "dev_winbond_super_io_and_floppy_stub")]
418 if let Some(options::dev::WinbondSuperIoAndFloppyStubDeps) =
419 deps_winbond_super_io_and_floppy_stub
420 {
421 if let Some(dma) = &dma {
422 let primary_dma = Box::new(ArcMutexIsaDmaChannel::new(dma.clone(), 2));
425 let secondary_dma = Box::new(vmcore::isa_dma_channel::FloatingDmaChannel);
426
427 builder.arc_mutex_device("floppy-sio").try_add(|services| {
428 let interrupt = services.new_line(IRQ_LINE_SET, "interrupt", 6);
429 chipset_legacy::winbond83977_sio::Winbond83977FloppySioDevice::<
430 floppy_pcat_stub::StubFloppyDiskController,
431 >::new(
432 foundation.untrusted_dma_memory.clone(),
433 interrupt,
434 &mut services.register_pio(),
435 floppy::DriveRibbon::None,
436 floppy::DriveRibbon::None,
437 primary_dma,
438 secondary_dma,
439 )
440 })?;
441 } else {
442 return Err(BaseChipsetBuilderError::NoDmaForFloppy);
443 }
444 }
445
446 if let Some(options::dev::HyperVIdeDeps {
447 attached_to,
448 primary_channel_drives,
449 secondary_channel_drives,
450 }) = deps_hyperv_ide
451 {
452 builder
453 .arc_mutex_device("ide")
454 .on_pci_bus(attached_to)
455 .try_add(|services| {
456 let primary_channel_line_interrupt =
458 services.new_line(IRQ_LINE_SET, "ide1", 14);
459 let secondary_channel_line_interrupt =
460 services.new_line(IRQ_LINE_SET, "ide2", 15);
461 ide::IdeDevice::new(
462 foundation.untrusted_dma_memory.clone(),
463 &mut services.register_pio(),
464 primary_channel_drives,
465 secondary_channel_drives,
466 primary_channel_line_interrupt,
467 secondary_channel_line_interrupt,
468 )
469 })?;
470 }
471
472 if let Some(options::dev::GenericCmosRtcDeps {
473 irq,
474 time_source,
475 century_reg_idx,
476 initial_cmos,
477 }) = deps_generic_cmos_rtc
478 {
479 builder.arc_mutex_device("rtc").add(|services| {
480 cmos_rtc::Rtc::new(
481 time_source,
482 services.new_line(IRQ_LINE_SET, "interrupt", irq),
483 services.register_vmtime(),
484 century_reg_idx,
485 initial_cmos,
486 false,
487 )
488 })?;
489 }
490
491 if let Some(options::dev::Piix4CmosRtcDeps {
492 time_source,
493 initial_cmos,
494 enlightened_interrupts,
495 }) = deps_piix4_cmos_rtc
496 {
497 builder.arc_mutex_device("piix4-rtc").add(|services| {
498 let rtc_interrupt = services.new_line(IRQ_LINE_SET, "interrupt", 8);
500 chipset_legacy::piix4_cmos_rtc::Piix4CmosRtc::new(
501 time_source,
502 rtc_interrupt,
503 services.register_vmtime(),
504 initial_cmos,
505 enlightened_interrupts,
506 )
507 })?;
508 }
509
510 const GPE0_LINE_GENERATION_ID: u32 = 0;
513 const GENERATION_ID_IRQ: u32 = 3;
516
517 let pm_action = || {
519 let power = foundation.power_event_handler.clone();
520 move |action: pm::PowerAction| {
521 tracing::info!(CVM_ALLOWED, ?action, "guest initiated");
522 let req = match action {
523 pm::PowerAction::PowerOff => PowerEvent::PowerOff,
524 pm::PowerAction::Hibernate => PowerEvent::Hibernate,
525 pm::PowerAction::Reboot => PowerEvent::Reset,
526 };
527 power.on_power_event(req);
528 }
529 };
530
531 if let Some(options::dev::HyperVPowerManagementDeps {
532 acpi_irq,
533 pio_base: pio_dynamic_reg_base,
534 pm_timer_assist,
535 }) = deps_hyperv_power_management
536 {
537 builder.arc_mutex_device("pm").add(|services| {
538 let pm = pm::PowerManagementDevice::new(
539 Box::new(pm_action()),
540 services.new_line(IRQ_LINE_SET, "gpe0", acpi_irq),
541 &mut services.register_pio(),
542 services.register_vmtime().access("pm"),
543 Some(pm::EnableAcpiMode {
544 default_pio_dynamic: pio_dynamic_reg_base,
545 }),
546 pm_timer_assist,
547 );
548 for range in pm.valid_lines() {
549 services.add_line_target(GPE0_LINE_SET, range.clone(), *range.start());
550 }
551 pm
552 })?;
553 }
554
555 if let Some(options::dev::Piix4PowerManagementDeps {
556 attached_to,
557 pm_timer_assist,
558 }) = deps_piix4_power_management
559 {
560 builder
561 .arc_mutex_device("piix4-pm")
562 .on_pci_bus(attached_to)
563 .add(|services| {
564 let interrupt = services.new_line(IRQ_LINE_SET, "acpi", 9);
566 let pm = chipset_legacy::piix4_pm::Piix4Pm::new(
567 Box::new(pm_action()),
568 interrupt,
569 &mut services.register_pio(),
570 services.register_vmtime().access("piix4-pm"),
571 pm_timer_assist,
572 );
573 for range in pm.valid_lines() {
574 services.add_line_target(GPE0_LINE_SET, range.clone(), *range.start());
575 }
576 pm
577 })?;
578 }
579
580 if let Some(options::dev::HyperVGuestWatchdogDeps {
581 watchdog_platform,
582 port_base: pio_wdat_port,
583 }) = deps_hyperv_guest_watchdog
584 {
585 builder
586 .arc_mutex_device("guest-watchdog")
587 .add_async(async |services| {
588 let vmtime = services.register_vmtime();
589 let mut register_pio = services.register_pio();
590 guest_watchdog::GuestWatchdogServices::new(
591 vmtime.access("guest-watchdog-time"),
592 watchdog_platform,
593 &mut register_pio,
594 pio_wdat_port,
595 foundation.is_restoring,
596 )
597 .await
598 })
599 .await?;
600 }
601
602 if let Some(options::dev::HyperVFirmwareUefi {
603 config,
604 logger,
605 nvram_storage,
606 generation_id_recv,
607 watchdog_platform,
608 watchdog_recv,
609 vsm_config,
610 time_source,
611 }) = deps_hyperv_firmware_uefi
612 {
613 builder
614 .arc_mutex_device("uefi")
615 .try_add_async(async |services| {
616 let notify_interrupt = match config.command_set {
617 UefiCommandSet::X64 => {
618 services.new_line(GPE0_LINE_SET, "genid", GPE0_LINE_GENERATION_ID)
619 }
620 UefiCommandSet::Aarch64 => {
621 services.new_line(IRQ_LINE_SET, "genid", GENERATION_ID_IRQ)
622 }
623 };
624 let vmtime = services.register_vmtime();
625 let gm = foundation.trusted_vtl0_dma_memory.clone();
626 let runtime_deps = firmware_uefi::UefiRuntimeDeps {
627 gm: gm.clone(),
628 nvram_storage,
629 logger,
630 vmtime,
631 watchdog_platform,
632 watchdog_recv,
633 generation_id_deps: generation_id::GenerationIdRuntimeDeps {
634 generation_id_recv,
635 gm,
636 notify_interrupt,
637 },
638 vsm_config,
639 time_source,
640 };
641
642 firmware_uefi::UefiDevice::new(runtime_deps, config, foundation.is_restoring)
643 .await
644 })
645 .await?;
646 }
647
648 if let Some(options::dev::HyperVFirmwarePcat {
649 config,
650 logger,
651 generation_id_recv,
652 rom,
653 replay_mtrrs,
654 }) = deps_hyperv_firmware_pcat
655 {
656 builder.arc_mutex_device("pcat").try_add(|services| {
657 let notify_interrupt =
658 services.new_line(GPE0_LINE_SET, "genid", GPE0_LINE_GENERATION_ID);
659 firmware_pcat::PcatBiosDevice::new(
660 firmware_pcat::PcatBiosRuntimeDeps {
661 gm: foundation.trusted_vtl0_dma_memory.clone(),
662 logger,
663 generation_id_deps: generation_id::GenerationIdRuntimeDeps {
664 generation_id_recv,
665 gm: foundation.trusted_vtl0_dma_memory.clone(),
666 notify_interrupt,
667 },
668 vmtime: services.register_vmtime(),
669 rom,
670 register_pio: &mut services.register_pio(),
671 replay_mtrrs,
672 },
673 config,
674 )
675 })?;
676 }
677
678 if let Some(options::dev::HyperVFramebufferDeps {
679 fb_mapper,
680 fb,
681 vtl2_framebuffer_gpa_base,
682 }) = deps_hyperv_framebuffer
683 {
684 let fb = FramebufferDevice::new(fb_mapper, fb, vtl2_framebuffer_gpa_base);
685 let control = fb.as_ref().ok().map(|fb| fb.control());
686 builder.arc_mutex_device("fb").try_add(|_| fb)?;
687 device_interfaces.framebuffer_local_control = Some(control.unwrap());
688 }
689
690 #[cfg(feature = "dev_hyperv_vga")]
691 if let Some(options::dev::HyperVVgaDeps { attached_to, rom }) = deps_hyperv_vga {
692 builder
693 .arc_mutex_device("vga")
694 .on_pci_bus(attached_to)
695 .try_add(|services| {
696 vga::VgaDevice::new(
697 &driver_source.simple(),
698 services.register_vmtime(),
699 device_interfaces.framebuffer_local_control.clone().unwrap(),
700 rom,
701 )
702 })?;
703 }
704
705 #[cfg(feature = "dev_underhill_vga_proxy")]
706 if let Some(options::dev::UnderhillVgaProxyDeps {
707 attached_to,
708 pci_cfg_proxy,
709 register_host_io_fastpath,
710 }) = deps_underhill_vga_proxy
711 {
712 builder
713 .arc_mutex_device("vga_proxy")
714 .on_pci_bus(attached_to)
715 .add(|_services| {
716 vga_proxy::VgaProxyDevice::new(pci_cfg_proxy, &*register_host_io_fastpath)
717 })?;
718 }
719
720 macro_rules! feature_gate_check {
721 ($feature:literal, $dep:ident) => {
722 #[cfg(not(feature = $feature))]
723 let None::<()> = $dep else {
724 return Err(BaseChipsetBuilderError::FeatureGatedDevice($feature));
725 };
726 };
727 }
728
729 feature_gate_check!("dev_hyperv_vga", deps_hyperv_vga);
730 feature_gate_check!("dev_underhill_vga_proxy", deps_underhill_vga_proxy);
731 feature_gate_check!("dev_generic_isa_floppy", deps_generic_isa_floppy);
732 feature_gate_check!(
733 "dev_winbond_super_io_and_floppy_full",
734 deps_winbond_super_io_and_floppy_full
735 );
736 feature_gate_check!(
737 "dev_winbond_super_io_and_floppy_stub",
738 deps_winbond_super_io_and_floppy_stub
739 );
740
741 for device in device_handles {
742 builder
743 .arc_mutex_device(device.name.as_ref())
744 .try_add_async(async |services| {
745 resolver
746 .resolve(
747 device.resource,
748 ResolveChipsetDeviceHandleParams {
749 device_name: device.name.as_ref(),
750 guest_memory: &foundation.untrusted_dma_memory,
751 encrypted_guest_memory: &foundation.trusted_vtl0_dma_memory,
752 vmtime: foundation.vmtime,
753 is_restoring: foundation.is_restoring,
754 task_driver_source: driver_source,
755 register_mmio: &mut services.register_mmio(),
756 register_pio: &mut services.register_pio(),
757 configure: services,
758 },
759 )
760 .await
761 .map(|dev| dev.0)
762 })
763 .await?;
764 }
765
766 Ok(BaseChipsetBuilderOutput {
767 chipset_builder: builder,
768 device_interfaces,
769 })
770 }
771}
772
773impl ConfigureChipsetDevice for ArcMutexChipsetServices<'_, '_> {
774 fn new_line(
775 &mut self,
776 id: chipset_device_resources::LineSetId,
777 name: &str,
778 vector: u32,
779 ) -> vmcore::line_interrupt::LineInterrupt {
780 self.new_line(id, name, vector)
781 }
782
783 fn add_line_target(
784 &mut self,
785 id: chipset_device_resources::LineSetId,
786 source_range: std::ops::RangeInclusive<u32>,
787 target_start: u32,
788 ) {
789 self.add_line_target(id, source_range, target_start)
790 }
791
792 fn omit_saved_state(&mut self) {
793 self.omit_saved_state();
794 }
795}
796
797mod weak_mutex_pci {
798 use crate::chipset::PciConflict;
799 use crate::chipset::PciConflictReason;
800 use crate::chipset::PcieConflict;
801 use crate::chipset::PcieConflictReason;
802 use crate::chipset::backing::arc_mutex::pci::RegisterWeakMutexPci;
803 use crate::chipset::backing::arc_mutex::pci::RegisterWeakMutexPcie;
804 use chipset_device::ChipsetDevice;
805 use chipset_device::io::IoResult;
806 use closeable_mutex::CloseableMutex;
807 use pci_bus::GenericPciBusDevice;
808 use std::sync::Arc;
809 use std::sync::Weak;
810
811 pub struct WeakMutexPciDeviceWrapper(Weak<CloseableMutex<dyn ChipsetDevice>>);
814
815 impl GenericPciBusDevice for WeakMutexPciDeviceWrapper {
816 fn pci_cfg_read(&mut self, offset: u16, value: &mut u32) -> Option<IoResult> {
817 Some(
818 self.0
819 .upgrade()?
820 .lock()
821 .supports_pci()
822 .expect("builder code ensures supports_pci.is_some()")
823 .pci_cfg_read(offset, value),
824 )
825 }
826
827 fn pci_cfg_write(&mut self, offset: u16, value: u32) -> Option<IoResult> {
828 Some(
829 self.0
830 .upgrade()?
831 .lock()
832 .supports_pci()
833 .expect("builder code ensures supports_pci.is_some()")
834 .pci_cfg_write(offset, value),
835 )
836 }
837
838 fn pci_cfg_read_forward(
839 &mut self,
840 bus: u8,
841 device_function: u8,
842 offset: u16,
843 value: &mut u32,
844 ) -> Option<IoResult> {
845 self.0
846 .upgrade()?
847 .lock()
848 .supports_pci()
849 .expect("builder code ensures supports_pci.is_some()")
850 .pci_cfg_read_forward(bus, device_function, offset, value)
851 }
852
853 fn pci_cfg_write_forward(
854 &mut self,
855 bus: u8,
856 device_function: u8,
857 offset: u16,
858 value: u32,
859 ) -> Option<IoResult> {
860 self.0
861 .upgrade()?
862 .lock()
863 .supports_pci()
864 .expect("builder code ensures supports_pci.is_some()")
865 .pci_cfg_write_forward(bus, device_function, offset, value)
866 }
867 }
868
869 impl RegisterWeakMutexPci for Arc<CloseableMutex<pci_bus::GenericPciBus>> {
871 fn add_pci_device(
872 &mut self,
873 bus: u8,
874 device: u8,
875 function: u8,
876 name: Arc<str>,
877 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
878 ) -> Result<(), PciConflict> {
879 self.lock()
880 .add_pci_device(
881 bus,
882 device,
883 function,
884 name.clone(),
885 WeakMutexPciDeviceWrapper(dev),
886 )
887 .map_err(|occ_err| PciConflict {
888 bdf: (bus, device, function),
889 reason: PciConflictReason::ExistingDev(occ_err.existing_device_name),
890 conflict_dev: name,
891 })
892 }
893 }
894
895 impl RegisterWeakMutexPci for Arc<CloseableMutex<chipset_legacy::piix4_pci_bus::Piix4PciBus>> {
897 fn add_pci_device(
898 &mut self,
899 bus: u8,
900 device: u8,
901 function: u8,
902 name: Arc<str>,
903 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
904 ) -> Result<(), PciConflict> {
905 self.lock()
906 .as_pci_bus()
907 .add_pci_device(
908 bus,
909 device,
910 function,
911 name.clone(),
912 WeakMutexPciDeviceWrapper(dev),
913 )
914 .map_err(|occ_err| PciConflict {
915 bdf: (bus, device, function),
916 reason: PciConflictReason::ExistingDev(occ_err.existing_device_name),
917 conflict_dev: name,
918 })
919 }
920 }
921
922 impl RegisterWeakMutexPcie for Arc<CloseableMutex<pcie::root::GenericPcieRootComplex>> {
924 fn add_pcie_device(
925 &mut self,
926 port: u8,
927 name: Arc<str>,
928 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
929 ) -> Result<(), PcieConflict> {
930 self.lock()
931 .add_pcie_device(port, name.clone(), Box::new(WeakMutexPciDeviceWrapper(dev)))
932 .map_err(|existing_dev_name| PcieConflict {
933 reason: PcieConflictReason::ExistingDev(existing_dev_name),
934 conflict_dev: name,
935 })
936 }
937
938 fn downstream_ports(&self) -> Vec<(u8, Arc<str>)> {
939 self.lock().downstream_ports()
940 }
941 }
942
943 impl RegisterWeakMutexPcie for Arc<CloseableMutex<pcie::switch::GenericPcieSwitch>> {
945 fn add_pcie_device(
946 &mut self,
947 port: u8,
948 name: Arc<str>,
949 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
950 ) -> Result<(), PcieConflict> {
951 self.lock()
952 .add_pcie_device(port, &name, Box::new(WeakMutexPciDeviceWrapper(dev)))
953 .map_err(|err| PcieConflict {
954 reason: PcieConflictReason::ExistingDev(err.to_string().into()),
955 conflict_dev: name,
956 })
957 }
958
959 fn downstream_ports(&self) -> Vec<(u8, Arc<str>)> {
960 self.lock().downstream_ports()
961 }
962 }
963}
964
965pub struct ArcMutexIsaDmaChannel {
966 channel_num: u8,
967 dma: Arc<CloseableMutex<dma::DmaController>>,
968}
969
970impl ArcMutexIsaDmaChannel {
971 #[allow(dead_code)] pub fn new(dma: Arc<CloseableMutex<dma::DmaController>>, channel_num: u8) -> Self {
973 Self { dma, channel_num }
974 }
975}
976
977impl vmcore::isa_dma_channel::IsaDmaChannel for ArcMutexIsaDmaChannel {
978 fn check_transfer_size(&mut self) -> u16 {
979 self.dma.lock().check_transfer_size(self.channel_num.into())
980 }
981
982 fn request(
983 &mut self,
984 direction: vmcore::isa_dma_channel::IsaDmaDirection,
985 ) -> Option<vmcore::isa_dma_channel::IsaDmaBuffer> {
986 self.dma.lock().request(self.channel_num.into(), direction)
987 }
988
989 fn complete(&mut self) {
990 self.dma.lock().complete(self.channel_num.into())
991 }
992}
993
994pub mod options {
996 use super::*;
997 use state_unit::UnitHandle;
998 use vmcore::vmtime::VmTimeSource;
999
1000 #[expect(missing_docs)] pub struct BaseChipsetFoundation<'a> {
1003 pub is_restoring: bool,
1004 pub untrusted_dma_memory: GuestMemory,
1016 pub trusted_vtl0_dma_memory: GuestMemory,
1028 pub power_event_handler: Arc<dyn crate::PowerEventHandler>,
1029 pub debug_event_handler: Arc<dyn crate::DebugEventHandler>,
1030 pub vmtime: &'a VmTimeSource,
1031 pub vmtime_unit: &'a UnitHandle,
1032 pub doorbell_registration: Option<Arc<dyn DoorbellRegistration>>,
1033 }
1034
1035 macro_rules! base_chipset_devices_and_manifest {
1036 (
1037 impls {
1041 $(#[$m:meta])*
1042 pub struct $base_chipset_devices:ident {
1043 ...
1044 }
1045
1046 $(#[$m2:meta])*
1047 pub struct $base_chipset_manifest:ident {
1048 ...
1049 }
1050 }
1051
1052 devices {
1053 $($name:ident: $ty:ty,)*
1054 }
1055 ) => {paste::paste!{
1056 $(#[$m])*
1057 pub struct $base_chipset_devices {
1058 $(pub [<deps_ $name>]: Option<$ty>,)*
1059 }
1060
1061 $(#[$m2])*
1062 pub struct $base_chipset_manifest {
1063 $(pub [<with_ $name>]: bool,)*
1064 }
1065
1066 impl $base_chipset_manifest {
1067 pub const fn empty() -> Self {
1070 Self {
1071 $([<with_ $name>]: false,)*
1072 }
1073 }
1074 }
1075
1076 impl $base_chipset_devices {
1077 pub fn empty() -> Self {
1080 Self {
1081 $([<deps_ $name>]: None,)*
1082 }
1083 }
1084
1085 pub fn to_manifest(&self) -> $base_chipset_manifest {
1087 let Self {
1088 $([<deps_ $name>],)*
1089 } = self;
1090
1091 $base_chipset_manifest {
1092 $([<with_ $name>]: [<deps_ $name>].is_some(),)*
1093 }
1094 }
1095 }
1096 }};
1097 }
1098
1099 base_chipset_devices_and_manifest! {
1100 impls {
1101 #[expect(missing_docs)] pub struct BaseChipsetDevices {
1104 ...
1108 }
1109
1110 #[expect(missing_docs)] #[derive(Debug, Clone, MeshPayload, PartialEq, Eq)]
1113 pub struct BaseChipsetManifest {
1114 ...
1118 }
1119 }
1120
1121 devices {
1122 generic_cmos_rtc: dev::GenericCmosRtcDeps,
1123 generic_ioapic: dev::GenericIoApicDeps,
1124 generic_isa_dma: dev::GenericIsaDmaDeps,
1125 generic_isa_floppy: dev::GenericIsaFloppyDeps,
1126 generic_pci_bus: dev::GenericPciBusDeps,
1127 generic_pic: dev::GenericPicDeps,
1128 generic_pit: dev::GenericPitDeps,
1129 generic_psp: dev::GenericPspDeps,
1130
1131 hyperv_firmware_pcat: dev::HyperVFirmwarePcat,
1132 hyperv_firmware_uefi: dev::HyperVFirmwareUefi,
1133 hyperv_framebuffer: dev::HyperVFramebufferDeps,
1134 hyperv_guest_watchdog: dev::HyperVGuestWatchdogDeps,
1135 hyperv_ide: dev::HyperVIdeDeps,
1136 hyperv_power_management: dev::HyperVPowerManagementDeps,
1137 hyperv_vga: dev::HyperVVgaDeps,
1138
1139 i440bx_host_pci_bridge: dev::I440BxHostPciBridgeDeps,
1140
1141 piix4_cmos_rtc: dev::Piix4CmosRtcDeps,
1142 piix4_pci_bus: dev::Piix4PciBusDeps,
1143 piix4_pci_isa_bridge: dev::Piix4PciIsaBridgeDeps,
1144 piix4_pci_usb_uhci_stub: dev::Piix4PciUsbUhciStubDeps,
1145 piix4_power_management: dev::Piix4PowerManagementDeps,
1146
1147 underhill_vga_proxy: dev::UnderhillVgaProxyDeps,
1148
1149 winbond_super_io_and_floppy_stub: dev::WinbondSuperIoAndFloppyStubDeps,
1150 winbond_super_io_and_floppy_full: dev::WinbondSuperIoAndFloppyFullDeps,
1151 }
1152 }
1153
1154 pub mod dev {
1156 use super::*;
1157 use crate::BusIdPci;
1158 use chipset_resources::battery::HostBatteryUpdate;
1159 use local_clock::InspectableLocalClock;
1160
1161 macro_rules! feature_gated {
1162 (
1163 feature = $feat:literal;
1164
1165 $(#[$m:meta])*
1166 pub struct $root_deps:ident $($rest:tt)*
1167 ) => {
1168 #[cfg(not(feature = $feat))]
1169 #[doc(hidden)]
1170 pub type $root_deps = ();
1171
1172 #[cfg(feature = $feat)]
1173 $(#[$m])*
1174 pub struct $root_deps $($rest)*
1175 };
1176 }
1177
1178 pub struct Piix4PciIsaBridgeDeps {
1180 pub attached_to: BusIdPci,
1182 }
1183
1184 pub struct HyperVIdeDeps {
1189 pub attached_to: BusIdPci,
1191 pub primary_channel_drives: [Option<ide::DriveMedia>; 2],
1193 pub secondary_channel_drives: [Option<ide::DriveMedia>; 2],
1195 }
1196
1197 pub struct Piix4PciUsbUhciStubDeps {
1202 pub attached_to: BusIdPci,
1204 }
1205
1206 pub struct Piix4PowerManagementDeps {
1208 pub attached_to: BusIdPci,
1210 pub pm_timer_assist: Option<Box<dyn pm::PmTimerAssist>>,
1212 }
1213
1214 pub struct GenericIsaDmaDeps;
1216
1217 pub struct HyperVPowerManagementDeps {
1219 pub acpi_irq: u32,
1221 pub pio_base: u16,
1223 pub pm_timer_assist: Option<Box<dyn pm::PmTimerAssist>>,
1225 }
1226
1227 pub struct GenericPspDeps;
1229
1230 feature_gated! {
1231 feature = "dev_generic_isa_floppy";
1232
1233 pub struct GenericIsaFloppyDeps {
1235 pub irq: u32,
1237 pub dma_channel: u8,
1239 pub pio_base: u16,
1241 pub drives: floppy::DriveRibbon,
1243 }
1244 }
1245
1246 feature_gated! {
1247 feature = "dev_winbond_super_io_and_floppy_stub";
1248
1249 pub struct WinbondSuperIoAndFloppyStubDeps;
1260 }
1261
1262 feature_gated! {
1263 feature = "dev_winbond_super_io_and_floppy_full";
1264
1265 pub struct WinbondSuperIoAndFloppyFullDeps {
1271 pub primary_disk_drive: floppy::DriveRibbon,
1273 pub secondary_disk_drive: floppy::DriveRibbon,
1275 }
1276 }
1277
1278 pub struct GenericPciBusDeps {
1280 pub bus_id: BusIdPci,
1282 pub pio_addr: u16,
1284 pub pio_data: u16,
1286 }
1287
1288 pub struct Piix4PciBusDeps {
1290 pub bus_id: BusIdPci,
1292 }
1293
1294 pub struct I440BxHostPciBridgeDeps {
1296 pub attached_to: BusIdPci,
1298 pub adjust_gpa_range: Box<dyn chipset_legacy::i440bx_host_pci_bridge::AdjustGpaRange>,
1300 }
1301
1302 pub struct GenericPitDeps;
1304
1305 feature_gated! {
1306 feature = "dev_hyperv_vga";
1307
1308 pub struct HyperVVgaDeps {
1310 pub attached_to: BusIdPci,
1312 pub rom: Option<Box<dyn guestmem::MapRom>>,
1315 }
1316 }
1317
1318 pub struct GenericPicDeps {}
1320
1321 pub struct GenericIoApicDeps {
1323 pub num_entries: u8,
1325 pub routing: Box<dyn ioapic::IoApicRouting>,
1327 }
1328
1329 pub struct GenericCmosRtcDeps {
1331 pub irq: u32,
1333 pub time_source: Box<dyn InspectableLocalClock>,
1335 pub century_reg_idx: u8,
1337 pub initial_cmos: Option<[u8; 256]>,
1339 }
1340
1341 pub struct Piix4CmosRtcDeps {
1343 pub time_source: Box<dyn InspectableLocalClock>,
1345 pub initial_cmos: Option<[u8; 256]>,
1347 pub enlightened_interrupts: bool,
1350 }
1351
1352 pub struct HyperVBatteryDeps {
1354 pub base_addr: u64,
1356 pub use_gpe0: bool,
1358 pub line_interrupt_no: u32,
1360 pub battery_status_recv: mesh::Receiver<HostBatteryUpdate>,
1362 }
1363
1364 pub struct HyperVGuestWatchdogDeps {
1366 pub port_base: u16,
1368 pub watchdog_platform: Box<dyn watchdog_core::platform::WatchdogPlatform>,
1371 }
1372
1373 pub struct HyperVFirmwarePcat {
1375 pub config: firmware_pcat::config::PcatBiosConfig,
1378 pub logger: Box<dyn firmware_pcat::PcatLogger>,
1380 pub generation_id_recv: mesh::Receiver<[u8; 16]>,
1382 pub rom: Option<Box<dyn guestmem::MapRom>>,
1385 pub replay_mtrrs: Box<dyn Send + FnMut()>,
1388 }
1389
1390 pub struct HyperVFirmwareUefi {
1392 pub config: firmware_uefi::UefiConfig,
1395 pub logger: Box<dyn firmware_uefi::platform::logger::UefiLogger>,
1397 pub nvram_storage: Box<dyn uefi_nvram_storage::VmmNvramStorage>,
1399 pub generation_id_recv: mesh::Receiver<[u8; 16]>,
1401 pub watchdog_platform: Box<dyn watchdog_core::platform::WatchdogPlatform>,
1404 pub watchdog_recv: mesh::Receiver<()>,
1406 pub vsm_config: Option<Box<dyn firmware_uefi::platform::nvram::VsmConfig>>,
1409 pub time_source: Box<dyn InspectableLocalClock>,
1411 }
1412
1413 #[expect(missing_docs)] pub struct HyperVFramebufferDeps {
1419 pub fb_mapper: Box<dyn guestmem::MemoryMapper>,
1420 pub fb: Framebuffer,
1421 pub vtl2_framebuffer_gpa_base: Option<u64>,
1422 }
1423
1424 feature_gated! {
1425 feature = "dev_underhill_vga_proxy";
1426
1427 pub struct UnderhillVgaProxyDeps {
1429 pub attached_to: BusIdPci,
1431 pub pci_cfg_proxy: Arc<dyn vga_proxy::ProxyVgaPciCfgAccess>,
1433 pub register_host_io_fastpath: Box<dyn vga_proxy::RegisterHostIoPortFastPath>,
1435 }
1436 }
1437 }
1438}