1use core::array;
6use fidl_next_fuchsia_hardware_pci as fidl_pci;
7use fuchsia_async::{OnInterrupt, Task};
8use futures::StreamExt;
9use mmio::region::MmioRegion;
10use mmio::vmo::VmoMemory;
11use std::num::NonZero;
12
13use super::capabilities::VirtioPciCapabilities;
14use super::common_configuration::{VirtioPciCommonConfiguration, *};
15use super::pci_notifications::VirtioPciNotifications;
16use super::pci_queue::VirtioPciQueue;
17use crate::{DeviceStatus, VirtioBufferRef, VirtioFeatureBits, VirtioMemoryRange};
18
19pub struct VirtioPciDevice {
24 #[expect(dead_code)]
28 pci: fidl_next::Client<fidl_pci::Device>,
29
30 bti: zx::Bti,
32
33 feature_bits: VirtioFeatureBits,
35
36 #[expect(dead_code)]
37 configuration: VirtioPciCommonConfiguration<MmioRegion<VmoMemory>>,
38
39 notifications: VirtioPciNotifications,
40
41 queues: Box<[VirtioPciQueue]>,
43
44 interrupt_receiver: futures::channel::mpsc::UnboundedReceiver<()>,
46
47 #[expect(dead_code)]
51 interrupt_task: Task<()>,
52}
53
54impl VirtioPciDevice {
55 pub fn bti(&self) -> &zx::Bti {
57 &self.bti
58 }
59
60 pub fn feature_bits(&self) -> VirtioFeatureBits {
62 self.feature_bits
63 }
64
65 pub async fn submit_and_wait_for_buffer(
67 &mut self,
68 queue_index: u16,
69 buffer: &[VirtioMemoryRange],
70 ) -> Result<u32, zx::Status> {
71 if queue_index as usize >= self.queues.len() {
72 return Err(zx::Status::INVALID_ARGS);
73 }
74 let queue = &mut self.queues[queue_index as usize];
75
76 let buffer_ref = VirtioBufferRef::new(buffer);
77
78 let submitted_buffer_id = unsafe { queue.submit_buffer(buffer_ref)? };
81
82 self.notifications.trigger(queue.notification_data());
83
84 let written_bytes = loop {
85 if let Some(returned_buffer_info) = queue.take_returned_buffer() {
86 assert!(
87 returned_buffer_info.submitted_buffer_id == submitted_buffer_id,
88 "Multiple concurrent buffer submissions not yet supported",
89 );
90 break returned_buffer_info.written_bytes;
91 }
92
93 let _ = self.interrupt_receiver.next().await;
96 };
97
98 Ok(written_bytes)
99 }
100
101 fn start_interrupt_task(
106 interrupt: zx::Interrupt,
107 interrupt_sender: futures::channel::mpsc::UnboundedSender<()>,
108 ) -> Task<()> {
109 let mut interrupt_stream = Box::pin(OnInterrupt::new(interrupt));
111
112 Task::local(async move {
113 while let Some(Ok(_interrupt_timestamp)) = interrupt_stream.as_mut().next().await {
114 let _ = interrupt_sender.unbounded_send(());
117
118 if let Err(status) =
119 std::pin::Pin::get_ref(interrupt_stream.as_ref()).as_ref().ack()
120 {
121 log::error!("Failed to acknowledge a queue interrupt: {:?}", status);
122 }
123 }
124 })
125 }
126}
127
128struct VirtioPciMsixVectors {
131 pub interrupts: [zx::Interrupt; Self::COUNT],
133}
134
135impl VirtioPciMsixVectors {
136 pub const COUNT: usize = 2;
138
139 pub const CONFIGURATION_CHANGE_VECTOR: u16 = 0;
141
142 pub const QUEUE_NOTIFICATION_VECTOR: u16 = 1;
144
145 pub const INVALID: u16 = 0xffff;
152}
153
154pub struct VirtioPciDeviceBuilder {
160 pci: fidl_next::Client<fidl_pci::Device>,
161 bti: zx::Bti,
162 msix_vectors: VirtioPciMsixVectors,
163
164 configuration: VirtioPciCommonConfiguration<MmioRegion<VmoMemory>>,
165 notifications: VirtioPciNotifications,
166
167 device_configuration: Option<MmioRegion<VmoMemory>>,
169
170 offered_features: Option<VirtioFeatureBits>,
172
173 accepted_features: Option<VirtioFeatureBits>,
175
176 queues: Vec<VirtioPciQueue>,
178}
179
180impl VirtioPciDeviceBuilder {
181 pub async fn new(
185 pci: fidl_next::ClientEnd<fidl_pci::Device>,
186 ) -> Result<VirtioPciDeviceBuilder, zx::Status> {
187 let pci = pci.spawn();
192 let bti = Self::get_pci_bti(&pci).await?;
193 let msix_vectors = Self::get_pci_interrupts(&pci).await?;
194 let capabilities = VirtioPciCapabilities::new(&pci).await?;
195
196 let mut builder = Self {
197 pci: pci.clone(),
198 bti,
199 msix_vectors,
200
201 configuration: capabilities.common_configuration,
202 notifications: capabilities.notifications,
203 device_configuration: capabilities.device_configuration,
204
205 offered_features: None,
206 accepted_features: None,
207 queues: Vec::new(),
208 };
209
210 builder.reset_virtio();
211 builder.read_offered_features();
212 Ok(builder)
213 }
214
215 pub fn offered_features(&self) -> VirtioFeatureBits {
220 self.offered_features.unwrap()
223 }
224
225 pub async fn accept_features(
240 &mut self,
241 mut accepted_features: VirtioFeatureBits,
242 ) -> Result<(), zx::Status> {
243 let offered_features = self.offered_features.unwrap();
251
252 if !offered_features.uses_virtio1_standard() {
253 log::warn!("Refusing to operate device without virtio 1.0+ standard support");
254 self.set_driver_terminated();
255 return Err(zx::Status::NOT_SUPPORTED);
256 }
257 accepted_features.set_uses_virtio1_standard(true);
258
259 self.write_accepted_features(accepted_features)?;
260 self.initialize_transport()?;
261 self.initialize_virtqueues()?;
262
263 Ok(())
264 }
265
266 pub fn bti(&self) -> &zx::Bti {
270 &self.bti
271 }
272
273 pub fn take_device_configuration(&mut self) -> Option<MmioRegion<VmoMemory>> {
284 self.device_configuration.take()
285 }
286
287 pub fn build(mut self) -> Result<VirtioPciDevice, zx::Status> {
295 self.finish_virtio_initialization()?;
298
299 let (interrupt_sender, interrupt_receiver) = futures::channel::mpsc::unbounded();
300
301 let [_configuration_change_interrupt, queue_notification_interrupt] =
305 self.msix_vectors.interrupts;
306 let interrupt_task =
307 VirtioPciDevice::start_interrupt_task(queue_notification_interrupt, interrupt_sender);
308
309 Ok(VirtioPciDevice {
310 pci: self.pci,
311 bti: self.bti,
312 feature_bits: self.accepted_features.expect("accept_features() not called"),
313
314 configuration: self.configuration,
315 notifications: self.notifications,
316
317 queues: self.queues.into_boxed_slice(),
318
319 interrupt_receiver,
320 interrupt_task,
321 })
322 }
323
324 async fn get_pci_bti(pci: &fidl_next::Client<fidl_pci::Device>) -> Result<zx::Bti, zx::Status> {
329 const PCI_DEVICE_ADDRESSABLE_BTI_ID: u32 = 0;
333
334 let get_bti_response = pci
335 .get_bti(PCI_DEVICE_ADDRESSABLE_BTI_ID)
336 .await
337 .map_err(|_| zx::Status::INTERNAL)?
338 .map_err(|_| zx::Status::INTERNAL)?;
339 let bti = get_bti_response.bti;
340 debug_assert!(!bti.is_invalid(), "GetBti() returned invalid BTI");
341 Ok(bti)
342 }
343
344 async fn get_pci_interrupts(
351 pci: &fidl_next::Client<fidl_pci::Device>,
352 ) -> Result<VirtioPciMsixVectors, zx::Status> {
353 pci.set_bus_mastering(true)
357 .await
358 .map_err(|error| {
359 log::warn!("Failed to enable PCI bus mastering: {:?}", error);
360 zx::Status::INTERNAL
361 })?
362 .map_err(|status| {
363 log::warn!("PCI bus rejected enabling bus mastering: {:?}", status);
364 zx::Status::INTERNAL
365 })?;
366
367 pci.set_interrupt_mode(fidl_pci::InterruptMode::MsiX, VirtioPciMsixVectors::COUNT as u32)
368 .await
369 .map_err(|error| {
370 log::warn!("Failed to switch the PCI device to MSI-X interrupts: {:?}", error);
371 zx::Status::INTERNAL
372 })?
373 .map_err(|status| {
374 log::warn!("PCI bus rejected MSI-X interrupts: {:?}", status);
375 zx::Status::INTERNAL
376 })?;
377
378 let mut interrupts: [zx::Interrupt; VirtioPciMsixVectors::COUNT] =
379 array::from_fn(|_vector_index| zx::Interrupt::invalid());
380 for (vector_index, interrupt) in interrupts.iter_mut().enumerate() {
381 let map_interrupt_response = pci
382 .map_interrupt(vector_index as u32)
383 .await
384 .map_err(|error| {
385 log::warn!("Failed to map MSI-X vector {}: {:?}", vector_index, error);
386 zx::Status::INTERNAL
387 })?
388 .map_err(|status| {
389 log::warn!(
390 "PCI bus rejected mapping MSI-X vector {}: {:?}",
391 vector_index,
392 status
393 );
394 zx::Status::INTERNAL
395 })?;
396 *interrupt = map_interrupt_response.interrupt;
397 }
398
399 Ok(VirtioPciMsixVectors { interrupts })
400 }
401
402 fn reset_virtio(&mut self) {
410 debug_assert!(
414 self.offered_features.is_none(),
415 "Reset not currently supported after feature negotiation started"
416 );
417 debug_assert!(
418 self.accepted_features.is_none(),
419 "Reset not currently supported after feature negotiation"
420 );
421
422 let mut device_status_register = self.configuration.device_status_mut();
425 device_status_register.write(DeviceStatusReg(DeviceStatus::RESET.0));
426
427 while device_status_register.read().value() != DeviceStatus::RESET.0 {}
428
429 let mut device_status = DeviceStatus::RESET;
432 device_status.set_virtio_device_detected(true);
433 device_status_register.write(DeviceStatusReg(device_status.0));
434
435 device_status.set_driver_found(true);
438 device_status_register.write(DeviceStatusReg(device_status.0));
439 }
440
441 fn read_offered_features(&mut self) {
443 debug_assert!(self.offered_features.is_none(), "Offered features already read");
444
445 let mut raw_feature_bits: u128 = 0;
448 for word_index in 0..4 {
449 self.configuration
450 .device_features_word_index_mut()
451 .write(DeviceFeaturesWordIndex(word_index));
452 let raw_feature_word: u32 = self.configuration.device_features_word().read().value();
453 raw_feature_bits |= (raw_feature_word as u128) << (word_index * 32);
454 }
455
456 self.offered_features = Some(VirtioFeatureBits(raw_feature_bits));
457 }
458
459 fn write_accepted_features(
472 &mut self,
473 feature_bits: VirtioFeatureBits,
474 ) -> Result<(), zx::Status> {
475 debug_assert!(
476 !self.offered_features.is_none(),
477 "Accepted features are not based on offered features"
478 );
479 debug_assert!(self.accepted_features.is_none(), "Accepted features already written");
480 debug_assert!(
481 feature_bits.0 & self.offered_features.unwrap().0 == feature_bits.0,
482 "Accepted features are not a subset of the offered features",
483 );
484
485 let mut device_status = DeviceStatus(self.configuration.device_status().read().value());
486
487 let mut expected_device_status = DeviceStatus::default();
488 expected_device_status.set_virtio_device_detected(true);
489 expected_device_status.set_driver_found(true);
490
491 if expected_device_status != device_status {
492 log::warn!(
493 "Unexpected virtio device status during feature negotiation: {:?}",
494 device_status
495 );
496
497 device_status.set_driver_terminated(true);
498 self.configuration.device_status_mut().write(DeviceStatusReg(device_status.0));
499 return Err(zx::Status::IO);
500 }
501
502 let mut raw_feature_bits: u128 = feature_bits.0;
505 for word_index in 0..4 {
506 let raw_feature_word: u32 = raw_feature_bits as u32;
507 raw_feature_bits >>= 32;
508
509 if raw_feature_word == 0 {
514 continue;
515 }
516
517 self.configuration
518 .driver_features_word_index_mut()
519 .write(DriverFeaturesWordIndex(word_index));
520 self.configuration
521 .driver_features_word_mut()
522 .write(DriverFeaturesWord(raw_feature_word));
523 }
524
525 device_status.set_feature_negotiation_complete(true);
528 self.configuration.device_status_mut().write(DeviceStatusReg(device_status.0));
529
530 device_status = DeviceStatus(self.configuration.device_status().read().value());
533 if !device_status.feature_negotiation_complete() {
534 log::warn!(
535 "virtio device does not support offered features {:#x} {:?}",
536 feature_bits.0,
537 feature_bits
538 );
539
540 device_status.set_driver_terminated(true);
541 self.configuration.device_status_mut().write(DeviceStatusReg(device_status.0));
542 return Err(zx::Status::IO);
543 }
544
545 self.accepted_features = Some(feature_bits);
546 Ok(())
547 }
548
549 fn initialize_transport(&mut self) -> Result<(), zx::Status> {
556 self.configuration.configuration_change_msix_vector_mut().write(
560 ConfigurationChangeMsixVector(VirtioPciMsixVectors::CONFIGURATION_CHANGE_VECTOR),
561 );
562
563 if self.configuration.configuration_change_msix_vector().read().0
567 == VirtioPciMsixVectors::INVALID
568 {
569 log::warn!("virtio device rejected the configuration change MSI-X vector");
570 self.set_driver_terminated();
571 return Err(zx::Status::IO);
572 }
573 Ok(())
574 }
575
576 fn initialize_virtqueues(&mut self) -> Result<(), zx::Status> {
583 debug_assert!(self.queues.is_empty(), "The device's virtqueues are already set up");
584
585 let queue_count = self.configuration.queue_count().read().value();
586
587 for queue_index in 0..queue_count {
588 self.configuration
590 .configured_queue_index_mut()
591 .write(ConfiguredQueueIndex(queue_index));
592
593 let queue_capacity_raw = self.configuration.configured_queue_capacity().read().value();
594 let Some(queue_capacity) = NonZero::<u16>::new(queue_capacity_raw) else {
595 log::warn!("virtqueue {} is disabled (capacity set to 0)", queue_index);
596 self.set_driver_terminated();
597 return Err(zx::Status::IO_DATA_LOSS);
598 };
599
600 let notification_offset =
601 self.configuration.configured_queue_notification_offset().read();
602
603 let queue_id = queue_index;
606
607 let notification_data =
608 self.notifications.data_for_queue(queue_id, notification_offset);
609
610 let (pci_queue, queue_memory_layout) =
611 VirtioPciQueue::new(&self.bti, queue_capacity, notification_data)?;
612
613 let desc_addr = queue_memory_layout.descriptor_table_physical_address;
618 self.configuration
619 .configured_queue_descriptor_table_address_low_mut()
620 .write(ConfiguredQueueDescriptorTableAddressLow(desc_addr as u32));
621 self.configuration
622 .configured_queue_descriptor_table_address_high_mut()
623 .write(ConfiguredQueueDescriptorTableAddressHigh((desc_addr >> 32) as u32));
624
625 let driver_addr = queue_memory_layout.submitted_ring_physical_address;
626 self.configuration
627 .configured_queue_driver_area_address_low_mut()
628 .write(ConfiguredQueueDriverAreaAddressLow(driver_addr as u32));
629 self.configuration
630 .configured_queue_driver_area_address_high_mut()
631 .write(ConfiguredQueueDriverAreaAddressHigh((driver_addr >> 32) as u32));
632
633 let device_addr = queue_memory_layout.returned_ring_physical_address;
634 self.configuration
635 .configured_queue_device_area_address_low_mut()
636 .write(ConfiguredQueueDeviceAreaAddressLow(device_addr as u32));
637 self.configuration
638 .configured_queue_device_area_address_high_mut()
639 .write(ConfiguredQueueDeviceAreaAddressHigh((device_addr >> 32) as u32));
640
641 self.configuration
643 .configured_queue_msix_vector_mut()
644 .write(ConfiguredQueueMsixVector(VirtioPciMsixVectors::QUEUE_NOTIFICATION_VECTOR));
645 if self.configuration.configured_queue_msix_vector().read().0
646 == VirtioPciMsixVectors::INVALID
647 {
648 log::warn!("virtio device rejected the MSI-X vector for queue {}", queue_index);
649 self.set_driver_terminated();
650 return Err(zx::Status::IO);
651 }
652
653 self.configuration.configured_queue_enabled_mut().write(ConfiguredQueueEnabled(1));
654 self.queues.push(pci_queue);
655 }
656
657 Ok(())
658 }
659
660 fn finish_virtio_initialization(&mut self) -> Result<(), zx::Status> {
667 let mut device_status = DeviceStatus(self.configuration.device_status().read().value());
668
669 let mut expected_device_status = DeviceStatus::default();
670 expected_device_status.set_virtio_device_detected(true);
671 expected_device_status.set_driver_found(true);
672 expected_device_status.set_feature_negotiation_complete(true);
673
674 if expected_device_status != device_status {
675 log::warn!(
676 "Unexpected virtio device status after device-specific initialization: {:?}",
677 device_status
678 );
679
680 device_status.set_driver_terminated(true);
681 self.configuration.device_status_mut().write(DeviceStatusReg(device_status.0));
682 return Err(zx::Status::IO);
683 }
684
685 device_status.set_driver_initialized(true);
688 self.configuration.device_status_mut().write(DeviceStatusReg(device_status.0));
689
690 Ok(())
691 }
692
693 fn set_driver_terminated(&mut self) {
699 let mut device_status = DeviceStatus(self.configuration.device_status().read().value());
700 device_status.set_driver_terminated(true);
701 self.configuration.device_status_mut().write(DeviceStatusReg(device_status.0));
702 }
703}
704
705