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 vsm_config,
609 time_source,
610 }) = deps_hyperv_firmware_uefi
611 {
612 builder
613 .arc_mutex_device("uefi")
614 .try_add_async(async |services| {
615 let notify_interrupt = match config.command_set {
616 UefiCommandSet::X64 => {
617 services.new_line(GPE0_LINE_SET, "genid", GPE0_LINE_GENERATION_ID)
618 }
619 UefiCommandSet::Aarch64 => {
620 services.new_line(IRQ_LINE_SET, "genid", GENERATION_ID_IRQ)
621 }
622 };
623 let vmtime = services.register_vmtime();
624 let gm = foundation.trusted_vtl0_dma_memory.clone();
625 let runtime_deps = firmware_uefi::UefiRuntimeDeps {
626 gm: gm.clone(),
627 nvram_storage,
628 logger,
629 vmtime,
630 watchdog_platform,
631 generation_id_deps: generation_id::GenerationIdRuntimeDeps {
632 generation_id_recv,
633 gm,
634 notify_interrupt,
635 },
636 vsm_config,
637 time_source,
638 };
639
640 firmware_uefi::UefiDevice::new(runtime_deps, config, foundation.is_restoring)
641 .await
642 })
643 .await?;
644 }
645
646 if let Some(options::dev::HyperVFirmwarePcat {
647 config,
648 logger,
649 generation_id_recv,
650 rom,
651 replay_mtrrs,
652 }) = deps_hyperv_firmware_pcat
653 {
654 builder.arc_mutex_device("pcat").try_add(|services| {
655 let notify_interrupt =
656 services.new_line(GPE0_LINE_SET, "genid", GPE0_LINE_GENERATION_ID);
657 firmware_pcat::PcatBiosDevice::new(
658 firmware_pcat::PcatBiosRuntimeDeps {
659 gm: foundation.trusted_vtl0_dma_memory.clone(),
660 logger,
661 generation_id_deps: generation_id::GenerationIdRuntimeDeps {
662 generation_id_recv,
663 gm: foundation.trusted_vtl0_dma_memory.clone(),
664 notify_interrupt,
665 },
666 vmtime: services.register_vmtime(),
667 rom,
668 register_pio: &mut services.register_pio(),
669 replay_mtrrs,
670 },
671 config,
672 )
673 })?;
674 }
675
676 if let Some(options::dev::HyperVFramebufferDeps {
677 fb_mapper,
678 fb,
679 vtl2_framebuffer_gpa_base,
680 }) = deps_hyperv_framebuffer
681 {
682 let fb = FramebufferDevice::new(fb_mapper, fb, vtl2_framebuffer_gpa_base);
683 let control = fb.as_ref().ok().map(|fb| fb.control());
684 builder.arc_mutex_device("fb").try_add(|_| fb)?;
685 device_interfaces.framebuffer_local_control = Some(control.unwrap());
686 }
687
688 #[cfg(feature = "dev_hyperv_vga")]
689 if let Some(options::dev::HyperVVgaDeps { attached_to, rom }) = deps_hyperv_vga {
690 builder
691 .arc_mutex_device("vga")
692 .on_pci_bus(attached_to)
693 .try_add(|services| {
694 vga::VgaDevice::new(
695 &driver_source.simple(),
696 services.register_vmtime(),
697 device_interfaces.framebuffer_local_control.clone().unwrap(),
698 rom,
699 )
700 })?;
701 }
702
703 #[cfg(feature = "dev_underhill_vga_proxy")]
704 if let Some(options::dev::UnderhillVgaProxyDeps {
705 attached_to,
706 pci_cfg_proxy,
707 register_host_io_fastpath,
708 }) = deps_underhill_vga_proxy
709 {
710 builder
711 .arc_mutex_device("vga_proxy")
712 .on_pci_bus(attached_to)
713 .add(|_services| {
714 vga_proxy::VgaProxyDevice::new(pci_cfg_proxy, &*register_host_io_fastpath)
715 })?;
716 }
717
718 macro_rules! feature_gate_check {
719 ($feature:literal, $dep:ident) => {
720 #[cfg(not(feature = $feature))]
721 let None::<()> = $dep else {
722 return Err(BaseChipsetBuilderError::FeatureGatedDevice($feature));
723 };
724 };
725 }
726
727 feature_gate_check!("dev_hyperv_vga", deps_hyperv_vga);
728 feature_gate_check!("dev_underhill_vga_proxy", deps_underhill_vga_proxy);
729 feature_gate_check!("dev_generic_isa_floppy", deps_generic_isa_floppy);
730 feature_gate_check!(
731 "dev_winbond_super_io_and_floppy_full",
732 deps_winbond_super_io_and_floppy_full
733 );
734 feature_gate_check!(
735 "dev_winbond_super_io_and_floppy_stub",
736 deps_winbond_super_io_and_floppy_stub
737 );
738
739 for device in device_handles {
740 builder
741 .arc_mutex_device(device.name.as_ref())
742 .try_add_async(async |services| {
743 resolver
744 .resolve(
745 device.resource,
746 ResolveChipsetDeviceHandleParams {
747 device_name: device.name.as_ref(),
748 guest_memory: &foundation.untrusted_dma_memory,
749 encrypted_guest_memory: &foundation.trusted_vtl0_dma_memory,
750 vmtime: foundation.vmtime,
751 is_restoring: foundation.is_restoring,
752 task_driver_source: driver_source,
753 register_mmio: &mut services.register_mmio(),
754 register_pio: &mut services.register_pio(),
755 configure: services,
756 },
757 )
758 .await
759 .map(|dev| dev.0)
760 })
761 .await?;
762 }
763
764 Ok(BaseChipsetBuilderOutput {
765 chipset_builder: builder,
766 device_interfaces,
767 })
768 }
769}
770
771impl ConfigureChipsetDevice for ArcMutexChipsetServices<'_, '_> {
772 fn new_line(
773 &mut self,
774 id: chipset_device_resources::LineSetId,
775 name: &str,
776 vector: u32,
777 ) -> vmcore::line_interrupt::LineInterrupt {
778 self.new_line(id, name, vector)
779 }
780
781 fn add_line_target(
782 &mut self,
783 id: chipset_device_resources::LineSetId,
784 source_range: std::ops::RangeInclusive<u32>,
785 target_start: u32,
786 ) {
787 self.add_line_target(id, source_range, target_start)
788 }
789
790 fn omit_saved_state(&mut self) {
791 self.omit_saved_state();
792 }
793}
794
795mod weak_mutex_pci {
796 use crate::chipset::PciConflict;
797 use crate::chipset::PciConflictReason;
798 use crate::chipset::backing::arc_mutex::pci::RegisterWeakMutexPci;
799 use chipset_device::ChipsetDevice;
800 use chipset_device::io::IoResult;
801 use closeable_mutex::CloseableMutex;
802 use pci_bus::GenericPciBusDevice;
803 use std::sync::Arc;
804 use std::sync::Weak;
805
806 pub struct WeakMutexPciDeviceWrapper(Weak<CloseableMutex<dyn ChipsetDevice>>);
809
810 impl GenericPciBusDevice for WeakMutexPciDeviceWrapper {
811 fn pci_cfg_read(&mut self, offset: u16, value: &mut u32) -> Option<IoResult> {
812 Some(
813 self.0
814 .upgrade()?
815 .lock()
816 .supports_pci()
817 .expect("builder code ensures supports_pci.is_some()")
818 .pci_cfg_read(offset, value),
819 )
820 }
821
822 fn pci_cfg_write(&mut self, offset: u16, value: u32) -> Option<IoResult> {
823 Some(
824 self.0
825 .upgrade()?
826 .lock()
827 .supports_pci()
828 .expect("builder code ensures supports_pci.is_some()")
829 .pci_cfg_write(offset, value),
830 )
831 }
832 }
833
834 impl RegisterWeakMutexPci for Arc<CloseableMutex<pci_bus::GenericPciBus>> {
836 fn add_pci_device(
837 &mut self,
838 bus: u8,
839 device: u8,
840 function: u8,
841 name: Arc<str>,
842 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
843 ) -> Result<(), PciConflict> {
844 self.lock()
845 .add_pci_device(
846 bus,
847 device,
848 function,
849 name.clone(),
850 WeakMutexPciDeviceWrapper(dev),
851 )
852 .map_err(|(_, existing_dev)| PciConflict {
853 bdf: (bus, device, function),
854 reason: PciConflictReason::ExistingDev(existing_dev),
855 conflict_dev: name,
856 })
857 }
858 }
859
860 impl RegisterWeakMutexPci for Arc<CloseableMutex<chipset_legacy::piix4_pci_bus::Piix4PciBus>> {
862 fn add_pci_device(
863 &mut self,
864 bus: u8,
865 device: u8,
866 function: u8,
867 name: Arc<str>,
868 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
869 ) -> Result<(), PciConflict> {
870 self.lock()
871 .as_pci_bus()
872 .add_pci_device(
873 bus,
874 device,
875 function,
876 name.clone(),
877 WeakMutexPciDeviceWrapper(dev),
878 )
879 .map_err(|(_, existing_dev)| PciConflict {
880 bdf: (bus, device, function),
881 reason: PciConflictReason::ExistingDev(existing_dev),
882 conflict_dev: name,
883 })
884 }
885 }
886}
887
888pub struct ArcMutexIsaDmaChannel {
889 channel_num: u8,
890 dma: Arc<CloseableMutex<dma::DmaController>>,
891}
892
893impl ArcMutexIsaDmaChannel {
894 #[allow(dead_code)] pub fn new(dma: Arc<CloseableMutex<dma::DmaController>>, channel_num: u8) -> Self {
896 Self { dma, channel_num }
897 }
898}
899
900impl vmcore::isa_dma_channel::IsaDmaChannel for ArcMutexIsaDmaChannel {
901 fn check_transfer_size(&mut self) -> u16 {
902 self.dma.lock().check_transfer_size(self.channel_num.into())
903 }
904
905 fn request(
906 &mut self,
907 direction: vmcore::isa_dma_channel::IsaDmaDirection,
908 ) -> Option<vmcore::isa_dma_channel::IsaDmaBuffer> {
909 self.dma.lock().request(self.channel_num.into(), direction)
910 }
911
912 fn complete(&mut self) {
913 self.dma.lock().complete(self.channel_num.into())
914 }
915}
916
917pub mod options {
919 use super::*;
920 use state_unit::UnitHandle;
921 use vmcore::vmtime::VmTimeSource;
922
923 #[expect(missing_docs)] pub struct BaseChipsetFoundation<'a> {
926 pub is_restoring: bool,
927 pub untrusted_dma_memory: GuestMemory,
939 pub trusted_vtl0_dma_memory: GuestMemory,
951 pub power_event_handler: Arc<dyn crate::PowerEventHandler>,
952 pub debug_event_handler: Arc<dyn crate::DebugEventHandler>,
953 pub vmtime: &'a VmTimeSource,
954 pub vmtime_unit: &'a UnitHandle,
955 pub doorbell_registration: Option<Arc<dyn DoorbellRegistration>>,
956 }
957
958 macro_rules! base_chipset_devices_and_manifest {
959 (
960 impls {
964 $(#[$m:meta])*
965 pub struct $base_chipset_devices:ident {
966 ...
967 }
968
969 $(#[$m2:meta])*
970 pub struct $base_chipset_manifest:ident {
971 ...
972 }
973 }
974
975 devices {
976 $($name:ident: $ty:ty,)*
977 }
978 ) => {paste::paste!{
979 $(#[$m])*
980 pub struct $base_chipset_devices {
981 $(pub [<deps_ $name>]: Option<$ty>,)*
982 }
983
984 $(#[$m2])*
985 pub struct $base_chipset_manifest {
986 $(pub [<with_ $name>]: bool,)*
987 }
988
989 impl $base_chipset_manifest {
990 pub const fn empty() -> Self {
993 Self {
994 $([<with_ $name>]: false,)*
995 }
996 }
997 }
998
999 impl $base_chipset_devices {
1000 pub fn empty() -> Self {
1003 Self {
1004 $([<deps_ $name>]: None,)*
1005 }
1006 }
1007
1008 pub fn to_manifest(&self) -> $base_chipset_manifest {
1010 let Self {
1011 $([<deps_ $name>],)*
1012 } = self;
1013
1014 $base_chipset_manifest {
1015 $([<with_ $name>]: [<deps_ $name>].is_some(),)*
1016 }
1017 }
1018 }
1019 }};
1020 }
1021
1022 base_chipset_devices_and_manifest! {
1023 impls {
1024 #[expect(missing_docs)] pub struct BaseChipsetDevices {
1027 ...
1031 }
1032
1033 #[expect(missing_docs)] #[derive(Debug, Clone, MeshPayload, PartialEq, Eq)]
1036 pub struct BaseChipsetManifest {
1037 ...
1041 }
1042 }
1043
1044 devices {
1045 generic_cmos_rtc: dev::GenericCmosRtcDeps,
1046 generic_ioapic: dev::GenericIoApicDeps,
1047 generic_isa_dma: dev::GenericIsaDmaDeps,
1048 generic_isa_floppy: dev::GenericIsaFloppyDeps,
1049 generic_pci_bus: dev::GenericPciBusDeps,
1050 generic_pic: dev::GenericPicDeps,
1051 generic_pit: dev::GenericPitDeps,
1052 generic_psp: dev::GenericPspDeps,
1053
1054 hyperv_firmware_pcat: dev::HyperVFirmwarePcat,
1055 hyperv_firmware_uefi: dev::HyperVFirmwareUefi,
1056 hyperv_framebuffer: dev::HyperVFramebufferDeps,
1057 hyperv_guest_watchdog: dev::HyperVGuestWatchdogDeps,
1058 hyperv_ide: dev::HyperVIdeDeps,
1059 hyperv_power_management: dev::HyperVPowerManagementDeps,
1060 hyperv_vga: dev::HyperVVgaDeps,
1061
1062 i440bx_host_pci_bridge: dev::I440BxHostPciBridgeDeps,
1063
1064 piix4_cmos_rtc: dev::Piix4CmosRtcDeps,
1065 piix4_pci_bus: dev::Piix4PciBusDeps,
1066 piix4_pci_isa_bridge: dev::Piix4PciIsaBridgeDeps,
1067 piix4_pci_usb_uhci_stub: dev::Piix4PciUsbUhciStubDeps,
1068 piix4_power_management: dev::Piix4PowerManagementDeps,
1069
1070 underhill_vga_proxy: dev::UnderhillVgaProxyDeps,
1071
1072 winbond_super_io_and_floppy_stub: dev::WinbondSuperIoAndFloppyStubDeps,
1073 winbond_super_io_and_floppy_full: dev::WinbondSuperIoAndFloppyFullDeps,
1074 }
1075 }
1076
1077 pub mod dev {
1079 use super::*;
1080 use crate::BusIdPci;
1081 use chipset_resources::battery::HostBatteryUpdate;
1082 use local_clock::InspectableLocalClock;
1083
1084 macro_rules! feature_gated {
1085 (
1086 feature = $feat:literal;
1087
1088 $(#[$m:meta])*
1089 pub struct $root_deps:ident $($rest:tt)*
1090 ) => {
1091 #[cfg(not(feature = $feat))]
1092 #[doc(hidden)]
1093 pub type $root_deps = ();
1094
1095 #[cfg(feature = $feat)]
1096 $(#[$m])*
1097 pub struct $root_deps $($rest)*
1098 };
1099 }
1100
1101 pub struct Piix4PciIsaBridgeDeps {
1103 pub attached_to: BusIdPci,
1105 }
1106
1107 pub struct HyperVIdeDeps {
1112 pub attached_to: BusIdPci,
1114 pub primary_channel_drives: [Option<ide::DriveMedia>; 2],
1116 pub secondary_channel_drives: [Option<ide::DriveMedia>; 2],
1118 }
1119
1120 pub struct Piix4PciUsbUhciStubDeps {
1125 pub attached_to: BusIdPci,
1127 }
1128
1129 pub struct Piix4PowerManagementDeps {
1131 pub attached_to: BusIdPci,
1133 pub pm_timer_assist: Option<Box<dyn pm::PmTimerAssist>>,
1135 }
1136
1137 pub struct GenericIsaDmaDeps;
1139
1140 pub struct HyperVPowerManagementDeps {
1142 pub acpi_irq: u32,
1144 pub pio_base: u16,
1146 pub pm_timer_assist: Option<Box<dyn pm::PmTimerAssist>>,
1148 }
1149
1150 pub struct GenericPspDeps;
1152
1153 feature_gated! {
1154 feature = "dev_generic_isa_floppy";
1155
1156 pub struct GenericIsaFloppyDeps {
1158 pub irq: u32,
1160 pub dma_channel: u8,
1162 pub pio_base: u16,
1164 pub drives: floppy::DriveRibbon,
1166 }
1167 }
1168
1169 feature_gated! {
1170 feature = "dev_winbond_super_io_and_floppy_stub";
1171
1172 pub struct WinbondSuperIoAndFloppyStubDeps;
1183 }
1184
1185 feature_gated! {
1186 feature = "dev_winbond_super_io_and_floppy_full";
1187
1188 pub struct WinbondSuperIoAndFloppyFullDeps {
1194 pub primary_disk_drive: floppy::DriveRibbon,
1196 pub secondary_disk_drive: floppy::DriveRibbon,
1198 }
1199 }
1200
1201 pub struct GenericPciBusDeps {
1203 pub bus_id: BusIdPci,
1205 pub pio_addr: u16,
1207 pub pio_data: u16,
1209 }
1210
1211 pub struct Piix4PciBusDeps {
1213 pub bus_id: BusIdPci,
1215 }
1216
1217 pub struct I440BxHostPciBridgeDeps {
1219 pub attached_to: BusIdPci,
1221 pub adjust_gpa_range: Box<dyn chipset_legacy::i440bx_host_pci_bridge::AdjustGpaRange>,
1223 }
1224
1225 pub struct GenericPitDeps;
1227
1228 feature_gated! {
1229 feature = "dev_hyperv_vga";
1230
1231 pub struct HyperVVgaDeps {
1233 pub attached_to: BusIdPci,
1235 pub rom: Option<Box<dyn guestmem::MapRom>>,
1238 }
1239 }
1240
1241 pub struct GenericPicDeps {}
1243
1244 pub struct GenericIoApicDeps {
1246 pub num_entries: u8,
1248 pub routing: Box<dyn ioapic::IoApicRouting>,
1250 }
1251
1252 pub struct GenericCmosRtcDeps {
1254 pub irq: u32,
1256 pub time_source: Box<dyn InspectableLocalClock>,
1258 pub century_reg_idx: u8,
1260 pub initial_cmos: Option<[u8; 256]>,
1262 }
1263
1264 pub struct Piix4CmosRtcDeps {
1266 pub time_source: Box<dyn InspectableLocalClock>,
1268 pub initial_cmos: Option<[u8; 256]>,
1270 pub enlightened_interrupts: bool,
1273 }
1274
1275 pub struct HyperVBatteryDeps {
1277 pub base_addr: u64,
1279 pub use_gpe0: bool,
1281 pub line_interrupt_no: u32,
1283 pub battery_status_recv: mesh::Receiver<HostBatteryUpdate>,
1285 }
1286
1287 pub struct HyperVGuestWatchdogDeps {
1289 pub port_base: u16,
1291 pub watchdog_platform: Box<dyn watchdog_core::platform::WatchdogPlatform>,
1294 }
1295
1296 pub struct HyperVFirmwarePcat {
1298 pub config: firmware_pcat::config::PcatBiosConfig,
1301 pub logger: Box<dyn firmware_pcat::PcatLogger>,
1303 pub generation_id_recv: mesh::Receiver<[u8; 16]>,
1305 pub rom: Option<Box<dyn guestmem::MapRom>>,
1308 pub replay_mtrrs: Box<dyn Send + FnMut()>,
1311 }
1312
1313 pub struct HyperVFirmwareUefi {
1315 pub config: firmware_uefi::UefiConfig,
1318 pub logger: Box<dyn firmware_uefi::platform::logger::UefiLogger>,
1320 pub nvram_storage: Box<dyn uefi_nvram_storage::VmmNvramStorage>,
1322 pub generation_id_recv: mesh::Receiver<[u8; 16]>,
1324 pub watchdog_platform: Box<dyn watchdog_core::platform::WatchdogPlatform>,
1327 pub vsm_config: Option<Box<dyn firmware_uefi::platform::nvram::VsmConfig>>,
1330 pub time_source: Box<dyn InspectableLocalClock>,
1332 }
1333
1334 #[expect(missing_docs)] pub struct HyperVFramebufferDeps {
1340 pub fb_mapper: Box<dyn guestmem::MemoryMapper>,
1341 pub fb: Framebuffer,
1342 pub vtl2_framebuffer_gpa_base: Option<u64>,
1343 }
1344
1345 feature_gated! {
1346 feature = "dev_underhill_vga_proxy";
1347
1348 pub struct UnderhillVgaProxyDeps {
1350 pub attached_to: BusIdPci,
1352 pub pci_cfg_proxy: Arc<dyn vga_proxy::ProxyVgaPciCfgAccess>,
1354 pub register_host_io_fastpath: Box<dyn vga_proxy::RegisterHostIoPortFastPath>,
1356 }
1357 }
1358 }
1359}