Alioth Code Coverage

pci.rs0.00%

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::io::ErrorKind;
16use std::marker::PhantomData;
17use std::mem::size_of;
18use std::os::fd::{AsFd, AsRawFd, BorrowedFd};
19use std::sync::Arc;
20use std::sync::atomic::{AtomicU16, Ordering};
21use std::sync::mpsc::Sender;
22
23use alioth_macros::Layout;
24use parking_lot::{Mutex, RwLock};
25use zerocopy::{FromZeros, Immutable, IntoBytes};
26
27use crate::device::Pause;
28use crate::hv::{self, IoeventFd, IoeventFdRegistry, IrqFd, MsiSender};
29use crate::mem::emulated::{Action, Mmio};
30use crate::mem::{MemRange, MemRegion, MemRegionCallback, MemRegionEntry};
31use crate::pci::cap::{
32 MsixCap, MsixCapMmio, MsixCapOffset, MsixMsgCtrl, MsixTableEntry, MsixTableMmio,
33 MsixTableMmioEntry, PciCap, PciCapHdr, PciCapId, PciCapList,
34};
35use crate::pci::config::{
36 BAR_MEM32, BAR_MEM64, BAR_PREFETCHABLE, CommonHeader, DeviceHeader, EmulatedConfig, HeaderType,
37 PciConfig, PciConfigArea,
38};
39use crate::pci::{self, Pci, PciBar};
40use crate::sync::notifier::Notifier;
41use crate::utils::{get_atomic_high32, get_atomic_low32, set_atomic_high32, set_atomic_low32};
42use crate::virtio::dev::{Register, StartParam, VirtioDevice, WakeEvent};
43use crate::virtio::queue::QueueReg;
44use crate::virtio::{DevStatus, DeviceId, IrqSender, Result, error};
45use crate::{consts, impl_mmio_for_zerocopy, mem};
46
47const VIRTIO_MSI_NO_VECTOR: u16 = 0xffff;
48
49#[derive(Debug)]
50struct VirtioPciMsixVector {
51 config: AtomicU16,
52 queues: Vec<AtomicU16>,
53}
54
55#[derive(Debug)]
56pub struct PciIrqSender<S>
57where
58 S: MsiSender,
59{
60 msix_vector: VirtioPciMsixVector,
61 msix_table: Arc<MsixTableMmio<S::IrqFd>>,
62 msi_sender: S,
63}
64
65impl<S> PciIrqSender<S>
66where
67 S: MsiSender,
68{
69 fn send(&self, vector: u16) {
70 let entries = self.msix_table.entries.read();
71 let Some(entry) = entries.get(vector as usize) else {
72 log::error!("invalid config vector: {vector:x}");
73 return;
74 };
75 if entry.get_masked() {
76 log::info!("{vector} is masked");
77 return;
78 }
79 let data = entry.get_data();
80 let addr = ((entry.get_addr_hi() as u64) << 32) | (entry.get_addr_lo() as u64);
81 if let Err(e) = self.msi_sender.send(addr, data) {
82 log::error!("send msi data = {data:#x} to {addr:#x}: {e}")
83 } else {
84 log::trace!("send msi data = {data:#x} to {addr:#x}: done")
85 }
86 }
87
88 fn get_irqfd<F, T>(&self, vector: u16, f: F) -> Result<T>
89 where
90 F: FnOnce(BorrowedFd) -> Result<T>,
91 {
92 let mut entries = self.msix_table.entries.write();
93 let Some(entry) = entries.get_mut(vector as usize) else {
94 return error::InvalidMsixVector { vector }.fail();
95 };
96 match &*entry {
97 MsixTableMmioEntry::Entry(e) => {
98 let irqfd = self.msi_sender.create_irqfd()?;
99 irqfd.set_addr_hi(e.addr_hi)?;
100 irqfd.set_addr_lo(e.addr_lo)?;
101 irqfd.set_data(e.data)?;
102 irqfd.set_masked(e.control.masked())?;
103 let r = f(irqfd.as_fd())?;
104 *entry = MsixTableMmioEntry::IrqFd(irqfd);
105 Ok(r)
106 }
107 MsixTableMmioEntry::IrqFd(fd) => f(fd.as_fd()),
108 }
109 }
110}
111
112impl<S> IrqSender for PciIrqSender<S>
113where
114 S: MsiSender,
115{
116 fn config_irq(&self) {
117 let vector = self.msix_vector.config.load(Ordering::Acquire);
118 if vector != VIRTIO_MSI_NO_VECTOR {
119 self.send(vector)
120 }
121 }
122
123 fn queue_irq(&self, idx: u16) {
124 let Some(vector) = self.msix_vector.queues.get(idx as usize) else {
125 log::error!("invalid queue index: {idx}");
126 return;
127 };
128 let vector = vector.load(Ordering::Acquire);
129 if vector != VIRTIO_MSI_NO_VECTOR {
130 self.send(vector);
131 }
132 }
133
134 fn config_irqfd<F, T>(&self, f: F) -> Result<T>
135 where
136 F: FnOnce(BorrowedFd) -> Result<T>,
137 {
138 self.get_irqfd(self.msix_vector.config.load(Ordering::Acquire), f)
139 }
140
141 fn queue_irqfd<F, T>(&self, idx: u16, f: F) -> Result<T>
142 where
143 F: FnOnce(BorrowedFd) -> Result<T>,
144 {
145 let Some(vector) = self.msix_vector.queues.get(idx as usize) else {
146 return error::InvalidQueueIndex { index: idx }.fail();
147 };
148 self.get_irqfd(vector.load(Ordering::Acquire), f)
149 }
150}
151
152#[repr(C, align(4))]
153#[derive(Layout)]
154pub struct VirtioCommonCfg {
155 device_feature_select: u32,
156 device_feature: u32,
157 driver_feature_select: u32,
158 driver_feature: u32,
159 config_msix_vector: u16,
160 num_queues: u16,
161 device_status: u8,
162 config_generation: u8,
163 queue_select: u16,
164 queue_size: u16,
165 queue_msix_vector: u16,
166 queue_enable: u16,
167 queue_notify_off: u16,
168 queue_desc_lo: u32,
169 queue_desc_hi: u32,
170 queue_driver_lo: u32,
171 queue_driver_hi: u32,
172 queue_device_lo: u32,
173 queue_device_hi: u32,
174 queue_notify_data: u16,
175 queue_reset: u16,
176}
177
178#[derive(Layout)]
179#[repr(C, align(4))]
180pub struct VirtioPciRegister {
181 common: VirtioCommonCfg,
182 isr_status: u32,
183 queue_notify: PhantomData<[u32]>,
184}
185
186#[derive(Debug)]
187pub struct VirtioPciRegisterMmio<M, E>
188where
189 M: MsiSender,
190 E: IoeventFd,
191{
192 name: Arc<str>,
193 reg: Register,
194 queues: Arc<[QueueReg]>,
195 irq_sender: Arc<PciIrqSender<M>>,
196 ioeventfds: Option<Arc<[E]>>,
197 event_tx: Sender<WakeEvent<PciIrqSender<M>, E>>,
198 notifier: Arc<Notifier>,
199}
200
201impl<M, E> VirtioPciRegisterMmio<M, E>
202where
203 M: MsiSender,
204 E: IoeventFd,
205{
206 fn wake_up_dev(&self, event: WakeEvent<PciIrqSender<M>, E>) {
207 let is_start = matches!(event, WakeEvent::Start { .. });
208 if let Err(e) = self.event_tx.send(event) {
209 log::error!("{}: failed to send event: {e}", self.name);
210 return;
211 }
212 if is_start {
213 return;
214 }
215 if let Err(e) = self.notifier.notify() {
216 log::error!("{}: failed to wake up device: {e}", self.name);
217 }
218 }
219
220 fn reset(&self) {
221 let config_msix = &self.irq_sender.msix_vector.config;
222 config_msix.store(VIRTIO_MSI_NO_VECTOR, Ordering::Release);
223 for q_vector in self.irq_sender.msix_vector.queues.iter() {
224 q_vector.store(VIRTIO_MSI_NO_VECTOR, Ordering::Release);
225 }
226 self.irq_sender.msix_table.reset();
227 for q in self.queues.iter() {
228 q.enabled.store(false, Ordering::Release);
229 }
230 }
231
232 fn msix_change_allowed(&self, old: u16) -> bool {
233 let entries = self.irq_sender.msix_table.entries.read();
234 let Some(entry) = entries.get(old as usize) else {
235 return true;
236 };
237 if let MsixTableMmioEntry::IrqFd(fd) = entry {
238 log::error!(
239 "{}: MSI-X vector {old:#x} was assigned to irqfd {:#x}",
240 self.name,
241 fd.as_fd().as_raw_fd(),
242 );
243 false
244 } else {
245 true
246 }
247 }
248}
249
250impl<M, E> Mmio for VirtioPciRegisterMmio<M, E>
251where
252 M: MsiSender,
253 E: IoeventFd,
254{
255 fn size(&self) -> u64 {
256 (size_of::<VirtioPciRegister>() + size_of::<u32>() * self.queues.len()) as u64
257 }
258
259 fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {
260 let reg = &self.reg;
261 let ret = match (offset as usize, size as usize) {
262 VirtioCommonCfg::LAYOUT_DEVICE_FEATURE_SELECT => {
263 reg.device_feature_sel.load(Ordering::Acquire) as u64
264 }
265 VirtioCommonCfg::LAYOUT_DEVICE_FEATURE => {
266 let sel = reg.device_feature_sel.load(Ordering::Acquire);
267 if let Some(feature) = reg.device_feature.get(sel as usize) {
268 *feature as u64
269 } else {
270 0
271 }
272 }
273 VirtioCommonCfg::LAYOUT_DRIVER_FEATURE_SELECT => {
274 reg.driver_feature_sel.load(Ordering::Acquire) as u64
275 }
276 VirtioCommonCfg::LAYOUT_DRIVER_FEATURE => {
277 let sel = reg.driver_feature_sel.load(Ordering::Acquire);
278 if let Some(feature) = reg.driver_feature.get(sel as usize) {
279 feature.load(Ordering::Acquire) as u64
280 } else {
281 0
282 }
283 }
284 VirtioCommonCfg::LAYOUT_CONFIG_MSIX_VECTOR => {
285 self.irq_sender.msix_vector.config.load(Ordering::Acquire) as u64
286 }
287 VirtioCommonCfg::LAYOUT_NUM_QUEUES => self.queues.len() as u64,
288 VirtioCommonCfg::LAYOUT_DEVICE_STATUS => reg.status.load(Ordering::Acquire) as u64,
289 VirtioCommonCfg::LAYOUT_CONFIG_GENERATION => {
290 0 // TODO: support device config change at runtime
291 }
292 VirtioCommonCfg::LAYOUT_QUEUE_SELECT => reg.queue_sel.load(Ordering::Acquire) as u64,
293 VirtioCommonCfg::LAYOUT_QUEUE_SIZE => {
294 let q_sel = reg.queue_sel.load(Ordering::Acquire) as usize;
295 if let Some(q) = self.queues.get(q_sel) {
296 q.size.load(Ordering::Acquire) as u64
297 } else {
298 0
299 }
300 }
301 VirtioCommonCfg::LAYOUT_QUEUE_MSIX_VECTOR => {
302 let q_sel = reg.queue_sel.load(Ordering::Acquire) as usize;
303 if let Some(msix_vector) = self.irq_sender.msix_vector.queues.get(q_sel) {
304 msix_vector.load(Ordering::Acquire) as u64
305 } else {
306 VIRTIO_MSI_NO_VECTOR as u64
307 }
308 }
309 VirtioCommonCfg::LAYOUT_QUEUE_ENABLE => {
310 let q_sel = reg.queue_sel.load(Ordering::Acquire) as usize;
311 if let Some(q) = self.queues.get(q_sel) {
312 q.enabled.load(Ordering::Acquire) as u64
313 } else {
314 0
315 }
316 }
317 VirtioCommonCfg::LAYOUT_QUEUE_NOTIFY_OFF => {
318 reg.queue_sel.load(Ordering::Acquire) as u64
319 }
320 VirtioCommonCfg::LAYOUT_QUEUE_DESC_LO => {
321 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
322 if let Some(q) = self.queues.get(q_sel as usize) {
323 get_atomic_low32(&q.desc) as u64
324 } else {
325 0
326 }
327 }
328 VirtioCommonCfg::LAYOUT_QUEUE_DESC_HI => {
329 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
330 if let Some(q) = self.queues.get(q_sel as usize) {
331 get_atomic_high32(&q.desc) as u64
332 } else {
333 0
334 }
335 }
336 VirtioCommonCfg::LAYOUT_QUEUE_DRIVER_LO => {
337 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
338 if let Some(q) = self.queues.get(q_sel as usize) {
339 get_atomic_high32(&q.driver) as u64
340 } else {
341 0
342 }
343 }
344 VirtioCommonCfg::LAYOUT_QUEUE_DRIVER_HI => {
345 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
346 if let Some(q) = self.queues.get(q_sel as usize) {
347 get_atomic_high32(&q.driver) as u64
348 } else {
349 0
350 }
351 }
352 VirtioCommonCfg::LAYOUT_QUEUE_DEVICE_LO => {
353 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
354 if let Some(q) = self.queues.get(q_sel as usize) {
355 get_atomic_high32(&q.device) as u64
356 } else {
357 0
358 }
359 }
360 VirtioCommonCfg::LAYOUT_QUEUE_DEVICE_HI => {
361 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
362 if let Some(q) = self.queues.get(q_sel as usize) {
363 get_atomic_high32(&q.device) as u64
364 } else {
365 0
366 }
367 }
368 VirtioCommonCfg::LAYOUT_QUEUE_NOTIFY_DATA => {
369 todo!()
370 }
371 VirtioCommonCfg::LAYOUT_QUEUE_RESET => {
372 todo!()
373 }
374 _ => {
375 log::error!(
376 "{}: read invalid register: offset = {offset:#x}, size = {size}",
377 self.name
378 );
379 0
380 }
381 };
382 Ok(ret)
383 }
384
385 fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {
386 let reg = &self.reg;
387 match (offset as usize, size as usize) {
388 VirtioCommonCfg::LAYOUT_DEVICE_FEATURE_SELECT => {
389 reg.device_feature_sel.store(val as u8, Ordering::Release);
390 }
391 VirtioCommonCfg::LAYOUT_DRIVER_FEATURE_SELECT => {
392 reg.driver_feature_sel.store(val as u8, Ordering::Release);
393 }
394 VirtioCommonCfg::LAYOUT_DRIVER_FEATURE => {
395 let sel = reg.driver_feature_sel.load(Ordering::Acquire);
396 if let Some(feature) = reg.driver_feature.get(sel as usize) {
397 feature.store(val as u32, Ordering::Release);
398 } else if val != 0 {
399 log::error!("{}: unknown feature {val:#x} for sel {sel}", self.name);
400 }
401 }
402 VirtioCommonCfg::LAYOUT_CONFIG_MSIX_VECTOR => {
403 let config_msix = &self.irq_sender.msix_vector.config;
404 let old = config_msix.load(Ordering::Acquire);
405 if self.msix_change_allowed(old) {
406 config_msix.store(val as u16, Ordering::Release);
407 log::trace!(
408 "{}: config MSI-X vector update: {old:#x} -> {val:#x}",
409 self.name
410 );
411 } else {
412 log::error!(
413 "{}: cannot change config MSI-X vector from {old:#x} to {val:#x}",
414 self.name
415 )
416 }
417 }
418 VirtioCommonCfg::LAYOUT_DEVICE_STATUS => {
419 let status = DevStatus::from_bits_truncate(val as u8);
420 let old = reg.status.swap(status.bits(), Ordering::AcqRel);
421 let old = DevStatus::from_bits_retain(old);
422 if (old ^ status).contains(DevStatus::DRIVER_OK) {
423 let event = if status.contains(DevStatus::DRIVER_OK) {
424 let mut feature = 0;
425 for (i, v) in reg.driver_feature.iter().enumerate() {
426 feature |= (v.load(Ordering::Acquire) as u128) << (i << 5);
427 }
428 let param = StartParam {
429 feature,
430 irq_sender: self.irq_sender.clone(),
431 ioeventfds: self.ioeventfds.clone(),
432 };
433 WakeEvent::Start { param }
434 } else {
435 self.reset();
436 WakeEvent::Reset
437 };
438 self.wake_up_dev(event);
439 }
440 }
441 VirtioCommonCfg::LAYOUT_QUEUE_SELECT => {
442 reg.queue_sel.store(val as u16, Ordering::Relaxed);
443 if self.queues.get(val as usize).is_none() {
444 log::error!("{}: unknown queue index {val}", self.name)
445 }
446 }
447 VirtioCommonCfg::LAYOUT_QUEUE_SIZE => {
448 let q_sel = reg.queue_sel.load(Ordering::Relaxed) as usize;
449 if let Some(q) = self.queues.get(q_sel) {
450 // TODO: validate queue size
451 q.size.store(val as u16, Ordering::Release);
452 }
453 }
454 VirtioCommonCfg::LAYOUT_QUEUE_MSIX_VECTOR => {
455 let q_sel = reg.queue_sel.load(Ordering::Relaxed) as usize;
456 if let Some(msix_vector) = self.irq_sender.msix_vector.queues.get(q_sel) {
457 let old = msix_vector.load(Ordering::Acquire);
458 if self.msix_change_allowed(old) {
459 msix_vector.store(val as u16, Ordering::Release);
460 log::trace!(
461 "{}: queue {q_sel} MSI-X vector update: {old:#x} -> {val:#x}",
462 self.name
463 );
464 } else {
465 log::error!(
466 "{}: cannot change queue {q_sel} MSI-X vector from {old:#x} to {val:#x}",
467 self.name
468 )
469 }
470 }
471 }
472 VirtioCommonCfg::LAYOUT_QUEUE_ENABLE => {
473 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
474 if let Some(q) = self.queues.get(q_sel as usize) {
475 q.enabled.store(val != 0, Ordering::Release);
476 };
477 }
478 VirtioCommonCfg::LAYOUT_QUEUE_DESC_LO => {
479 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
480 if let Some(q) = self.queues.get(q_sel as usize) {
481 set_atomic_low32(&q.desc, val as u32)
482 }
483 }
484 VirtioCommonCfg::LAYOUT_QUEUE_DESC_HI => {
485 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
486 if let Some(q) = self.queues.get(q_sel as usize) {
487 set_atomic_high32(&q.desc, val as u32)
488 }
489 }
490 VirtioCommonCfg::LAYOUT_QUEUE_DRIVER_LO => {
491 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
492 if let Some(q) = self.queues.get(q_sel as usize) {
493 set_atomic_low32(&q.driver, val as u32)
494 }
495 }
496 VirtioCommonCfg::LAYOUT_QUEUE_DRIVER_HI => {
497 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
498 if let Some(q) = self.queues.get(q_sel as usize) {
499 set_atomic_high32(&q.driver, val as u32)
500 }
501 }
502 VirtioCommonCfg::LAYOUT_QUEUE_DEVICE_LO => {
503 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
504 if let Some(q) = self.queues.get(q_sel as usize) {
505 set_atomic_low32(&q.device, val as u32)
506 }
507 }
508 VirtioCommonCfg::LAYOUT_QUEUE_DEVICE_HI => {
509 let q_sel = reg.queue_sel.load(Ordering::Relaxed);
510 if let Some(q) = self.queues.get(q_sel as usize) {
511 set_atomic_high32(&q.device, val as u32)
512 }
513 }
514 VirtioCommonCfg::LAYOUT_QUEUE_RESET => {
515 todo!()
516 }
517 (offset, _)
518 if offset >= VirtioPciRegister::OFFSET_QUEUE_NOTIFY
519 && offset
520 < VirtioPciRegister::OFFSET_QUEUE_NOTIFY
521 + size_of::<u32>() * self.queues.len() =>
522 {
523 let q_index = (offset - VirtioPciRegister::OFFSET_QUEUE_NOTIFY) as u16 / 4;
524 if self.ioeventfds.is_some() {
525 log::warn!("{}: notifying queue-{q_index} by vm exit!", self.name);
526 }
527 let event = WakeEvent::Notify { q_index };
528 self.wake_up_dev(event)
529 }
530 _ => {
531 log::error!(
532 "{}: write 0x{val:0width$x} to invalid register offset = {offset:#x}",
533 self.name,
534 width = 2 * size as usize
535 );
536 }
537 }
538 Ok(Action::None)
539 }
540}
541
542#[derive(Debug)]
543struct IoeventFdCallback<R>
544where
545 R: IoeventFdRegistry,
546{
547 registry: R,
548 ioeventfds: Arc<[R::IoeventFd]>,
549}
550
551impl<R> MemRegionCallback for IoeventFdCallback<R>
552where
553 R: IoeventFdRegistry,
554{
555 fn mapped(&self, addr: u64) -> mem::Result<()> {
556 for (q_index, fd) in self.ioeventfds.iter().enumerate() {
557 let base_addr = addr + (12 << 10) + VirtioPciRegister::OFFSET_QUEUE_NOTIFY as u64;
558 let notify_addr = base_addr + (q_index * size_of::<u32>()) as u64;
559 self.registry.register(fd, notify_addr, 0, None)?;
560 log::info!("q-{q_index} ioeventfd registered at {notify_addr:x}",)
561 }
562 Ok(())
563 }
564
565 fn unmapped(&self) -> mem::Result<()> {
566 for fd in self.ioeventfds.iter() {
567 self.registry.deregister(fd)?;
568 log::info!("ioeventfd {fd:?} de-registered")
569 }
570 Ok(())
571 }
572}
573
574const VIRTIO_VENDOR_ID: u16 = 0x1af4;
575const VIRTIO_DEVICE_ID_BASE: u16 = 0x1040;
576
577fn get_class(id: DeviceId) -> (u8, u8) {
578 match id {
579 DeviceId::NET => (0x02, 0x00),
580 DeviceId::FILE_SYSTEM => (0x01, 0x80),
581 DeviceId::BLOCK => (0x01, 0x00),
582 DeviceId::SOCKET => (0x02, 0x80),
583 _ => (0xff, 0x00),
584 }
585}
586
587consts! {
588 #[derive(Default, FromZeros, Immutable, IntoBytes)]
589 pub struct VirtioPciCfg(u8) {
590 COMMON = 1;
591 NOTIFY = 2;
592 ISR = 3;
593 DEVICE = 4;
594 PCI = 5;
595 SHARED_MEMORY = 8;
596 VENDOR = 9;
597 }
598}
599
600#[repr(C, align(4))]
601#[derive(Debug, Default, FromZeros, Immutable, IntoBytes)]
602pub struct VirtioPciCap {
603 header: PciCapHdr,
604 cap_len: u8,
605 cfg_type: VirtioPciCfg,
606 bar: u8,
607 id: u8,
608 padding: [u8; 2],
609 offset: u32,
610 length: u32,
611}
612impl_mmio_for_zerocopy!(VirtioPciCap);
613
614impl PciConfigArea for VirtioPciCap {
615 fn reset(&self) -> pci::Result<()> {
616 Ok(())
617 }
618}
619
620impl PciCap for VirtioPciCap {
621 fn set_next(&mut self, val: u8) {
622 self.header.next = val
623 }
624}
625
626#[repr(C, align(4))]
627#[derive(Debug, Default, FromZeros, Immutable, IntoBytes)]
628pub struct VirtioPciCap64 {
629 cap: VirtioPciCap,
630 offset_hi: u32,
631 length_hi: u32,
632}
633impl_mmio_for_zerocopy!(VirtioPciCap64);
634
635impl PciConfigArea for VirtioPciCap64 {
636 fn reset(&self) -> pci::Result<()> {
637 Ok(())
638 }
639}
640
641impl PciCap for VirtioPciCap64 {
642 fn set_next(&mut self, val: u8) {
643 PciCap::set_next(&mut self.cap, val)
644 }
645}
646
647#[repr(C, align(4))]
648#[derive(Debug, Default, FromZeros, Immutable, IntoBytes)]
649pub struct VirtioPciNotifyCap {
650 cap: VirtioPciCap,
651 multiplier: u32,
652}
653impl_mmio_for_zerocopy!(VirtioPciNotifyCap);
654
655impl PciConfigArea for VirtioPciNotifyCap {
656 fn reset(&self) -> pci::Result<()> {
657 Ok(())
658 }
659}
660
661impl PciCap for VirtioPciNotifyCap {
662 fn set_next(&mut self, val: u8) {
663 self.cap.header.next = val;
664 }
665}
666
667#[derive(Debug)]
668pub struct VirtioPciDevice<M, E>
669where
670 M: MsiSender,
671 E: IoeventFd,
672{
673 pub dev: VirtioDevice<PciIrqSender<M>, E>,
674 pub config: EmulatedConfig,
675 pub registers: Arc<VirtioPciRegisterMmio<M, E>>,
676}
677
678impl<M, E> VirtioPciDevice<M, E>
679where
680 M: MsiSender,
681 E: IoeventFd,
682{
683 pub fn new<R>(
684 dev: VirtioDevice<PciIrqSender<M>, E>,
685 msi_sender: M,
686 ioeventfd_reg: R,
687 ) -> Result<Self>
688 where
689 R: IoeventFdRegistry<IoeventFd = E>,
690 {
691 let (class, subclass) = get_class(dev.id);
692 let mut header = DeviceHeader {
693 common: CommonHeader {
694 vendor: VIRTIO_VENDOR_ID,
695 device: VIRTIO_DEVICE_ID_BASE + dev.id.raw(),
696 revision: 0x1,
697 header_type: HeaderType::DEVICE,
698 class,
699 subclass,
700 ..Default::default()
701 },
702 subsystem: VIRTIO_DEVICE_ID_BASE + dev.id.raw(),
703 ..Default::default()
704 };
705 let device_config = dev.device_config.clone();
706 let num_queues = dev.queue_regs.len();
707 let table_entries = num_queues + 1;
708
709 let msix_table_offset = 0;
710 let msix_table_size = size_of::<MsixTableEntry>() * table_entries;
711
712 let msix_pba_offset = 8 << 10;
713
714 let virtio_register_offset = 12 << 10;
715 let device_config_offset =
716 virtio_register_offset + size_of::<VirtioPciRegister>() + size_of::<u32>() * num_queues;
717
718 let msix_msg_ctrl = MsixMsgCtrl::new(table_entries as u16);
719
720 let cap_msix = MsixCap {
721 header: PciCapHdr {
722 id: PciCapId::MSIX,
723 ..Default::default()
724 },
725 control: msix_msg_ctrl,
726 table_offset: MsixCapOffset::new(msix_table_offset as u32, 0),
727 pba_offset: MsixCapOffset::new(msix_pba_offset as u32, 0),
728 };
729 let cap_common = VirtioPciCap {
730 header: PciCapHdr {
731 id: PciCapId::VENDOR,
732 ..Default::default()
733 },
734 cap_len: size_of::<VirtioPciCap>() as u8,
735 cfg_type: VirtioPciCfg::COMMON,
736 bar: 0,
737 id: 0,
738 offset: (virtio_register_offset + VirtioPciRegister::OFFSET_COMMON) as u32,
739 length: size_of::<VirtioCommonCfg>() as u32,
740 ..Default::default()
741 };
742 let cap_isr = VirtioPciCap {
743 header: PciCapHdr {
744 id: PciCapId::VENDOR,
745 ..Default::default()
746 },
747 cap_len: size_of::<VirtioPciCap>() as u8,
748 cfg_type: VirtioPciCfg::ISR,
749 bar: 0,
750 id: 0,
751 offset: (virtio_register_offset + VirtioPciRegister::OFFSET_ISR_STATUS) as u32,
752 length: size_of::<u32>() as u32,
753 ..Default::default()
754 };
755 let cap_notify = VirtioPciNotifyCap {
756 cap: VirtioPciCap {
757 header: PciCapHdr {
758 id: PciCapId::VENDOR,
759 ..Default::default()
760 },
761 cap_len: size_of::<VirtioPciNotifyCap>() as u8,
762 cfg_type: VirtioPciCfg::NOTIFY,
763 bar: 0,
764 id: 0,
765 offset: (virtio_register_offset + VirtioPciRegister::OFFSET_QUEUE_NOTIFY) as u32,
766 length: (size_of::<u32>() * num_queues) as u32,
767 ..Default::default()
768 },
769 multiplier: size_of::<u32>() as u32,
770 };
771 let cap_device_config = VirtioPciCap {
772 header: PciCapHdr {
773 id: PciCapId::VENDOR,
774 ..Default::default()
775 },
776 cap_len: size_of::<VirtioPciCap>() as u8,
777 cfg_type: VirtioPciCfg::DEVICE,
778 bar: 0,
779 id: 0,
780 offset: device_config_offset as u32,
781 length: device_config.size() as u32,
782 ..Default::default()
783 };
784 let entries = RwLock::new(
785 (0..table_entries)
786 .map(|_| MsixTableMmioEntry::Entry(MsixTableEntry::default()))
787 .collect(),
788 );
789 let msix_table = Arc::new(MsixTableMmio { entries });
790 let bar0_size = 16 << 10;
791 let mut bar0 = MemRegion {
792 ranges: vec![],
793 entries: vec![MemRegionEntry {
794 size: bar0_size,
795 type_: mem::MemRegionType::Hidden,
796 }],
797 callbacks: Mutex::new(vec![]),
798 };
799
800 let mut caps: Vec<Box<dyn PciCap>> = vec![
801 Box::new(MsixCapMmio::new(cap_msix)),
802 Box::new(cap_common),
803 Box::new(cap_isr),
804 Box::new(cap_notify),
805 ];
806 if device_config.size() > 0 {
807 caps.push(Box::new(cap_device_config));
808 }
809 if let Some(region) = &dev.shared_mem_regions {
810 let mut offset = 0;
811 for (index, entry) in region.entries.iter().enumerate() {
812 let share_mem_cap = VirtioPciCap64 {
813 cap: VirtioPciCap {
814 header: PciCapHdr {
815 id: PciCapId::VENDOR,
816 ..Default::default()
817 },
818 cap_len: size_of::<VirtioPciCap64>() as u8,
819 cfg_type: VirtioPciCfg::SHARED_MEMORY,
820 bar: 2,
821 id: index as u8,
822 offset: offset as u32,
823 length: entry.size as u32,
824 ..Default::default()
825 },
826 length_hi: (entry.size >> 32) as u32,
827 offset_hi: (offset >> 32) as u32,
828 };
829 caps.push(Box::new(share_mem_cap));
830 offset += entry.size;
831 }
832 }
833
834 let cap_list = PciCapList::try_from(caps)?;
835
836 let msix_vector = VirtioPciMsixVector {
837 config: AtomicU16::new(VIRTIO_MSI_NO_VECTOR),
838 queues: (0..num_queues)
839 .map(|_| AtomicU16::new(VIRTIO_MSI_NO_VECTOR))
840 .collect(),
841 };
842
843 let maybe_ioeventfds = (0..num_queues)
844 .map(|_| ioeventfd_reg.create())
845 .collect::<Result<Arc<_>, _>>();
846 let ioeventfds = match maybe_ioeventfds {
847 Ok(fds) => Some(fds),
848 Err(hv::Error::IoeventFd { error, .. }) if error.kind() == ErrorKind::Unsupported => {
849 None
850 }
851 Err(e) => {
852 log::warn!("{}: failed to create ioeventfds: {e:?}", dev.name);
853 None
854 }
855 };
856
857 let mut device_feature = [0u32; 4];
858 for (i, v) in device_feature.iter_mut().enumerate() {
859 *v = (dev.device_feature >> (i << 5)) as u32;
860 }
861 let registers = Arc::new(VirtioPciRegisterMmio {
862 name: dev.name.clone(),
863 reg: Register {
864 device_feature,
865 ..Default::default()
866 },
867 event_tx: dev.event_tx.clone(),
868 notifier: dev.notifier.clone(),
869 queues: dev.queue_regs.clone(),
870 irq_sender: Arc::new(PciIrqSender {
871 msix_vector,
872 msix_table: msix_table.clone(),
873 msi_sender,
874 }),
875 ioeventfds: ioeventfds.clone(),
876 });
877 bar0.ranges.push(MemRange::Emulated(msix_table));
878 bar0.ranges
879 .push(MemRange::Span((12 << 10) - msix_table_size as u64));
880 bar0.ranges.push(MemRange::Emulated(registers.clone()));
881 if let Some(ioeventfds) = ioeventfds {
882 bar0.callbacks.lock().push(Box::new(IoeventFdCallback {
883 registry: ioeventfd_reg,
884 ioeventfds,
885 }));
886 }
887 if device_config.size() > 0 {
888 bar0.ranges.push(MemRange::Emulated(device_config))
889 }
890 let mut bars = [const { PciBar::Empty }; 6];
891
892 bars[0] = PciBar::Mem(Arc::new(bar0));
893 header.bars[0] = BAR_MEM32;
894
895 if let Some(region) = &dev.shared_mem_regions {
896 let region_size = region.size();
897
898 let mut not_emulated = |r| !matches!(r, &MemRange::Emulated(_));
899 let prefetchable = region.ranges.iter().all(&mut not_emulated);
900 if prefetchable {
901 bars[2] = PciBar::Mem(region.clone());
902 header.bars[2] = BAR_MEM64 | BAR_PREFETCHABLE;
903 } else {
904 assert!(region_size <= u32::MAX as u64);
905 bars[2] = PciBar::Mem(region.clone());
906 header.bars[2] = BAR_MEM32;
907 }
908 }
909
910 let config = EmulatedConfig::new_device(header, bars, cap_list);
911
912 Ok(VirtioPciDevice {
913 dev,
914 config,
915 registers,
916 })
917 }
918}
919
920impl<M, E> Pause for VirtioPciDevice<M, E>
921where
922 M: MsiSender,
923 E: IoeventFd,
924{
925}
926
927impl<M, E> Pci for VirtioPciDevice<M, E>
928where
929 M: MsiSender,
930 E: IoeventFd,
931{
932 fn name(&self) -> &str {
933 &self.dev.name
934 }
935
936 fn config(&self) -> &dyn PciConfig {
937 &self.config
938 }
939
940 fn reset(&self) -> pci::Result<()> {
941 self.registers.wake_up_dev(WakeEvent::Reset);
942 self.registers.reset();
943 self.registers.reg.status.store(0, Ordering::Release);
944 Ok(())
945 }
946}
947