1use crate::ChipsetDeviceHandle;
8use crate::LegacyPciChipsetDeviceHandle;
9use crate::PowerEvent;
10use crate::chipset::ChipsetBuilder;
11use crate::chipset::backing::arc_mutex::device::AddDeviceError;
12use crate::chipset::backing::arc_mutex::services::ArcMutexChipsetServices;
13use chipset::*;
14use chipset_device::ChipsetDevice;
15#[cfg(any(
16 feature = "dev_generic_isa_floppy",
17 feature = "dev_winbond_super_io_and_floppy_full",
18 feature = "dev_winbond_super_io_and_floppy_stub"
19))]
20use chipset_device::isa_dma::IsaDmaController;
21use chipset_device_resources::ConfigureChipsetDevice;
22use chipset_device_resources::GPE0_LINE_SET;
23use chipset_device_resources::IRQ_LINE_SET;
24use chipset_device_resources::ResolveChipsetDeviceHandleParams;
25use closeable_mutex::CloseableMutex;
26use framebuffer::Framebuffer;
27use framebuffer::FramebufferDevice;
28use framebuffer::FramebufferLocalControl;
29use guestmem::DoorbellRegistration;
30use guestmem::GuestMemory;
31use mesh::MeshPayload;
32use state_unit::StateUnits;
33use std::fmt::Debug;
34use std::sync::Arc;
35use thiserror::Error;
36use vm_resource::Resource;
37use vm_resource::ResourceResolver;
38use vm_resource::kind::IsaDmaControllerHandleKind;
39use vmcore::vm_task::VmTaskDriverSource;
40
41#[expect(missing_docs)] #[derive(Error, Debug)]
44pub enum BaseChipsetBuilderError {
45 #[error(transparent)]
48 AddDevice(#[from] AddDeviceError),
49 #[error("no valid interrupt controller")]
50 MissingInterruptController,
51 #[error("attempted to add feature-gated device (requires {0})")]
52 FeatureGatedDevice(&'static str),
53 #[error("no valid ISA DMA controller for floppy")]
54 NoDmaForFloppy,
55 #[error("failed to resolve ISA DMA controller")]
56 ResolveIsaDma(#[source] vm_resource::ResolveError),
57 #[error("failed to resolve resource")]
58 ResolveResource(#[source] vm_resource::ResolveError),
59}
60
61#[expect(missing_docs)] pub struct BaseChipsetDeviceInterfaces {
68 pub framebuffer_local_control: Option<FramebufferLocalControl>,
69}
70
71pub struct BaseChipsetBuilderOutput<'a> {
73 pub chipset_builder: ChipsetBuilder<'a>,
75 pub device_interfaces: BaseChipsetDeviceInterfaces,
78}
79
80pub struct BaseChipsetBuilder<'a> {
86 foundation: options::BaseChipsetFoundation<'a>,
87 devices: options::BaseChipsetDevices,
88 device_handles: Vec<ChipsetDeviceHandle>,
89 pci_device_handles: Vec<LegacyPciChipsetDeviceHandle>,
90 isa_dma_handle: Option<Resource<IsaDmaControllerHandleKind>>,
91 expected_manifest: Option<options::BaseChipsetManifest>,
92 fallback_mmio_device: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
93 flags: BaseChipsetBuilderFlags,
94}
95
96struct BaseChipsetBuilderFlags {
97 trace_unknown_pio: bool,
98 trace_unknown_mmio: bool,
99}
100
101impl<'a> BaseChipsetBuilder<'a> {
102 pub fn new(
104 foundation: options::BaseChipsetFoundation<'a>,
105 devices: options::BaseChipsetDevices,
106 ) -> Self {
107 BaseChipsetBuilder {
108 foundation,
109 devices,
110 device_handles: Vec::new(),
111 pci_device_handles: Vec::new(),
112 isa_dma_handle: None,
113 expected_manifest: None,
114 fallback_mmio_device: None,
115 flags: BaseChipsetBuilderFlags {
116 trace_unknown_pio: false,
131 trace_unknown_mmio: true,
132 },
133 }
134 }
135
136 pub fn with_expected_manifest(
139 mut self,
140 expected_manifest: options::BaseChipsetManifest,
141 ) -> Self {
142 self.expected_manifest = Some(expected_manifest);
143 self
144 }
145
146 pub fn with_device_handles(mut self, mut device_handles: Vec<ChipsetDeviceHandle>) -> Self {
148 self.device_handles.append(&mut device_handles);
149 self
150 }
151
152 pub fn with_pci_device_handles(
154 mut self,
155 mut pci_device_handles: Vec<LegacyPciChipsetDeviceHandle>,
156 ) -> Self {
157 self.pci_device_handles.append(&mut pci_device_handles);
158 self
159 }
160
161 pub fn with_isa_dma_handle(
163 mut self,
164 handle: Option<Resource<IsaDmaControllerHandleKind>>,
165 ) -> Self {
166 self.isa_dma_handle = handle;
167 self
168 }
169
170 pub fn with_trace_unknown_pio(mut self, active: bool) -> Self {
174 self.flags.trace_unknown_pio = active;
175 self
176 }
177
178 pub fn with_trace_unknown_mmio(mut self, active: bool) -> Self {
182 self.flags.trace_unknown_mmio = active;
183 self
184 }
185
186 pub fn with_fallback_mmio_device(
189 mut self,
190 fallback_mmio_device: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
191 ) -> Self {
192 self.fallback_mmio_device = fallback_mmio_device;
193 self
194 }
195
196 pub async fn build(
201 self,
202 driver_source: &'a VmTaskDriverSource,
203 units: &'a StateUnits,
204 resolver: &ResourceResolver,
205 ) -> Result<BaseChipsetBuilderOutput<'a>, BaseChipsetBuilderError> {
206 let Self {
207 foundation,
208 devices,
209 device_handles,
210 pci_device_handles,
211 isa_dma_handle,
212 expected_manifest,
213 fallback_mmio_device,
214 flags,
215 } = self;
216
217 let manifest = devices.to_manifest();
218 if let Some(expected_manifest) = expected_manifest {
219 assert_eq!(expected_manifest, manifest, "manifests do not match");
220 }
221
222 let mut device_interfaces = BaseChipsetDeviceInterfaces {
223 framebuffer_local_control: None,
224 };
225
226 let builder = ChipsetBuilder::new(
227 driver_source,
228 units,
229 foundation.debug_event_handler.clone(),
230 foundation.vmtime,
231 foundation.vmtime_unit,
232 flags.trace_unknown_pio,
233 flags.trace_unknown_mmio,
234 fallback_mmio_device,
235 );
236
237 let options::BaseChipsetDevices {
239 deps_generic_cmos_rtc,
240 deps_generic_isa_floppy,
241 deps_generic_pci_bus,
242 deps_generic_psp: _, deps_hyperv_firmware_pcat,
244 deps_hyperv_framebuffer,
245 deps_hyperv_ide,
246 deps_hyperv_vga,
247 deps_piix4_cmos_rtc,
248 deps_piix4_pci_bus,
249 deps_underhill_vga_proxy,
250 deps_winbond_super_io_and_floppy_stub,
251 deps_winbond_super_io_and_floppy_full,
252 } = devices;
253
254 if let Some(options::dev::GenericPciBusDeps {
255 bus_id,
256 pio_addr,
257 pio_data,
258 }) = deps_generic_pci_bus
259 {
260 let pci = builder.arc_mutex_device("pci_bus").add(|services| {
261 pci_bus::GenericPciBus::new(&mut services.register_pio(), pio_addr, pio_data)
262 })?;
263
264 builder.register_weak_mutex_pci_bus(bus_id, Box::new(pci));
265 }
266
267 if let Some(options::dev::Piix4PciBusDeps { bus_id }) = deps_piix4_pci_bus {
268 let reset = {
270 let power = foundation.power_event_handler.clone();
271 Box::new(move || power.on_power_event(PowerEvent::Reset))
272 };
273
274 let pci = builder.arc_mutex_device("piix4-pci-bus").add(|services| {
275 chipset_legacy::piix4_pci_bus::Piix4PciBus::new(
276 &mut services.register_pio(),
277 reset.clone(),
278 )
279 })?;
280 builder.register_weak_mutex_pci_bus(bus_id, Box::new(pci));
281 }
282
283 let dma = if let Some(dma_handle) = isa_dma_handle {
284 let resolved = resolver
285 .resolve(dma_handle, ())
286 .await
287 .map_err(BaseChipsetBuilderError::ResolveIsaDma)?;
288 let dev = builder
289 .arc_mutex_device::<dma::DmaController>("dma")
290 .add(|_services| resolved.0)?;
291 Some(dev)
292 } else {
293 None
294 };
295
296 for device in device_handles {
297 let ChipsetDeviceHandle { name, resource } = device;
298 builder
299 .arc_mutex_device(name.as_ref())
300 .try_add_async(async |services| {
301 resolver
302 .resolve(
303 resource,
304 ResolveChipsetDeviceHandleParams {
305 device_name: name.as_ref(),
306 guest_memory: &foundation.untrusted_dma_memory,
307 encrypted_guest_memory: &foundation.trusted_vtl0_dma_memory,
308 vmtime: foundation.vmtime,
309 is_restoring: foundation.is_restoring,
310 task_driver_source: driver_source,
311 register_mmio: &mut services.register_mmio(),
312 register_pio: &mut services.register_pio(),
313 configure: services,
314 },
315 )
316 .await
317 .map(|device| device.0)
318 })
319 .await?;
320 }
321
322 let _ = dma;
323 #[cfg(feature = "dev_generic_isa_floppy")]
324 if let Some(options::dev::GenericIsaFloppyDeps {
325 irq,
326 dma_channel: dma_chan,
327 pio_base,
328 drives,
329 }) = deps_generic_isa_floppy
330 {
331 if let Some(dma) = &dma {
332 let dma_channel = ArcMutexIsaDmaChannel::new(dma.clone(), dma_chan);
333
334 builder.arc_mutex_device("floppy").try_add(|services| {
335 let interrupt = services.new_line(IRQ_LINE_SET, "interrupt", irq);
336 floppy::FloppyDiskController::new(
337 foundation.untrusted_dma_memory.clone(),
338 interrupt,
339 &mut services.register_pio(),
340 pio_base,
341 drives,
342 Box::new(dma_channel),
343 )
344 })?;
345 } else {
346 return Err(BaseChipsetBuilderError::NoDmaForFloppy);
347 }
348 }
349
350 #[cfg(feature = "dev_winbond_super_io_and_floppy_full")]
351 if let Some(options::dev::WinbondSuperIoAndFloppyFullDeps {
352 primary_disk_drive,
353 secondary_disk_drive,
354 }) = deps_winbond_super_io_and_floppy_full
355 {
356 if let Some(dma) = &dma {
357 let primary_dma = Box::new(ArcMutexIsaDmaChannel::new(dma.clone(), 2));
360 let secondary_dma = Box::new(vmcore::isa_dma_channel::FloatingDmaChannel);
361
362 builder.arc_mutex_device("floppy-sio").try_add(|services| {
363 let interrupt = services.new_line(IRQ_LINE_SET, "interrupt", 6);
364 chipset_legacy::winbond83977_sio::Winbond83977FloppySioDevice::<
365 floppy::FloppyDiskController,
366 >::new(
367 foundation.untrusted_dma_memory.clone(),
368 interrupt,
369 &mut services.register_pio(),
370 primary_disk_drive,
371 secondary_disk_drive,
372 primary_dma,
373 secondary_dma,
374 )
375 })?;
376 } else {
377 return Err(BaseChipsetBuilderError::NoDmaForFloppy);
378 }
379 }
380
381 #[cfg(feature = "dev_winbond_super_io_and_floppy_stub")]
382 if let Some(options::dev::WinbondSuperIoAndFloppyStubDeps) =
383 deps_winbond_super_io_and_floppy_stub
384 {
385 if let Some(dma) = &dma {
386 let primary_dma = Box::new(ArcMutexIsaDmaChannel::new(dma.clone(), 2));
389 let secondary_dma = Box::new(vmcore::isa_dma_channel::FloatingDmaChannel);
390
391 builder.arc_mutex_device("floppy-sio").try_add(|services| {
392 let interrupt = services.new_line(IRQ_LINE_SET, "interrupt", 6);
393 chipset_legacy::winbond83977_sio::Winbond83977FloppySioDevice::<
394 floppy_pcat_stub::StubFloppyDiskController,
395 >::new(
396 foundation.untrusted_dma_memory.clone(),
397 interrupt,
398 &mut services.register_pio(),
399 floppy::DriveRibbon::None,
400 floppy::DriveRibbon::None,
401 primary_dma,
402 secondary_dma,
403 )
404 })?;
405 } else {
406 return Err(BaseChipsetBuilderError::NoDmaForFloppy);
407 }
408 }
409
410 if let Some(options::dev::HyperVIdeDeps {
411 attached_to,
412 primary_channel_drives,
413 secondary_channel_drives,
414 }) = deps_hyperv_ide
415 {
416 builder
417 .arc_mutex_device("ide")
418 .on_pci_bus(attached_to)
419 .try_add(|services| {
420 let primary_channel_line_interrupt =
422 services.new_line(IRQ_LINE_SET, "ide1", 14);
423 let secondary_channel_line_interrupt =
424 services.new_line(IRQ_LINE_SET, "ide2", 15);
425 ide::IdeDevice::new(
426 foundation.untrusted_dma_memory.clone(),
427 &mut services.register_pio(),
428 primary_channel_drives,
429 secondary_channel_drives,
430 primary_channel_line_interrupt,
431 secondary_channel_line_interrupt,
432 )
433 })?;
434 }
435
436 if let Some(options::dev::GenericCmosRtcDeps {
437 irq,
438 time_source,
439 century_reg_idx,
440 initial_cmos,
441 }) = deps_generic_cmos_rtc
442 {
443 let resolved = resolver
444 .resolve(time_source, ())
445 .await
446 .map_err(BaseChipsetBuilderError::ResolveResource)?;
447 builder.arc_mutex_device("rtc").add(|services| {
448 cmos_rtc::Rtc::new(
449 resolved.0,
450 services.new_line(IRQ_LINE_SET, "interrupt", irq),
451 services.register_vmtime(),
452 century_reg_idx,
453 initial_cmos,
454 false,
455 )
456 })?;
457 }
458
459 if let Some(options::dev::Piix4CmosRtcDeps {
460 time_source,
461 initial_cmos,
462 enlightened_interrupts,
463 }) = deps_piix4_cmos_rtc
464 {
465 let resolved = resolver
466 .resolve(time_source, ())
467 .await
468 .map_err(BaseChipsetBuilderError::ResolveResource)?;
469 builder.arc_mutex_device("piix4-rtc").add(|services| {
470 let rtc_interrupt = services.new_line(IRQ_LINE_SET, "interrupt", 8);
472 chipset_legacy::piix4_cmos_rtc::Piix4CmosRtc::new(
473 resolved.0,
474 rtc_interrupt,
475 services.register_vmtime(),
476 initial_cmos,
477 enlightened_interrupts,
478 )
479 })?;
480 }
481
482 const GPE0_LINE_GENERATION_ID: u32 = 0;
485
486 if let Some(options::dev::HyperVFirmwarePcat {
487 config,
488 logger,
489 generation_id_recv,
490 rom,
491 replay_mtrrs,
492 }) = deps_hyperv_firmware_pcat
493 {
494 builder.arc_mutex_device("pcat").try_add(|services| {
495 let notify_interrupt =
496 services.new_line(GPE0_LINE_SET, "genid", GPE0_LINE_GENERATION_ID);
497 firmware_pcat::PcatBiosDevice::new(
498 firmware_pcat::PcatBiosRuntimeDeps {
499 gm: foundation.trusted_vtl0_dma_memory.clone(),
500 logger,
501 generation_id_deps: generation_id::GenerationIdRuntimeDeps {
502 generation_id_recv,
503 gm: foundation.trusted_vtl0_dma_memory.clone(),
504 notify_interrupt,
505 },
506 vmtime: services.register_vmtime(),
507 rom,
508 register_pio: &mut services.register_pio(),
509 replay_mtrrs,
510 },
511 config,
512 )
513 })?;
514 }
515
516 if let Some(options::dev::HyperVFramebufferDeps {
517 fb_mapper,
518 fb,
519 vtl2_framebuffer_gpa_base,
520 }) = deps_hyperv_framebuffer
521 {
522 let fb = FramebufferDevice::new(fb_mapper, fb, vtl2_framebuffer_gpa_base);
523 let control = fb.as_ref().ok().map(|fb| fb.control());
524 builder.arc_mutex_device("fb").try_add(|_| fb)?;
525 device_interfaces.framebuffer_local_control = Some(control.unwrap());
526 }
527
528 #[cfg(feature = "dev_hyperv_vga")]
529 if let Some(options::dev::HyperVVgaDeps { attached_to, rom }) = deps_hyperv_vga {
530 builder
531 .arc_mutex_device("vga")
532 .on_pci_bus(attached_to)
533 .try_add(|services| {
534 vga::VgaDevice::new(
535 &driver_source.simple(),
536 services.register_vmtime(),
537 device_interfaces.framebuffer_local_control.clone().unwrap(),
538 rom,
539 )
540 })?;
541 }
542
543 #[cfg(feature = "dev_underhill_vga_proxy")]
544 if let Some(options::dev::UnderhillVgaProxyDeps {
545 attached_to,
546 pci_cfg_proxy,
547 register_host_io_fastpath,
548 }) = deps_underhill_vga_proxy
549 {
550 builder
551 .arc_mutex_device("vga_proxy")
552 .on_pci_bus(attached_to)
553 .add(|_services| {
554 vga_proxy::VgaProxyDevice::new(pci_cfg_proxy, &*register_host_io_fastpath)
555 })?;
556 }
557
558 macro_rules! feature_gate_check {
559 ($feature:literal, $dep:ident) => {
560 #[cfg(not(feature = $feature))]
561 let None::<()> = $dep else {
562 return Err(BaseChipsetBuilderError::FeatureGatedDevice($feature));
563 };
564 };
565 }
566
567 feature_gate_check!("dev_hyperv_vga", deps_hyperv_vga);
568 feature_gate_check!("dev_underhill_vga_proxy", deps_underhill_vga_proxy);
569 feature_gate_check!("dev_generic_isa_floppy", deps_generic_isa_floppy);
570 feature_gate_check!(
571 "dev_winbond_super_io_and_floppy_full",
572 deps_winbond_super_io_and_floppy_full
573 );
574 feature_gate_check!(
575 "dev_winbond_super_io_and_floppy_stub",
576 deps_winbond_super_io_and_floppy_stub
577 );
578
579 for device in pci_device_handles {
580 let LegacyPciChipsetDeviceHandle {
581 name,
582 resource,
583 pci_bus_name,
584 bdf,
585 } = device;
586
587 let (bus, slot, function) = bdf;
588
589 builder
590 .arc_mutex_device(name.as_ref())
591 .on_pci_bus(crate::BusId::new(pci_bus_name.as_str()))
592 .with_pci_addr(bus, slot, function)
593 .try_add_async(async |services| {
594 resolver
595 .resolve(
596 resource,
597 ResolveChipsetDeviceHandleParams {
598 device_name: name.as_ref(),
599 guest_memory: &foundation.untrusted_dma_memory,
600 encrypted_guest_memory: &foundation.trusted_vtl0_dma_memory,
601 vmtime: foundation.vmtime,
602 is_restoring: foundation.is_restoring,
603 task_driver_source: driver_source,
604 register_mmio: &mut services.register_mmio(),
605 register_pio: &mut services.register_pio(),
606 configure: services,
607 },
608 )
609 .await
610 .map(|dev| dev.0)
611 })
612 .await?;
613 }
614
615 Ok(BaseChipsetBuilderOutput {
616 chipset_builder: builder,
617 device_interfaces,
618 })
619 }
620}
621
622impl ConfigureChipsetDevice for ArcMutexChipsetServices<'_, '_> {
623 fn new_line(
624 &mut self,
625 id: chipset_device_resources::LineSetId,
626 name: &str,
627 vector: u32,
628 ) -> vmcore::line_interrupt::LineInterrupt {
629 self.new_line(id, name, vector)
630 }
631
632 fn add_line_target(
633 &mut self,
634 id: chipset_device_resources::LineSetId,
635 source_range: std::ops::RangeInclusive<u32>,
636 target_start: u32,
637 ) {
638 self.add_line_target(id, source_range, target_start)
639 }
640
641 fn omit_saved_state(&mut self) {
642 self.omit_saved_state();
643 }
644}
645
646mod weak_mutex_pci {
647 use crate::chipset::PciConflict;
648 use crate::chipset::PciConflictReason;
649 use crate::chipset::PcieConflict;
650 use crate::chipset::PcieConflictReason;
651 use crate::chipset::backing::arc_mutex::pci::RegisterWeakMutexPci;
652 use crate::chipset::backing::arc_mutex::pci::RegisterWeakMutexPcie;
653 use chipset_device::ChipsetDevice;
654 use chipset_device::io::IoResult;
655 use chipset_device::pci::ByteEnabledDwordRead;
656 use chipset_device::pci::ByteEnabledDwordWrite;
657 use chipset_device::pci::PciConfigAccessType;
658 use chipset_device::pci::PciConfigAddress;
659 use closeable_mutex::CloseableMutex;
660 use pci_bus::GenericPciBusDevice;
661 use std::sync::Arc;
662 use std::sync::Weak;
663
664 pub struct WeakMutexPciDeviceWrapper(Weak<CloseableMutex<dyn ChipsetDevice>>);
667
668 impl GenericPciBusDevice for WeakMutexPciDeviceWrapper {
669 fn pci_cfg_read(
670 &mut self,
671 offset: u16,
672 value: ByteEnabledDwordRead<'_>,
673 ) -> Option<IoResult> {
674 Some(
675 self.0
676 .upgrade()?
677 .lock()
678 .supports_pci()
679 .expect("builder code ensures supports_pci.is_some()")
680 .pci_cfg_read(offset, value),
681 )
682 }
683
684 fn pci_cfg_write(&mut self, offset: u16, value: ByteEnabledDwordWrite) -> Option<IoResult> {
685 Some(
686 self.0
687 .upgrade()?
688 .lock()
689 .supports_pci()
690 .expect("builder code ensures supports_pci.is_some()")
691 .pci_cfg_write(offset, value),
692 )
693 }
694
695 fn pci_cfg_read_with_routing(
696 &mut self,
697 access_type: PciConfigAccessType,
698 address: PciConfigAddress,
699 value: ByteEnabledDwordRead<'_>,
700 ) -> Option<IoResult> {
701 Some(
702 self.0
703 .upgrade()?
704 .lock()
705 .supports_pci()
706 .expect("builder code ensures supports_pci.is_some()")
707 .pci_cfg_read_with_routing(access_type, address, value),
708 )
709 }
710
711 fn pci_cfg_write_with_routing(
712 &mut self,
713 access_type: PciConfigAccessType,
714 address: PciConfigAddress,
715 value: ByteEnabledDwordWrite,
716 ) -> Option<IoResult> {
717 Some(
718 self.0
719 .upgrade()?
720 .lock()
721 .supports_pci()
722 .expect("builder code ensures supports_pci.is_some()")
723 .pci_cfg_write_with_routing(access_type, address, value),
724 )
725 }
726 }
727
728 impl RegisterWeakMutexPci for Arc<CloseableMutex<pci_bus::GenericPciBus>> {
730 fn add_pci_device(
731 &mut self,
732 bus: u8,
733 device: u8,
734 function: u8,
735 name: Arc<str>,
736 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
737 ) -> Result<(), PciConflict> {
738 self.lock()
739 .add_pci_device(
740 bus,
741 device,
742 function,
743 name.clone(),
744 WeakMutexPciDeviceWrapper(dev),
745 )
746 .map_err(|occ_err| PciConflict {
747 bdf: (bus, device, function),
748 reason: PciConflictReason::ExistingDev(occ_err.existing_device_name),
749 conflict_dev: name,
750 })
751 }
752 }
753
754 impl RegisterWeakMutexPci for Arc<CloseableMutex<chipset_legacy::piix4_pci_bus::Piix4PciBus>> {
756 fn add_pci_device(
757 &mut self,
758 bus: u8,
759 device: u8,
760 function: u8,
761 name: Arc<str>,
762 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
763 ) -> Result<(), PciConflict> {
764 self.lock()
765 .as_pci_bus()
766 .add_pci_device(
767 bus,
768 device,
769 function,
770 name.clone(),
771 WeakMutexPciDeviceWrapper(dev),
772 )
773 .map_err(|occ_err| PciConflict {
774 bdf: (bus, device, function),
775 reason: PciConflictReason::ExistingDev(occ_err.existing_device_name),
776 conflict_dev: name,
777 })
778 }
779 }
780
781 impl RegisterWeakMutexPcie for Arc<CloseableMutex<pcie::root::GenericPcieRootComplex>> {
783 fn add_pcie_device(
784 &mut self,
785 port_devfn: u8,
786 name: Arc<str>,
787 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
788 ) -> Result<(), PcieConflict> {
789 self.lock()
790 .add_pcie_device(
791 port_devfn,
792 name.clone(),
793 Box::new(WeakMutexPciDeviceWrapper(dev)),
794 )
795 .map_err(|existing_dev_name| PcieConflict {
796 reason: PcieConflictReason::ExistingDev(existing_dev_name),
797 conflict_dev: name,
798 })
799 }
800
801 fn downstream_ports(&self) -> Vec<pcie::root::DownstreamPortInfo> {
802 self.lock().downstream_ports()
803 }
804
805 fn add_rciep(
806 &mut self,
807 devfn: u8,
808 name: Arc<str>,
809 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
810 ) -> Result<(), PcieConflict> {
811 self.lock()
812 .add_rciep(
813 devfn,
814 name.clone(),
815 Box::new(WeakMutexPciDeviceWrapper(dev)),
816 )
817 .map_err(|existing_dev_name| PcieConflict {
818 reason: PcieConflictReason::ExistingDev(existing_dev_name),
819 conflict_dev: name,
820 })
821 }
822 }
823
824 impl RegisterWeakMutexPcie for Arc<CloseableMutex<pcie::switch::GenericPcieSwitch>> {
826 fn add_pcie_device(
827 &mut self,
828 port_devfn: u8,
829 name: Arc<str>,
830 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
831 ) -> Result<(), PcieConflict> {
832 self.lock()
833 .add_pcie_device(port_devfn, &name, Box::new(WeakMutexPciDeviceWrapper(dev)))
834 .map_err(|err| PcieConflict {
835 reason: PcieConflictReason::ExistingDev(err.to_string().into()),
836 conflict_dev: name,
837 })
838 }
839
840 fn downstream_ports(&self) -> Vec<pcie::root::DownstreamPortInfo> {
841 self.lock().downstream_ports()
842 }
843 }
844}
845
846#[cfg(any(
847 feature = "dev_generic_isa_floppy",
848 feature = "dev_winbond_super_io_and_floppy_full",
849 feature = "dev_winbond_super_io_and_floppy_stub"
850))]
851pub struct ArcMutexIsaDmaChannel {
852 channel_num: u8,
853 dma: Arc<CloseableMutex<dma::DmaController>>,
854}
855
856#[cfg(any(
857 feature = "dev_generic_isa_floppy",
858 feature = "dev_winbond_super_io_and_floppy_full",
859 feature = "dev_winbond_super_io_and_floppy_stub"
860))]
861impl ArcMutexIsaDmaChannel {
862 pub fn new(dma: Arc<CloseableMutex<dma::DmaController>>, channel_num: u8) -> Self {
863 Self { dma, channel_num }
864 }
865}
866
867#[cfg(any(
868 feature = "dev_generic_isa_floppy",
869 feature = "dev_winbond_super_io_and_floppy_full",
870 feature = "dev_winbond_super_io_and_floppy_stub"
871))]
872impl vmcore::isa_dma_channel::IsaDmaChannel for ArcMutexIsaDmaChannel {
873 fn check_transfer_size(&mut self) -> u16 {
874 self.dma.lock().check_transfer_size(self.channel_num.into())
875 }
876
877 fn request(
878 &mut self,
879 direction: vmcore::isa_dma_channel::IsaDmaDirection,
880 ) -> Option<vmcore::isa_dma_channel::IsaDmaBuffer> {
881 self.dma.lock().request(self.channel_num.into(), direction)
882 }
883
884 fn complete(&mut self) {
885 self.dma.lock().complete(self.channel_num.into())
886 }
887}
888
889pub mod options {
891 use super::*;
892 use state_unit::UnitHandle;
893 use vmcore::vmtime::VmTimeSource;
894
895 #[expect(missing_docs)] pub struct BaseChipsetFoundation<'a> {
898 pub is_restoring: bool,
899 pub untrusted_dma_memory: GuestMemory,
911 pub trusted_vtl0_dma_memory: GuestMemory,
923 pub power_event_handler: Arc<dyn crate::PowerEventHandler>,
924 pub debug_event_handler: Arc<dyn crate::DebugEventHandler>,
925 pub vmtime: &'a VmTimeSource,
926 pub vmtime_unit: &'a UnitHandle,
927 pub doorbell_registration: Option<Arc<dyn DoorbellRegistration>>,
928 }
929
930 macro_rules! base_chipset_devices_and_manifest {
931 (
932 impls {
936 $(#[$m:meta])*
937 pub struct $base_chipset_devices:ident {
938 ...
939 }
940
941 $(#[$m2:meta])*
942 pub struct $base_chipset_manifest:ident {
943 ...
944 }
945 }
946
947 devices {
948 $($name:ident: $ty:ty,)*
949 }
950 ) => {paste::paste!{
951 $(#[$m])*
952 pub struct $base_chipset_devices {
953 $(pub [<deps_ $name>]: Option<$ty>,)*
954 }
955
956 $(#[$m2])*
957 pub struct $base_chipset_manifest {
958 $(pub [<with_ $name>]: bool,)*
959 }
960
961 impl $base_chipset_manifest {
962 pub const fn empty() -> Self {
965 Self {
966 $([<with_ $name>]: false,)*
967 }
968 }
969 }
970
971 impl $base_chipset_devices {
972 pub fn empty() -> Self {
975 Self {
976 $([<deps_ $name>]: None,)*
977 }
978 }
979
980 pub fn to_manifest(&self) -> $base_chipset_manifest {
982 let Self {
983 $([<deps_ $name>],)*
984 } = self;
985
986 $base_chipset_manifest {
987 $([<with_ $name>]: [<deps_ $name>].is_some(),)*
988 }
989 }
990 }
991 }};
992 }
993
994 base_chipset_devices_and_manifest! {
995 impls {
996 #[expect(missing_docs)] pub struct BaseChipsetDevices {
999 ...
1003 }
1004
1005 #[expect(missing_docs)] #[derive(Debug, Clone, MeshPayload, PartialEq, Eq)]
1008 pub struct BaseChipsetManifest {
1009 ...
1013 }
1014 }
1015
1016 devices {
1017 generic_cmos_rtc: dev::GenericCmosRtcDeps,
1018 generic_isa_floppy: dev::GenericIsaFloppyDeps,
1019 generic_pci_bus: dev::GenericPciBusDeps,
1020 generic_psp: dev::GenericPspDeps,
1021
1022 hyperv_firmware_pcat: dev::HyperVFirmwarePcat,
1023 hyperv_framebuffer: dev::HyperVFramebufferDeps,
1024 hyperv_ide: dev::HyperVIdeDeps,
1025 hyperv_vga: dev::HyperVVgaDeps,
1026
1027 piix4_cmos_rtc: dev::Piix4CmosRtcDeps,
1028 piix4_pci_bus: dev::Piix4PciBusDeps,
1029
1030 underhill_vga_proxy: dev::UnderhillVgaProxyDeps,
1031
1032 winbond_super_io_and_floppy_stub: dev::WinbondSuperIoAndFloppyStubDeps,
1033 winbond_super_io_and_floppy_full: dev::WinbondSuperIoAndFloppyFullDeps,
1034 }
1035 }
1036
1037 #[derive(MeshPayload, Debug, Copy, Clone)]
1039 pub struct VmChipsetCapabilities {
1040 pub with_ioapic: bool,
1042 pub with_pic: bool,
1044 pub with_pit: bool,
1046 pub with_generic_isa_dma: bool,
1048 pub with_psp: bool,
1050 pub with_guest_watchdog: bool,
1052 pub with_i440bx_host_pci_bridge: bool,
1054 }
1055
1056 pub mod dev {
1058 use super::*;
1059 use crate::BusIdPci;
1060
1061 macro_rules! feature_gated {
1062 (
1063 feature = $feat:literal;
1064
1065 $(#[$m:meta])*
1066 pub struct $root_deps:ident $($rest:tt)*
1067 ) => {
1068 #[cfg(not(feature = $feat))]
1069 #[doc(hidden)]
1070 pub type $root_deps = ();
1071
1072 #[cfg(feature = $feat)]
1073 $(#[$m])*
1074 pub struct $root_deps $($rest)*
1075 };
1076 }
1077
1078 pub struct HyperVIdeDeps {
1083 pub attached_to: BusIdPci,
1085 pub primary_channel_drives: [Option<ide::DriveMedia>; 2],
1087 pub secondary_channel_drives: [Option<ide::DriveMedia>; 2],
1089 }
1090
1091 pub struct GenericPspDeps;
1093
1094 feature_gated! {
1095 feature = "dev_generic_isa_floppy";
1096
1097 pub struct GenericIsaFloppyDeps {
1099 pub irq: u32,
1101 pub dma_channel: u8,
1103 pub pio_base: u16,
1105 pub drives: floppy::DriveRibbon,
1107 }
1108 }
1109
1110 feature_gated! {
1111 feature = "dev_winbond_super_io_and_floppy_stub";
1112
1113 pub struct WinbondSuperIoAndFloppyStubDeps;
1124 }
1125
1126 feature_gated! {
1127 feature = "dev_winbond_super_io_and_floppy_full";
1128
1129 pub struct WinbondSuperIoAndFloppyFullDeps {
1135 pub primary_disk_drive: floppy::DriveRibbon,
1137 pub secondary_disk_drive: floppy::DriveRibbon,
1139 }
1140 }
1141
1142 pub struct GenericPciBusDeps {
1144 pub bus_id: BusIdPci,
1146 pub pio_addr: u16,
1148 pub pio_data: u16,
1150 }
1151
1152 pub struct Piix4PciBusDeps {
1154 pub bus_id: BusIdPci,
1156 }
1157
1158 feature_gated! {
1159 feature = "dev_hyperv_vga";
1160
1161 pub struct HyperVVgaDeps {
1163 pub attached_to: BusIdPci,
1165 pub rom: Option<Box<dyn guestmem::MapRom>>,
1168 }
1169 }
1170
1171 pub struct GenericCmosRtcDeps {
1173 pub irq: u32,
1175 pub time_source: Resource<chipset_resources::CmosRtcTimeSourceHandleKind>,
1177 pub century_reg_idx: u8,
1179 pub initial_cmos: Option<[u8; 256]>,
1181 }
1182
1183 pub struct Piix4CmosRtcDeps {
1185 pub time_source: Resource<chipset_resources::CmosRtcTimeSourceHandleKind>,
1187 pub initial_cmos: Option<[u8; 256]>,
1189 pub enlightened_interrupts: bool,
1192 }
1193
1194 pub struct HyperVFirmwarePcat {
1196 pub config: firmware_pcat::config::PcatBiosConfig,
1199 pub logger: Box<dyn firmware_pcat::PcatLogger>,
1201 pub generation_id_recv: mesh::Receiver<[u8; 16]>,
1203 pub rom: Option<Box<dyn guestmem::MapRom>>,
1206 pub replay_mtrrs: Box<dyn Send + FnMut()>,
1209 }
1210
1211 #[expect(missing_docs)] pub struct HyperVFramebufferDeps {
1217 pub fb_mapper: Box<dyn guestmem::MemoryMapper>,
1218 pub fb: Framebuffer,
1219 pub vtl2_framebuffer_gpa_base: Option<u64>,
1220 }
1221
1222 feature_gated! {
1223 feature = "dev_underhill_vga_proxy";
1224
1225 pub struct UnderhillVgaProxyDeps {
1227 pub attached_to: BusIdPci,
1229 pub pci_cfg_proxy: Arc<dyn vga_proxy::ProxyVgaPciCfgAccess>,
1231 pub register_host_io_fastpath: Box<dyn vga_proxy::RegisterHostIoPortFastPath>,
1233 }
1234 }
1235 }
1236}