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