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::backing::arc_mutex::pci::RegisterWeakMutexPci;
801 use chipset_device::ChipsetDevice;
802 use chipset_device::io::IoResult;
803 use closeable_mutex::CloseableMutex;
804 use pci_bus::GenericPciBusDevice;
805 use std::sync::Arc;
806 use std::sync::Weak;
807
808 pub struct WeakMutexPciDeviceWrapper(Weak<CloseableMutex<dyn ChipsetDevice>>);
811
812 impl GenericPciBusDevice for WeakMutexPciDeviceWrapper {
813 fn pci_cfg_read(&mut self, offset: u16, value: &mut u32) -> Option<IoResult> {
814 Some(
815 self.0
816 .upgrade()?
817 .lock()
818 .supports_pci()
819 .expect("builder code ensures supports_pci.is_some()")
820 .pci_cfg_read(offset, value),
821 )
822 }
823
824 fn pci_cfg_write(&mut self, offset: u16, value: u32) -> Option<IoResult> {
825 Some(
826 self.0
827 .upgrade()?
828 .lock()
829 .supports_pci()
830 .expect("builder code ensures supports_pci.is_some()")
831 .pci_cfg_write(offset, value),
832 )
833 }
834 }
835
836 impl RegisterWeakMutexPci for Arc<CloseableMutex<pci_bus::GenericPciBus>> {
838 fn add_pci_device(
839 &mut self,
840 bus: u8,
841 device: u8,
842 function: u8,
843 name: Arc<str>,
844 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
845 ) -> Result<(), PciConflict> {
846 self.lock()
847 .add_pci_device(
848 bus,
849 device,
850 function,
851 name.clone(),
852 WeakMutexPciDeviceWrapper(dev),
853 )
854 .map_err(|(_, existing_dev)| PciConflict {
855 bdf: (bus, device, function),
856 reason: PciConflictReason::ExistingDev(existing_dev),
857 conflict_dev: name,
858 })
859 }
860 }
861
862 impl RegisterWeakMutexPci for Arc<CloseableMutex<chipset_legacy::piix4_pci_bus::Piix4PciBus>> {
864 fn add_pci_device(
865 &mut self,
866 bus: u8,
867 device: u8,
868 function: u8,
869 name: Arc<str>,
870 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
871 ) -> Result<(), PciConflict> {
872 self.lock()
873 .as_pci_bus()
874 .add_pci_device(
875 bus,
876 device,
877 function,
878 name.clone(),
879 WeakMutexPciDeviceWrapper(dev),
880 )
881 .map_err(|(_, existing_dev)| PciConflict {
882 bdf: (bus, device, function),
883 reason: PciConflictReason::ExistingDev(existing_dev),
884 conflict_dev: name,
885 })
886 }
887 }
888}
889
890pub struct ArcMutexIsaDmaChannel {
891 channel_num: u8,
892 dma: Arc<CloseableMutex<dma::DmaController>>,
893}
894
895impl ArcMutexIsaDmaChannel {
896 #[allow(dead_code)] pub fn new(dma: Arc<CloseableMutex<dma::DmaController>>, channel_num: u8) -> Self {
898 Self { dma, channel_num }
899 }
900}
901
902impl vmcore::isa_dma_channel::IsaDmaChannel for ArcMutexIsaDmaChannel {
903 fn check_transfer_size(&mut self) -> u16 {
904 self.dma.lock().check_transfer_size(self.channel_num.into())
905 }
906
907 fn request(
908 &mut self,
909 direction: vmcore::isa_dma_channel::IsaDmaDirection,
910 ) -> Option<vmcore::isa_dma_channel::IsaDmaBuffer> {
911 self.dma.lock().request(self.channel_num.into(), direction)
912 }
913
914 fn complete(&mut self) {
915 self.dma.lock().complete(self.channel_num.into())
916 }
917}
918
919pub mod options {
921 use super::*;
922 use state_unit::UnitHandle;
923 use vmcore::vmtime::VmTimeSource;
924
925 #[expect(missing_docs)] pub struct BaseChipsetFoundation<'a> {
928 pub is_restoring: bool,
929 pub untrusted_dma_memory: GuestMemory,
941 pub trusted_vtl0_dma_memory: GuestMemory,
953 pub power_event_handler: Arc<dyn crate::PowerEventHandler>,
954 pub debug_event_handler: Arc<dyn crate::DebugEventHandler>,
955 pub vmtime: &'a VmTimeSource,
956 pub vmtime_unit: &'a UnitHandle,
957 pub doorbell_registration: Option<Arc<dyn DoorbellRegistration>>,
958 }
959
960 macro_rules! base_chipset_devices_and_manifest {
961 (
962 impls {
966 $(#[$m:meta])*
967 pub struct $base_chipset_devices:ident {
968 ...
969 }
970
971 $(#[$m2:meta])*
972 pub struct $base_chipset_manifest:ident {
973 ...
974 }
975 }
976
977 devices {
978 $($name:ident: $ty:ty,)*
979 }
980 ) => {paste::paste!{
981 $(#[$m])*
982 pub struct $base_chipset_devices {
983 $(pub [<deps_ $name>]: Option<$ty>,)*
984 }
985
986 $(#[$m2])*
987 pub struct $base_chipset_manifest {
988 $(pub [<with_ $name>]: bool,)*
989 }
990
991 impl $base_chipset_manifest {
992 pub const fn empty() -> Self {
995 Self {
996 $([<with_ $name>]: false,)*
997 }
998 }
999 }
1000
1001 impl $base_chipset_devices {
1002 pub fn empty() -> Self {
1005 Self {
1006 $([<deps_ $name>]: None,)*
1007 }
1008 }
1009
1010 pub fn to_manifest(&self) -> $base_chipset_manifest {
1012 let Self {
1013 $([<deps_ $name>],)*
1014 } = self;
1015
1016 $base_chipset_manifest {
1017 $([<with_ $name>]: [<deps_ $name>].is_some(),)*
1018 }
1019 }
1020 }
1021 }};
1022 }
1023
1024 base_chipset_devices_and_manifest! {
1025 impls {
1026 #[expect(missing_docs)] pub struct BaseChipsetDevices {
1029 ...
1033 }
1034
1035 #[expect(missing_docs)] #[derive(Debug, Clone, MeshPayload, PartialEq, Eq)]
1038 pub struct BaseChipsetManifest {
1039 ...
1043 }
1044 }
1045
1046 devices {
1047 generic_cmos_rtc: dev::GenericCmosRtcDeps,
1048 generic_ioapic: dev::GenericIoApicDeps,
1049 generic_isa_dma: dev::GenericIsaDmaDeps,
1050 generic_isa_floppy: dev::GenericIsaFloppyDeps,
1051 generic_pci_bus: dev::GenericPciBusDeps,
1052 generic_pic: dev::GenericPicDeps,
1053 generic_pit: dev::GenericPitDeps,
1054 generic_psp: dev::GenericPspDeps,
1055
1056 hyperv_firmware_pcat: dev::HyperVFirmwarePcat,
1057 hyperv_firmware_uefi: dev::HyperVFirmwareUefi,
1058 hyperv_framebuffer: dev::HyperVFramebufferDeps,
1059 hyperv_guest_watchdog: dev::HyperVGuestWatchdogDeps,
1060 hyperv_ide: dev::HyperVIdeDeps,
1061 hyperv_power_management: dev::HyperVPowerManagementDeps,
1062 hyperv_vga: dev::HyperVVgaDeps,
1063
1064 i440bx_host_pci_bridge: dev::I440BxHostPciBridgeDeps,
1065
1066 piix4_cmos_rtc: dev::Piix4CmosRtcDeps,
1067 piix4_pci_bus: dev::Piix4PciBusDeps,
1068 piix4_pci_isa_bridge: dev::Piix4PciIsaBridgeDeps,
1069 piix4_pci_usb_uhci_stub: dev::Piix4PciUsbUhciStubDeps,
1070 piix4_power_management: dev::Piix4PowerManagementDeps,
1071
1072 underhill_vga_proxy: dev::UnderhillVgaProxyDeps,
1073
1074 winbond_super_io_and_floppy_stub: dev::WinbondSuperIoAndFloppyStubDeps,
1075 winbond_super_io_and_floppy_full: dev::WinbondSuperIoAndFloppyFullDeps,
1076 }
1077 }
1078
1079 pub mod dev {
1081 use super::*;
1082 use crate::BusIdPci;
1083 use chipset_resources::battery::HostBatteryUpdate;
1084 use local_clock::InspectableLocalClock;
1085
1086 macro_rules! feature_gated {
1087 (
1088 feature = $feat:literal;
1089
1090 $(#[$m:meta])*
1091 pub struct $root_deps:ident $($rest:tt)*
1092 ) => {
1093 #[cfg(not(feature = $feat))]
1094 #[doc(hidden)]
1095 pub type $root_deps = ();
1096
1097 #[cfg(feature = $feat)]
1098 $(#[$m])*
1099 pub struct $root_deps $($rest)*
1100 };
1101 }
1102
1103 pub struct Piix4PciIsaBridgeDeps {
1105 pub attached_to: BusIdPci,
1107 }
1108
1109 pub struct HyperVIdeDeps {
1114 pub attached_to: BusIdPci,
1116 pub primary_channel_drives: [Option<ide::DriveMedia>; 2],
1118 pub secondary_channel_drives: [Option<ide::DriveMedia>; 2],
1120 }
1121
1122 pub struct Piix4PciUsbUhciStubDeps {
1127 pub attached_to: BusIdPci,
1129 }
1130
1131 pub struct Piix4PowerManagementDeps {
1133 pub attached_to: BusIdPci,
1135 pub pm_timer_assist: Option<Box<dyn pm::PmTimerAssist>>,
1137 }
1138
1139 pub struct GenericIsaDmaDeps;
1141
1142 pub struct HyperVPowerManagementDeps {
1144 pub acpi_irq: u32,
1146 pub pio_base: u16,
1148 pub pm_timer_assist: Option<Box<dyn pm::PmTimerAssist>>,
1150 }
1151
1152 pub struct GenericPspDeps;
1154
1155 feature_gated! {
1156 feature = "dev_generic_isa_floppy";
1157
1158 pub struct GenericIsaFloppyDeps {
1160 pub irq: u32,
1162 pub dma_channel: u8,
1164 pub pio_base: u16,
1166 pub drives: floppy::DriveRibbon,
1168 }
1169 }
1170
1171 feature_gated! {
1172 feature = "dev_winbond_super_io_and_floppy_stub";
1173
1174 pub struct WinbondSuperIoAndFloppyStubDeps;
1185 }
1186
1187 feature_gated! {
1188 feature = "dev_winbond_super_io_and_floppy_full";
1189
1190 pub struct WinbondSuperIoAndFloppyFullDeps {
1196 pub primary_disk_drive: floppy::DriveRibbon,
1198 pub secondary_disk_drive: floppy::DriveRibbon,
1200 }
1201 }
1202
1203 pub struct GenericPciBusDeps {
1205 pub bus_id: BusIdPci,
1207 pub pio_addr: u16,
1209 pub pio_data: u16,
1211 }
1212
1213 pub struct Piix4PciBusDeps {
1215 pub bus_id: BusIdPci,
1217 }
1218
1219 pub struct I440BxHostPciBridgeDeps {
1221 pub attached_to: BusIdPci,
1223 pub adjust_gpa_range: Box<dyn chipset_legacy::i440bx_host_pci_bridge::AdjustGpaRange>,
1225 }
1226
1227 pub struct GenericPitDeps;
1229
1230 feature_gated! {
1231 feature = "dev_hyperv_vga";
1232
1233 pub struct HyperVVgaDeps {
1235 pub attached_to: BusIdPci,
1237 pub rom: Option<Box<dyn guestmem::MapRom>>,
1240 }
1241 }
1242
1243 pub struct GenericPicDeps {}
1245
1246 pub struct GenericIoApicDeps {
1248 pub num_entries: u8,
1250 pub routing: Box<dyn ioapic::IoApicRouting>,
1252 }
1253
1254 pub struct GenericCmosRtcDeps {
1256 pub irq: u32,
1258 pub time_source: Box<dyn InspectableLocalClock>,
1260 pub century_reg_idx: u8,
1262 pub initial_cmos: Option<[u8; 256]>,
1264 }
1265
1266 pub struct Piix4CmosRtcDeps {
1268 pub time_source: Box<dyn InspectableLocalClock>,
1270 pub initial_cmos: Option<[u8; 256]>,
1272 pub enlightened_interrupts: bool,
1275 }
1276
1277 pub struct HyperVBatteryDeps {
1279 pub base_addr: u64,
1281 pub use_gpe0: bool,
1283 pub line_interrupt_no: u32,
1285 pub battery_status_recv: mesh::Receiver<HostBatteryUpdate>,
1287 }
1288
1289 pub struct HyperVGuestWatchdogDeps {
1291 pub port_base: u16,
1293 pub watchdog_platform: Box<dyn watchdog_core::platform::WatchdogPlatform>,
1296 }
1297
1298 pub struct HyperVFirmwarePcat {
1300 pub config: firmware_pcat::config::PcatBiosConfig,
1303 pub logger: Box<dyn firmware_pcat::PcatLogger>,
1305 pub generation_id_recv: mesh::Receiver<[u8; 16]>,
1307 pub rom: Option<Box<dyn guestmem::MapRom>>,
1310 pub replay_mtrrs: Box<dyn Send + FnMut()>,
1313 }
1314
1315 pub struct HyperVFirmwareUefi {
1317 pub config: firmware_uefi::UefiConfig,
1320 pub logger: Box<dyn firmware_uefi::platform::logger::UefiLogger>,
1322 pub nvram_storage: Box<dyn uefi_nvram_storage::VmmNvramStorage>,
1324 pub generation_id_recv: mesh::Receiver<[u8; 16]>,
1326 pub watchdog_platform: Box<dyn watchdog_core::platform::WatchdogPlatform>,
1329 pub watchdog_recv: mesh::Receiver<()>,
1331 pub vsm_config: Option<Box<dyn firmware_uefi::platform::nvram::VsmConfig>>,
1334 pub time_source: Box<dyn InspectableLocalClock>,
1336 }
1337
1338 #[expect(missing_docs)] pub struct HyperVFramebufferDeps {
1344 pub fb_mapper: Box<dyn guestmem::MemoryMapper>,
1345 pub fb: Framebuffer,
1346 pub vtl2_framebuffer_gpa_base: Option<u64>,
1347 }
1348
1349 feature_gated! {
1350 feature = "dev_underhill_vga_proxy";
1351
1352 pub struct UnderhillVgaProxyDeps {
1354 pub attached_to: BusIdPci,
1356 pub pci_cfg_proxy: Arc<dyn vga_proxy::ProxyVgaPciCfgAccess>,
1358 pub register_host_io_fastpath: Box<dyn vga_proxy::RegisterHostIoPortFastPath>,
1360 }
1361 }
1362 }
1363}