cap.rs98.38%
1
// Copyright 2024 Google LLC2
//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 at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// 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 and13
// limitations under the License.14
15
use std::cmp::min;16
use std::fmt::Debug;17
use std::mem::size_of;18
19
use alioth_macros::Layout;20
use bitfield::bitfield;21
use parking_lot::RwLock;22
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};23
24
use crate::errors::BoxTrace;25
use crate::hv::{self, IrqFd};26
use crate::mem::addressable::SlotBackend;27
use crate::mem::emulated::{Action, Mmio, MmioBus};28
use crate::pci::config::{DeviceHeader, PciConfigArea};29
use crate::pci::{self, Error, Result};30
use crate::utils::truncate_u64;31
use crate::{align_up, consts, impl_mmio_for_zerocopy, mask_bits, mem};32
33
consts! {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)]44
pub struct PciCapHdr {45
pub id: PciCapId,46
pub next: u8,47
}48
49
bitfield! {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
59
bitfield! {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
73
impl MsiMsgCtrl {74
pub fn cap_size(&self) -> u8 {21x75
let mut size = 12;21x76
if self.addr_64_cap() {21x77
size += 4;9x78
}12x79
if self.per_vector_masking_cap() {21x80
size += 8;12x81
}12x82
size21x83
}21x84
}85
86
#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, Layout)]87
#[repr(C)]88
pub struct MsiCapHdr {89
pub header: PciCapHdr,90
pub control: MsiMsgCtrl,91
}92
impl_mmio_for_zerocopy!(MsiCapHdr);93
94
#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, Layout)]95
#[repr(C)]96
struct MsiCapBody {97
data: [u32; 4],98
}99
impl_mmio_for_zerocopy!(MsiCapBody);100
101
#[derive(Debug)]102
pub struct MsiCapMmio<F>103
where104
F: IrqFd,105
{106
cap: RwLock<(MsiCapHdr, MsiCapBody)>,107
irqfds: Box<[F]>,108
}109
110
impl<F> MsiCapMmio<F>111
where112
F: IrqFd,113
{114
pub fn new(ctrl: MsiMsgCtrl, irqfds: Box<[F]>) -> Self {9x115
debug_assert_eq!(irqfds.len(), 1 << ctrl.multi_msg_cap());9x116
let cap = RwLock::new((9x117
MsiCapHdr {9x118
header: PciCapHdr {9x119
id: PciCapId::MSI,9x120
next: 0,9x121
},9x122
control: ctrl,9x123
},9x124
MsiCapBody::default(),9x125
));9x126
Self { cap, irqfds }9x127
}9x128
129
fn update_msi(&self) -> hv::Result<()> {30x130
let (hdr, body) = &*self.cap.read();30x131
let ctrl = &hdr.control;30x132
let data = &body.data;30x133
let msg_mask = if ctrl.ext_msg_data() {30x134
0xffff_ffff12x135
} else {136
0xffff18x137
};138
let (addr, msg) = if ctrl.addr_64_cap() {30x139
(12x140
((data[1] as u64) << 32) | data[0] as u64,12x141
data[2] & msg_mask,12x142
)12x143
} else {144
(data[0] as u64, data[1] & msg_mask)18x145
};146
let mask = match (ctrl.addr_64_cap(), ctrl.per_vector_masking_cap()) {30x147
(true, true) => data[3],12x148
(false, true) => data[2],9x149
(_, false) => 0,9x150
};151
let count = 1 << ctrl.multi_msg();30x152
for (index, irqfd) in self.irqfds.iter().enumerate() {408x153
irqfd.set_masked(true)?;408x154
if !ctrl.enable() || index >= count || mask & (1 << index) > 0 {408x155
continue;222x156
}186x157
let msg = msg | index as u32;186x158
irqfd.set_addr_hi((addr >> 32) as u32)?;186x159
irqfd.set_addr_lo(addr as u32)?;186x160
irqfd.set_data(msg)?;186x161
irqfd.set_masked(false)?;186x162
}163
Ok(())30x164
}30x165
}166
167
impl<F> Mmio for MsiCapMmio<F>168
where169
F: IrqFd,170
{171
fn size(&self) -> u64 {9x172
let (hdr, _) = &*self.cap.read();9x173
hdr.control.cap_size() as u649x174
}9x175
176
fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {27x177
let (hdr, body) = &*self.cap.read();27x178
if offset < 4 {27x179
hdr.read(offset, size)15x180
} else {181
body.read(offset - size_of_val(hdr) as u64, size)12x182
}183
}27x184
185
fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {42x186
let mut need_update = false;42x187
let mut cap = self.cap.write();42x188
let (hdr, body) = &mut *cap;42x189
let ctrl = &mut hdr.control;42x190
let addr_64_cap = ctrl.addr_64_cap();42x191
let per_vector_masking_cap = ctrl.per_vector_masking_cap();42x192
match (offset, size, addr_64_cap, per_vector_masking_cap) {42x193
(0x2, 2, _, _) | (0x0, 4, _, _) => {194
let new_ctrl = MsiMsgCtrl((val >> ((2 - offset) << 3)) as u16);12x195
196
if !ctrl.enable() || !new_ctrl.enable() {12x197
let multi_msg = min(ctrl.multi_msg_cap(), new_ctrl.multi_msg());12x198
ctrl.set_multi_msg(multi_msg);12x199
}12x200
201
let ext_msg_data = ctrl.ext_msg_data_cap() && new_ctrl.ext_msg_data();12x202
need_update |= new_ctrl.enable() && ctrl.ext_msg_data() != ext_msg_data;12x203
ctrl.set_ext_msg_data(ext_msg_data);12x204
205
need_update |= ctrl.enable() != new_ctrl.enable();12x206
ctrl.set_enable(new_ctrl.enable());12x207
}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;18x210
let reg = &mut body.data[data_offset];18x211
need_update = hdr.control.enable() && *reg != val as u32;18x212
*reg = val as u32;18x213
}214
(0x8, 2 | 4, false, _) | (0xc, 2 | 4, true, _) => {215
let data_offset = (offset as usize - size_of::<MsiCapHdr>()) >> 2;9x216
let reg = &mut body.data[data_offset];9x217
let mask = if size == 4 && hdr.control.ext_msg_data_cap() {9x218
0xffff_ffff6x219
} else {220
0xffff3x221
};222
let new_val = mask_bits!(*reg, val as u32, mask);9x223
need_update = hdr.control.enable() && *reg != new_val;9x224
*reg = new_val;9x225
}226
_ => log::error!(3x227
"MsiCapMmio: write 0x{val:0width$x} to invalid offset 0x{offset:x}.",228
width = 2 * size as usize3x229
),230
}231
drop(cap);42x232
if need_update {42x233
self.update_msi().box_trace(mem::error::Mmio)?;27x234
}15x235
Ok(Action::None)42x236
}42x237
}238
239
impl<F> PciCap for MsiCapMmio<F>240
where241
F: IrqFd,242
{243
fn set_next(&mut self, val: u8) {3x244
let (hdr, _) = self.cap.get_mut();3x245
hdr.header.next = val;3x246
}3x247
}248
249
impl<F> PciConfigArea for MsiCapMmio<F>250
where251
F: IrqFd,252
{253
fn reset(&self) -> pci::Result<()> {3x254
{3x255
let (hdr, _) = &mut *self.cap.write();3x256
hdr.control.set_enable(false);3x257
}3x258
self.update_msi().box_trace(pci::error::Reset)3x259
}3x260
}261
262
bitfield! {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
272
impl MsixMsgCtrl {273
pub fn new(len: u16) -> Self {9x274
MsixMsgCtrl(len - 1)9x275
}9x276
}277
278
bitfield! {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
286
impl MsixCapOffset {287
pub fn new(offset: u32, bar: u8) -> Self {6x288
MsixCapOffset(mask_bits!(offset, bar as u32, 0b111))6x289
}6x290
291
pub fn offset(&self) -> u32 {6x292
self.0 & !0b1116x293
}6x294
}295
296
#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, Layout)]297
#[repr(C)]298
pub struct MsixCap {299
pub header: PciCapHdr,300
pub control: MsixMsgCtrl,301
pub table_offset: MsixCapOffset,302
pub pba_offset: MsixCapOffset,303
}304
impl_mmio_for_zerocopy!(MsixCap);305
306
bitfield! {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)]315
pub struct MsixTableEntry {316
pub addr_lo: u32,317
pub addr_hi: u32,318
pub data: u32,319
pub control: MsixVectorCtrl,320
}321
322
impl Default for MsixTableEntry {323
fn default() -> Self {9x324
MsixTableEntry {9x325
addr_lo: 0,9x326
addr_hi: 0,9x327
data: 0,9x328
control: MsixVectorCtrl(1),9x329
}9x330
}9x331
}332
333
pub trait PciCap: PciConfigArea {334
fn set_next(&mut self, val: u8);335
}336
337
impl SlotBackend for Box<dyn PciCap> {338
fn size(&self) -> u64 {60x339
Mmio::size(self.as_ref())60x340
}60x341
}342
343
impl Mmio for Box<dyn PciCap> {344
fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {36x345
Mmio::read(self.as_ref(), offset, size)36x346
}36x347
348
fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {6x349
Mmio::write(self.as_ref(), offset, size, val)6x350
}6x351
352
fn size(&self) -> u64 {6x353
Mmio::size(self.as_ref())6x354
}6x355
}356
357
#[derive(Debug)]358
pub struct PciCapList {359
inner: MmioBus<Box<dyn PciCap>>,360
}361
362
impl Default for PciCapList {363
fn default() -> Self {3x364
Self::new()3x365
}3x366
}367
368
impl PciCapList {369
pub fn new() -> PciCapList {83x370
Self {83x371
inner: MmioBus::new(),83x372
}83x373
}83x374
375
pub fn is_empty(&self) -> bool {89x376
self.inner.is_empty()89x377
}89x378
}379
380
impl PciConfigArea for PciCapList {381
fn reset(&self) -> Result<()> {6x382
for (_, cap) in self.inner.inner.iter() {12x383
cap.reset()?;12x384
}385
Ok(())6x386
}6x387
}388
389
impl Mmio for PciCapList {390
fn read(&self, offset: u64, size: u8) -> Result<u64, mem::Error> {36x391
self.inner.read(offset, size)36x392
}36x393
394
fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {6x395
self.inner.write(offset, size, val)6x396
}6x397
398
fn size(&self) -> u64 {3x399
40963x400
}3x401
}402
403
impl 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> {6x407
let mut bus = MmioBus::new();6x408
let mut ptr = size_of::<DeviceHeader>() as u64;6x409
let num_caps = caps.len();6x410
for (index, mut cap) in caps.into_iter().enumerate() {12x411
let next = if index == num_caps - 1 {12x412
06x413
} else {414
align_up!(ptr + Mmio::size(&cap), 2)6x415
};416
cap.set_next(next as u8);12x417
bus.add(ptr, cap)?;12x418
ptr = next;12x419
}420
Ok(Self { inner: bus })6x421
}6x422
}423
424
#[derive(Debug)]425
pub struct MsixCapMmio {426
cap: RwLock<MsixCap>,427
}428
429
impl MsixCapMmio {430
pub fn new(cap: MsixCap) -> Self {6x431
Self {6x432
cap: RwLock::new(cap),6x433
}6x434
}6x435
}436
437
impl Mmio for MsixCapMmio {438
fn size(&self) -> u64 {51x439
size_of::<MsixCap>() as u6451x440
}51x441
442
fn read(&self, offset: u64, size: u8) -> Result<u64, mem::Error> {42x443
let cap = self.cap.read();42x444
Mmio::read(&*cap, offset, size)42x445
}42x446
447
fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {15x448
if offset == 2 && size == 2 {15x449
let mut cap = self.cap.write();9x450
let control = MsixMsgCtrl(val as u16);9x451
cap.control.set_enabled(control.enabled());9x452
cap.control.set_masked(control.masked());9x453
}9x454
Ok(Action::None)15x455
}15x456
}457
458
impl PciConfigArea for MsixCapMmio {459
fn reset(&self) -> pci::Result<()> {9x460
let mut cap = self.cap.write();9x461
cap.control.set_enabled(false);9x462
cap.control.set_masked(false);9x463
Ok(())9x464
}9x465
}466
467
impl PciCap for MsixCapMmio {468
fn set_next(&mut self, val: u8) {9x469
self.cap.write().header.next = val;9x470
}9x471
}472
473
#[derive(Debug)]474
pub enum MsixTableMmioEntry<F> {475
Entry(MsixTableEntry),476
IrqFd(F),477
}478
479
impl<F> Default for MsixTableMmioEntry<F> {480
fn default() -> Self {9x481
MsixTableMmioEntry::Entry(MsixTableEntry::default())9x482
}9x483
}484
485
macro_rules! impl_msix_table_mmio_entry_method {486
($field:ident, $get:ident, $set:ident) => {487
pub fn $get(&self) -> u32 {18x488
match self {18x489
MsixTableMmioEntry::Entry(e) => e.$field,9x490
MsixTableMmioEntry::IrqFd(f) => f.$get(),9x491
}492
}18x493
fn $set(&mut self, val: u32) -> mem::Result<()> {18x494
match self {18x495
MsixTableMmioEntry::Entry(e) => e.$field = val,9x496
MsixTableMmioEntry::IrqFd(f) => f.$set(val)?,9x497
}498
Ok(())18x499
}18x500
};501
}502
503
impl<F> MsixTableMmioEntry<F>504
where505
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> {6x514
match self {6x515
MsixTableMmioEntry::Entry(e) => {3x516
let masked = e.control.masked();3x517
e.control.set_masked(val);3x518
Ok(masked != val)3x519
}520
MsixTableMmioEntry::IrqFd(f) => {3x521
let changed = f.set_masked(val)?;3x522
Ok(changed)3x523
}524
}525
}6x526
527
pub fn get_masked(&self) -> bool {12x528
match self {12x529
MsixTableMmioEntry::Entry(e) => e.control.masked(),6x530
MsixTableMmioEntry::IrqFd(f) => f.get_masked(),6x531
}532
}12x533
}534
535
#[derive(Debug)]536
pub struct MsixTableMmio<F> {537
pub entries: RwLock<Box<[MsixTableMmioEntry<F>]>>,538
}539
540
impl<F> MsixTableMmio<F>541
where542
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> {33x548
if size != 4 || offset & 0b11 != 0 {33x549
log::error!("unaligned access to msix table: size = {size}, offset = {offset:#x}");6x550
return Ok(false);6x551
}27x552
let val = val as u32;27x553
let index = offset as usize / size_of::<MsixTableEntry>();27x554
let mut entries = self.entries.write();27x555
let Some(entry) = entries.get_mut(index) else {27x556
log::error!(3x557
"MSI-X table size: {}, accessing index {index}",558
entries.len()3x559
);560
return Ok(false);3x561
};562
let mut state_changed = false;24x563
match offset as usize % size_of::<MsixTableEntry>() {24x564
0 => entry.set_addr_lo(val)?,6x565
4 => entry.set_addr_hi(val)?,6x566
8 => entry.set_data(val)?,6x567
12 => state_changed = entry.set_masked(MsixVectorCtrl(val).masked())?,6x568
_ => unreachable!(),569
};570
Ok(state_changed)24x571
}33x572
573
pub fn reset(&self) {3x574
let mut entries = self.entries.write();3x575
for entry in entries.iter_mut() {6x576
*entry = MsixTableMmioEntry::default();6x577
}6x578
}3x579
}580
581
impl<F> Mmio for MsixTableMmio<F>582
where583
F: IrqFd,584
{585
fn size(&self) -> u64 {3x586
(size_of::<MsixTableEntry>() * self.entries.read().len()) as u643x587
}3x588
589
fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {39x590
if size != 4 || offset & 0b11 != 0 {39x591
log::error!("unaligned access to msix table: size = {size}, offset = {offset:#x}");6x592
return Ok(0);6x593
}33x594
let index = offset as usize / size_of::<MsixTableEntry>();33x595
let entries = self.entries.read();33x596
let Some(entry) = entries.get(index) else {33x597
log::error!(3x598
"MSI-X table size: {}, accessing index {index}",599
entries.len()3x600
);601
return Ok(0);3x602
};603
let ret = match offset as usize % size_of::<MsixTableEntry>() {30x604
0 => entry.get_addr_lo(),6x605
4 => entry.get_addr_hi(),6x606
8 => entry.get_data(),6x607
12 => entry.get_masked() as _,12x608
_ => unreachable!(),609
};610
Ok(ret as u64)30x611
}39x612
613
fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {27x614
self.write_val(offset, size, val)?;27x615
Ok(Action::None)27x616
}27x617
}618
619
#[repr(C)]620
#[derive(Debug, Default, Clone)]621
pub struct NullCap {622
pub next: u8,623
pub size: u8,624
}625
626
impl Mmio for NullCap {627
fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {39x628
let shift = std::cmp::min(63, offset << 3);39x629
let val = ((self.next as u64) << 8) >> shift;39x630
Ok(truncate_u64(val, size as u64))39x631
}39x632
633
fn write(&self, _offset: u64, _size: u8, _val: u64) -> mem::Result<Action> {634
Ok(Action::None)635
}636
637
fn size(&self) -> u64 {18x638
self.size as u6418x639
}18x640
}641
642
impl PciCap for NullCap {643
fn set_next(&mut self, val: u8) {6x644
self.next = val;6x645
}6x646
}647
648
impl PciConfigArea for NullCap {649
fn reset(&self) -> Result<()> {6x650
Ok(())6x651
}6x652
}653
654
#[cfg(test)]655
#[path = "cap_test.rs"]656
mod tests;657