Alioth Code Coverage

cap.rs98.38%

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::cmp::min;
16use std::fmt::Debug;
17use std::mem::size_of;
18
19use alioth_macros::Layout;
20use bitfield::bitfield;
21use parking_lot::RwLock;
22use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
23
24use crate::errors::BoxTrace;
25use crate::hv::{self, IrqFd};
26use crate::mem::addressable::SlotBackend;
27use crate::mem::emulated::{Action, Mmio, MmioBus};
28use crate::pci::config::{DeviceHeader, PciConfigArea};
29use crate::pci::{self, Error, Result};
30use crate::utils::truncate_u64;
31use crate::{align_up, consts, impl_mmio_for_zerocopy, mask_bits, mem};
32
33consts! {
34 #[derive(Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
35 pub struct PciCapId(u8) {
36 MSI = 0x05;
37 VENDOR = 0x09;
38 MSIX = 0x11;
39 }
40}
41
42#[repr(C)]
43#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, KnownLayout, Layout)]
44pub struct PciCapHdr {
45 pub id: PciCapId,
46 pub next: u8,
47}
48
49bitfield! {
50 #[derive(Copy, Clone, Default)]
51 #[repr(C)]
52 pub struct PcieExtCapHdr(u32);
53 impl Debug;
54 pub next, _: 31,20;
55 pub version, _: 19,16;
56 pub id, _: 15,0;
57}
58
59bitfield! {
60 #[derive(Copy, Clone, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
61 #[repr(C)]
62 pub struct MsiMsgCtrl(u16);
63 impl Debug;
64 pub enable, set_enable: 0;
65 pub multi_msg_cap, set_multi_msg_cap: 3, 1;
66 pub multi_msg, set_multi_msg: 6, 4;
67 pub addr_64_cap, set_addr_64_cap: 7;
68 pub per_vector_masking_cap, set_per_vector_masking_cap: 8;
69 pub ext_msg_data_cap, set_ext_msg_data_cap: 9;
70 pub ext_msg_data, set_ext_msg_data: 10;
71}
72
73impl MsiMsgCtrl {
74 pub fn cap_size(&self) -> u8 {21x
75 let mut size = 12;21x
76 if self.addr_64_cap() {21x
77 size += 4;9x
78 }12x
79 if self.per_vector_masking_cap() {21x
80 size += 8;12x
81 }12x
82 size21x
83 }21x
84}
85
86#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, Layout)]
87#[repr(C)]
88pub struct MsiCapHdr {
89 pub header: PciCapHdr,
90 pub control: MsiMsgCtrl,
91}
92impl_mmio_for_zerocopy!(MsiCapHdr);
93
94#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, Layout)]
95#[repr(C)]
96struct MsiCapBody {
97 data: [u32; 4],
98}
99impl_mmio_for_zerocopy!(MsiCapBody);
100
101#[derive(Debug)]
102pub struct MsiCapMmio<F>
103where
104 F: IrqFd,
105{
106 cap: RwLock<(MsiCapHdr, MsiCapBody)>,
107 irqfds: Box<[F]>,
108}
109
110impl<F> MsiCapMmio<F>
111where
112 F: IrqFd,
113{
114 pub fn new(ctrl: MsiMsgCtrl, irqfds: Box<[F]>) -> Self {9x
115 debug_assert_eq!(irqfds.len(), 1 << ctrl.multi_msg_cap());9x
116 let cap = RwLock::new((9x
117 MsiCapHdr {9x
118 header: PciCapHdr {9x
119 id: PciCapId::MSI,9x
120 next: 0,9x
121 },9x
122 control: ctrl,9x
123 },9x
124 MsiCapBody::default(),9x
125 ));9x
126 Self { cap, irqfds }9x
127 }9x
128
129 fn update_msi(&self) -> hv::Result<()> {30x
130 let (hdr, body) = &*self.cap.read();30x
131 let ctrl = &hdr.control;30x
132 let data = &body.data;30x
133 let msg_mask = if ctrl.ext_msg_data() {30x
134 0xffff_ffff12x
135 } else {
136 0xffff18x
137 };
138 let (addr, msg) = if ctrl.addr_64_cap() {30x
139 (12x
140 ((data[1] as u64) << 32) | data[0] as u64,12x
141 data[2] & msg_mask,12x
142 )12x
143 } else {
144 (data[0] as u64, data[1] & msg_mask)18x
145 };
146 let mask = match (ctrl.addr_64_cap(), ctrl.per_vector_masking_cap()) {30x
147 (true, true) => data[3],12x
148 (false, true) => data[2],9x
149 (_, false) => 0,9x
150 };
151 let count = 1 << ctrl.multi_msg();30x
152 for (index, irqfd) in self.irqfds.iter().enumerate() {408x
153 irqfd.set_masked(true)?;408x
154 if !ctrl.enable() || index >= count || mask & (1 << index) > 0 {408x
155 continue;222x
156 }186x
157 let msg = msg | index as u32;186x
158 irqfd.set_addr_hi((addr >> 32) as u32)?;186x
159 irqfd.set_addr_lo(addr as u32)?;186x
160 irqfd.set_data(msg)?;186x
161 irqfd.set_masked(false)?;186x
162 }
163 Ok(())30x
164 }30x
165}
166
167impl<F> Mmio for MsiCapMmio<F>
168where
169 F: IrqFd,
170{
171 fn size(&self) -> u64 {9x
172 let (hdr, _) = &*self.cap.read();9x
173 hdr.control.cap_size() as u649x
174 }9x
175
176 fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {27x
177 let (hdr, body) = &*self.cap.read();27x
178 if offset < 4 {27x
179 hdr.read(offset, size)15x
180 } else {
181 body.read(offset - size_of_val(hdr) as u64, size)12x
182 }
183 }27x
184
185 fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {42x
186 let mut need_update = false;42x
187 let mut cap = self.cap.write();42x
188 let (hdr, body) = &mut *cap;42x
189 let ctrl = &mut hdr.control;42x
190 let addr_64_cap = ctrl.addr_64_cap();42x
191 let per_vector_masking_cap = ctrl.per_vector_masking_cap();42x
192 match (offset, size, addr_64_cap, per_vector_masking_cap) {42x
193 (0x2, 2, _, _) | (0x0, 4, _, _) => {
194 let new_ctrl = MsiMsgCtrl((val >> ((2 - offset) << 3)) as u16);12x
195
196 if !ctrl.enable() || !new_ctrl.enable() {12x
197 let multi_msg = min(ctrl.multi_msg_cap(), new_ctrl.multi_msg());12x
198 ctrl.set_multi_msg(multi_msg);12x
199 }12x
200
201 let ext_msg_data = ctrl.ext_msg_data_cap() && new_ctrl.ext_msg_data();12x
202 need_update |= new_ctrl.enable() && ctrl.ext_msg_data() != ext_msg_data;12x
203 ctrl.set_ext_msg_data(ext_msg_data);12x
204
205 need_update |= ctrl.enable() != new_ctrl.enable();12x
206 ctrl.set_enable(new_ctrl.enable());12x
207 }
208 (0x4, 4, _, _) | (0x8, 4, true, _) | (0xc, 4, false, true) | (0x10, 4, true, true) => {
209 let data_offset = (offset as usize - size_of::<MsiCapHdr>()) >> 2;18x
210 let reg = &mut body.data[data_offset];18x
211 need_update = hdr.control.enable() && *reg != val as u32;18x
212 *reg = val as u32;18x
213 }
214 (0x8, 2 | 4, false, _) | (0xc, 2 | 4, true, _) => {
215 let data_offset = (offset as usize - size_of::<MsiCapHdr>()) >> 2;9x
216 let reg = &mut body.data[data_offset];9x
217 let mask = if size == 4 && hdr.control.ext_msg_data_cap() {9x
218 0xffff_ffff6x
219 } else {
220 0xffff3x
221 };
222 let new_val = mask_bits!(*reg, val as u32, mask);9x
223 need_update = hdr.control.enable() && *reg != new_val;9x
224 *reg = new_val;9x
225 }
226 _ => log::error!(3x
227 "MsiCapMmio: write 0x{val:0width$x} to invalid offset 0x{offset:x}.",
228 width = 2 * size as usize3x
229 ),
230 }
231 drop(cap);42x
232 if need_update {42x
233 self.update_msi().box_trace(mem::error::Mmio)?;27x
234 }15x
235 Ok(Action::None)42x
236 }42x
237}
238
239impl<F> PciCap for MsiCapMmio<F>
240where
241 F: IrqFd,
242{
243 fn set_next(&mut self, val: u8) {3x
244 let (hdr, _) = self.cap.get_mut();3x
245 hdr.header.next = val;3x
246 }3x
247}
248
249impl<F> PciConfigArea for MsiCapMmio<F>
250where
251 F: IrqFd,
252{
253 fn reset(&self) -> pci::Result<()> {3x
254 {3x
255 let (hdr, _) = &mut *self.cap.write();3x
256 hdr.control.set_enable(false);3x
257 }3x
258 self.update_msi().box_trace(pci::error::Reset)3x
259 }3x
260}
261
262bitfield! {
263 #[derive(Copy, Clone, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
264 #[repr(C)]
265 pub struct MsixMsgCtrl(u16);
266 impl Debug;
267 pub table_len, _ : 10, 0;
268 pub masked, set_masked: 14;
269 pub enabled, set_enabled: 15;
270}
271
272impl MsixMsgCtrl {
273 pub fn new(len: u16) -> Self {9x
274 MsixMsgCtrl(len - 1)9x
275 }9x
276}
277
278bitfield! {
279 #[derive(Copy, Clone, Default, FromBytes, Immutable, IntoBytes)]
280 #[repr(C)]
281 pub struct MsixCapOffset(u32);
282 impl Debug;
283 pub bar, _: 2, 0;
284}
285
286impl MsixCapOffset {
287 pub fn new(offset: u32, bar: u8) -> Self {6x
288 MsixCapOffset(mask_bits!(offset, bar as u32, 0b111))6x
289 }6x
290
291 pub fn offset(&self) -> u32 {6x
292 self.0 & !0b1116x
293 }6x
294}
295
296#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, Layout)]
297#[repr(C)]
298pub struct MsixCap {
299 pub header: PciCapHdr,
300 pub control: MsixMsgCtrl,
301 pub table_offset: MsixCapOffset,
302 pub pba_offset: MsixCapOffset,
303}
304impl_mmio_for_zerocopy!(MsixCap);
305
306bitfield! {
307 #[derive(Copy, Clone, Default)]
308 #[repr(C)]
309 pub struct MsixVectorCtrl(u32);
310 impl Debug;
311 pub masked, set_masked: 0;
312}
313
314#[derive(Debug, Clone)]
315pub struct MsixTableEntry {
316 pub addr_lo: u32,
317 pub addr_hi: u32,
318 pub data: u32,
319 pub control: MsixVectorCtrl,
320}
321
322impl Default for MsixTableEntry {
323 fn default() -> Self {9x
324 MsixTableEntry {9x
325 addr_lo: 0,9x
326 addr_hi: 0,9x
327 data: 0,9x
328 control: MsixVectorCtrl(1),9x
329 }9x
330 }9x
331}
332
333pub trait PciCap: PciConfigArea {
334 fn set_next(&mut self, val: u8);
335}
336
337impl SlotBackend for Box<dyn PciCap> {
338 fn size(&self) -> u64 {60x
339 Mmio::size(self.as_ref())60x
340 }60x
341}
342
343impl Mmio for Box<dyn PciCap> {
344 fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {36x
345 Mmio::read(self.as_ref(), offset, size)36x
346 }36x
347
348 fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {6x
349 Mmio::write(self.as_ref(), offset, size, val)6x
350 }6x
351
352 fn size(&self) -> u64 {48x
353 Mmio::size(self.as_ref())48x
354 }48x
355}
356
357#[derive(Debug)]
358pub struct PciCapList {
359 inner: MmioBus<Box<dyn PciCap>>,
360}
361
362impl Default for PciCapList {
363 fn default() -> Self {3x
364 Self::new()3x
365 }3x
366}
367
368impl PciCapList {
369 pub fn new() -> PciCapList {83x
370 Self {83x
371 inner: MmioBus::new(),83x
372 }83x
373 }83x
374
375 pub fn is_empty(&self) -> bool {89x
376 self.inner.is_empty()89x
377 }89x
378}
379
380impl PciConfigArea for PciCapList {
381 fn reset(&self) -> Result<()> {6x
382 for (_, cap) in self.inner.inner.iter() {12x
383 cap.reset()?;12x
384 }
385 Ok(())6x
386 }6x
387}
388
389impl Mmio for PciCapList {
390 fn read(&self, offset: u64, size: u8) -> Result<u64, mem::Error> {36x
391 self.inner.read(offset, size)36x
392 }36x
393
394 fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {6x
395 self.inner.write(offset, size, val)6x
396 }6x
397
398 fn size(&self) -> u64 {3x
399 40963x
400 }3x
401}
402
403impl TryFrom<Vec<Box<dyn PciCap>>> for PciCapList {
404 type Error = Error;
405
406 fn try_from(caps: Vec<Box<dyn PciCap>>) -> Result<Self, Self::Error> {6x
407 let mut bus = MmioBus::new();6x
408 let mut ptr = size_of::<DeviceHeader>() as u64;6x
409 let num_caps = caps.len();6x
410 for (index, mut cap) in caps.into_iter().enumerate() {12x
411 let next = if index == num_caps - 1 {12x
412 06x
413 } else {
414 align_up!(ptr + Mmio::size(&cap), 2)6x
415 };
416 cap.set_next(next as u8);12x
417 bus.add(ptr, cap)?;12x
418 ptr = next;12x
419 }
420 Ok(Self { inner: bus })6x
421 }6x
422}
423
424#[derive(Debug)]
425pub struct MsixCapMmio {
426 cap: RwLock<MsixCap>,
427}
428
429impl MsixCapMmio {
430 pub fn new(cap: MsixCap) -> Self {6x
431 Self {6x
432 cap: RwLock::new(cap),6x
433 }6x
434 }6x
435}
436
437impl Mmio for MsixCapMmio {
438 fn size(&self) -> u64 {81x
439 size_of::<MsixCap>() as u6481x
440 }81x
441
442 fn read(&self, offset: u64, size: u8) -> Result<u64, mem::Error> {42x
443 let cap = self.cap.read();42x
444 Mmio::read(&*cap, offset, size)42x
445 }42x
446
447 fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {15x
448 if offset == 2 && size == 2 {15x
449 let mut cap = self.cap.write();9x
450 let control = MsixMsgCtrl(val as u16);9x
451 cap.control.set_enabled(control.enabled());9x
452 cap.control.set_masked(control.masked());9x
453 }9x
454 Ok(Action::None)15x
455 }15x
456}
457
458impl PciConfigArea for MsixCapMmio {
459 fn reset(&self) -> pci::Result<()> {9x
460 let mut cap = self.cap.write();9x
461 cap.control.set_enabled(false);9x
462 cap.control.set_masked(false);9x
463 Ok(())9x
464 }9x
465}
466
467impl PciCap for MsixCapMmio {
468 fn set_next(&mut self, val: u8) {9x
469 self.cap.write().header.next = val;9x
470 }9x
471}
472
473#[derive(Debug)]
474pub enum MsixTableMmioEntry<F> {
475 Entry(MsixTableEntry),
476 IrqFd(F),
477}
478
479impl<F> Default for MsixTableMmioEntry<F> {
480 fn default() -> Self {9x
481 MsixTableMmioEntry::Entry(MsixTableEntry::default())9x
482 }9x
483}
484
485macro_rules! impl_msix_table_mmio_entry_method {
486 ($field:ident, $get:ident, $set:ident) => {
487 pub fn $get(&self) -> u32 {18x
488 match self {18x
489 MsixTableMmioEntry::Entry(e) => e.$field,9x
490 MsixTableMmioEntry::IrqFd(f) => f.$get(),9x
491 }
492 }18x
493 fn $set(&mut self, val: u32) -> mem::Result<()> {18x
494 match self {18x
495 MsixTableMmioEntry::Entry(e) => e.$field = val,9x
496 MsixTableMmioEntry::IrqFd(f) => f.$set(val)?,9x
497 }
498 Ok(())18x
499 }18x
500 };
501}
502
503impl<F> MsixTableMmioEntry<F>
504where
505 F: IrqFd,
506{
507 impl_msix_table_mmio_entry_method!(addr_lo, get_addr_lo, set_addr_lo);
508
509 impl_msix_table_mmio_entry_method!(addr_hi, get_addr_hi, set_addr_hi);
510
511 impl_msix_table_mmio_entry_method!(data, get_data, set_data);
512
513 fn set_masked(&mut self, val: bool) -> mem::Result<bool> {6x
514 match self {6x
515 MsixTableMmioEntry::Entry(e) => {3x
516 let masked = e.control.masked();3x
517 e.control.set_masked(val);3x
518 Ok(masked != val)3x
519 }
520 MsixTableMmioEntry::IrqFd(f) => {3x
521 let changed = f.set_masked(val)?;3x
522 Ok(changed)3x
523 }
524 }
525 }6x
526
527 pub fn get_masked(&self) -> bool {12x
528 match self {12x
529 MsixTableMmioEntry::Entry(e) => e.control.masked(),6x
530 MsixTableMmioEntry::IrqFd(f) => f.get_masked(),6x
531 }
532 }12x
533}
534
535#[derive(Debug)]
536pub struct MsixTableMmio<F> {
537 pub entries: RwLock<Box<[MsixTableMmioEntry<F>]>>,
538}
539
540impl<F> MsixTableMmio<F>
541where
542 F: IrqFd,
543{
544 /// Write `val` to `offset`.
545 ///
546 /// Returns `true` if a `masked` bit gets flipped.
547 pub fn write_val(&self, offset: u64, size: u8, val: u64) -> mem::Result<bool> {33x
548 if size != 4 || offset & 0b11 != 0 {33x
549 log::error!("unaligned access to msix table: size = {size}, offset = {offset:#x}");6x
550 return Ok(false);6x
551 }27x
552 let val = val as u32;27x
553 let index = offset as usize / size_of::<MsixTableEntry>();27x
554 let mut entries = self.entries.write();27x
555 let Some(entry) = entries.get_mut(index) else {27x
556 log::error!(3x
557 "MSI-X table size: {}, accessing index {index}",
558 entries.len()3x
559 );
560 return Ok(false);3x
561 };
562 let mut state_changed = false;24x
563 match offset as usize % size_of::<MsixTableEntry>() {24x
564 0 => entry.set_addr_lo(val)?,6x
565 4 => entry.set_addr_hi(val)?,6x
566 8 => entry.set_data(val)?,6x
567 12 => state_changed = entry.set_masked(MsixVectorCtrl(val).masked())?,6x
568 _ => unreachable!(),
569 };
570 Ok(state_changed)24x
571 }33x
572
573 pub fn reset(&self) {3x
574 let mut entries = self.entries.write();3x
575 for entry in entries.iter_mut() {6x
576 *entry = MsixTableMmioEntry::default();6x
577 }6x
578 }3x
579}
580
581impl<F> Mmio for MsixTableMmio<F>
582where
583 F: IrqFd,
584{
585 fn size(&self) -> u64 {3x
586 (size_of::<MsixTableEntry>() * self.entries.read().len()) as u643x
587 }3x
588
589 fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {39x
590 if size != 4 || offset & 0b11 != 0 {39x
591 log::error!("unaligned access to msix table: size = {size}, offset = {offset:#x}");6x
592 return Ok(0);6x
593 }33x
594 let index = offset as usize / size_of::<MsixTableEntry>();33x
595 let entries = self.entries.read();33x
596 let Some(entry) = entries.get(index) else {33x
597 log::error!(3x
598 "MSI-X table size: {}, accessing index {index}",
599 entries.len()3x
600 );
601 return Ok(0);3x
602 };
603 let ret = match offset as usize % size_of::<MsixTableEntry>() {30x
604 0 => entry.get_addr_lo(),6x
605 4 => entry.get_addr_hi(),6x
606 8 => entry.get_data(),6x
607 12 => entry.get_masked() as _,12x
608 _ => unreachable!(),
609 };
610 Ok(ret as u64)30x
611 }39x
612
613 fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {27x
614 self.write_val(offset, size, val)?;27x
615 Ok(Action::None)27x
616 }27x
617}
618
619#[repr(C)]
620#[derive(Debug, Default, Clone)]
621pub struct NullCap {
622 pub next: u8,
623 pub size: u8,
624}
625
626impl Mmio for NullCap {
627 fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {39x
628 let shift = std::cmp::min(63, offset << 3);39x
629 let val = ((self.next as u64) << 8) >> shift;39x
630 Ok(truncate_u64(val, size as u64))39x
631 }39x
632
633 fn write(&self, _offset: u64, _size: u8, _val: u64) -> mem::Result<Action> {
634 Ok(Action::None)
635 }
636
637 fn size(&self) -> u64 {30x
638 self.size as u6430x
639 }30x
640}
641
642impl PciCap for NullCap {
643 fn set_next(&mut self, val: u8) {6x
644 self.next = val;6x
645 }6x
646}
647
648impl PciConfigArea for NullCap {
649 fn reset(&self) -> Result<()> {6x
650 Ok(())6x
651 }6x
652}
653
654#[cfg(test)]
655#[path = "cap_test.rs"]
656mod tests;
657