Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/pci/pci_configuration.rs
5394 views
1
// Copyright 2018 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
use std::collections::BTreeMap;
6
use std::convert::TryFrom;
7
use std::convert::TryInto;
8
use std::sync::Arc;
9
10
use anyhow::bail;
11
use anyhow::Context;
12
use base::custom_serde::deserialize_seq_to_arr;
13
use base::custom_serde::serialize_arr;
14
use base::error;
15
use base::warn;
16
use base::MemoryMapping;
17
use base::MemoryMappingBuilder;
18
use base::SharedMemory;
19
use downcast_rs::impl_downcast;
20
use downcast_rs::Downcast;
21
use remain::sorted;
22
use serde::Deserialize;
23
use serde::Serialize;
24
use snapshot::AnySnapshot;
25
use sync::Mutex;
26
use thiserror::Error;
27
28
use crate::pci::PciInterruptPin;
29
30
// The number of 32bit registers in the config space, 256 bytes.
31
const NUM_CONFIGURATION_REGISTERS: usize = 64;
32
33
pub const PCI_ID_REG: usize = 0;
34
pub const COMMAND_REG: usize = 1;
35
pub const COMMAND_REG_IO_SPACE_MASK: u32 = 0x0000_0001;
36
pub const COMMAND_REG_MEMORY_SPACE_MASK: u32 = 0x0000_0002;
37
const STATUS_REG: usize = 1;
38
pub const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000;
39
#[allow(dead_code)]
40
#[cfg(any(target_os = "android", target_os = "linux"))]
41
pub const CLASS_REG: usize = 2;
42
pub const HEADER_TYPE_REG: usize = 3;
43
pub const HEADER_TYPE_REG_OFFSET: usize = 2;
44
pub const HEADER_TYPE_MULTIFUNCTION_MASK: u8 = 0x80;
45
pub const BAR0_REG: usize = 4;
46
const BAR_IO_ADDR_MASK: u32 = 0xffff_fffc;
47
const BAR_IO_MIN_SIZE: u64 = 4;
48
const BAR_MEM_ADDR_MASK: u32 = 0xffff_fff0;
49
const BAR_MEM_MIN_SIZE: u64 = 16;
50
const BAR_ROM_MIN_SIZE: u64 = 2048;
51
pub const NUM_BAR_REGS: usize = 7; // 6 normal BARs + expansion ROM BAR.
52
pub const ROM_BAR_IDX: PciBarIndex = 6;
53
pub const ROM_BAR_REG: usize = 12;
54
pub const CAPABILITY_LIST_HEAD_OFFSET: usize = 0x34;
55
#[cfg(any(target_os = "android", target_os = "linux"))]
56
pub const PCI_CAP_NEXT_POINTER: usize = 0x1;
57
const FIRST_CAPABILITY_OFFSET: usize = 0x40;
58
pub const CAPABILITY_MAX_OFFSET: usize = 255;
59
60
const INTERRUPT_LINE_PIN_REG: usize = 15;
61
62
/// Represents the types of PCI headers allowed in the configuration registers.
63
#[allow(dead_code)]
64
#[derive(Copy, Clone)]
65
pub enum PciHeaderType {
66
Device,
67
Bridge,
68
}
69
70
/// Classes of PCI nodes.
71
#[allow(dead_code)]
72
#[derive(Copy, Clone, Debug, enumn::N, Serialize, Deserialize, PartialEq, Eq)]
73
pub enum PciClassCode {
74
TooOld,
75
MassStorage,
76
NetworkController,
77
DisplayController,
78
MultimediaController,
79
MemoryController,
80
BridgeDevice,
81
SimpleCommunicationController,
82
BaseSystemPeripheral,
83
InputDevice,
84
DockingStation,
85
Processor,
86
SerialBusController,
87
WirelessController,
88
IntelligentIoController,
89
SatelliteCommunicationController,
90
EncryptionController,
91
DataAcquisitionSignalProcessing,
92
ProcessingAccelerator,
93
NonEssentialInstrumentation,
94
Other = 0xff,
95
}
96
97
impl PciClassCode {
98
pub fn get_register_value(&self) -> u8 {
99
*self as u8
100
}
101
}
102
103
#[sorted]
104
#[derive(Error, Debug)]
105
pub enum PciClassCodeParseError {
106
#[error("Unknown class code")]
107
Unknown,
108
}
109
110
impl TryFrom<u8> for PciClassCode {
111
type Error = PciClassCodeParseError;
112
fn try_from(v: u8) -> std::result::Result<PciClassCode, PciClassCodeParseError> {
113
match PciClassCode::n(v) {
114
Some(class) => Ok(class),
115
None => Err(PciClassCodeParseError::Unknown),
116
}
117
}
118
}
119
120
/// A PCI sublcass. Each class in `PciClassCode` can specify a unique set of subclasses. This trait
121
/// is implemented by each subclass. It allows use of a trait object to generate configurations.
122
pub trait PciSubclass {
123
/// Convert this subclass to the value used in the PCI specification.
124
fn get_register_value(&self) -> u8;
125
}
126
127
/// Subclasses of the MassStorage class.
128
#[allow(dead_code)]
129
#[derive(Copy, Clone)]
130
pub enum PciMassStorageSubclass {
131
Scsi = 0x00,
132
NonVolatileMemory = 0x08,
133
Other = 0x80,
134
}
135
136
impl PciSubclass for PciMassStorageSubclass {
137
fn get_register_value(&self) -> u8 {
138
*self as u8
139
}
140
}
141
142
/// Subclasses of the NetworkController class.
143
#[allow(dead_code)]
144
#[derive(Copy, Clone)]
145
pub enum PciNetworkControllerSubclass {
146
Other = 0x80,
147
}
148
149
impl PciSubclass for PciNetworkControllerSubclass {
150
fn get_register_value(&self) -> u8 {
151
*self as u8
152
}
153
}
154
155
/// Subclasses of the DisplayController class.
156
#[allow(dead_code)]
157
#[derive(Copy, Clone)]
158
pub enum PciDisplaySubclass {
159
VgaCompatibleController = 0x00,
160
XgaCompatibleController = 0x01,
161
ThreeDController = 0x02,
162
Other = 0x80,
163
}
164
165
impl PciSubclass for PciDisplaySubclass {
166
fn get_register_value(&self) -> u8 {
167
*self as u8
168
}
169
}
170
171
/// Subclasses of the MultimediaController class.
172
#[allow(dead_code)]
173
#[derive(Copy, Clone)]
174
pub enum PciMultimediaSubclass {
175
VideoController = 0x00,
176
AudioController = 0x01,
177
TelephonyDevice = 0x02,
178
AudioDevice = 0x03,
179
Other = 0x80,
180
}
181
182
impl PciSubclass for PciMultimediaSubclass {
183
fn get_register_value(&self) -> u8 {
184
*self as u8
185
}
186
}
187
188
/// Subclasses of the BridgeDevice
189
#[allow(dead_code)]
190
#[derive(Copy, Clone)]
191
pub enum PciBridgeSubclass {
192
HostBridge = 0x00,
193
IsaBridge = 0x01,
194
EisaBridge = 0x02,
195
McaBridge = 0x03,
196
PciToPciBridge = 0x04,
197
PcmciaBridge = 0x05,
198
NuBusBridge = 0x06,
199
CardBusBridge = 0x07,
200
RaceWayBridge = 0x08,
201
PciToPciSemiTransparentBridge = 0x09,
202
InfiniBrandToPciHostBridge = 0x0a,
203
OtherBridgeDevice = 0x80,
204
}
205
206
impl PciSubclass for PciBridgeSubclass {
207
fn get_register_value(&self) -> u8 {
208
*self as u8
209
}
210
}
211
212
/// Subclasses of the SimpleCommunicationController class.
213
#[allow(dead_code)]
214
#[derive(Copy, Clone)]
215
pub enum PciSimpleCommunicationControllerSubclass {
216
Other = 0x80,
217
}
218
219
impl PciSubclass for PciSimpleCommunicationControllerSubclass {
220
fn get_register_value(&self) -> u8 {
221
*self as u8
222
}
223
}
224
225
/// Subclasses of the BaseSystemPeripheral class.
226
#[allow(dead_code)]
227
#[derive(Copy, Clone)]
228
pub enum PciBaseSystemPeripheralSubclass {
229
Iommu = 0x06,
230
Other = 0x80,
231
}
232
233
impl PciSubclass for PciBaseSystemPeripheralSubclass {
234
fn get_register_value(&self) -> u8 {
235
*self as u8
236
}
237
}
238
239
/// Subclasses of the InputDevice class.
240
#[allow(dead_code)]
241
#[derive(Copy, Clone)]
242
pub enum PciInputDeviceSubclass {
243
Other = 0x80,
244
}
245
246
impl PciSubclass for PciInputDeviceSubclass {
247
fn get_register_value(&self) -> u8 {
248
*self as u8
249
}
250
}
251
252
/// Subclass of the SerialBus
253
#[allow(dead_code)]
254
#[derive(Copy, Clone)]
255
pub enum PciSerialBusSubClass {
256
Firewire = 0x00,
257
AccessBus = 0x01,
258
Ssa = 0x02,
259
Usb = 0x03,
260
}
261
262
impl PciSubclass for PciSerialBusSubClass {
263
fn get_register_value(&self) -> u8 {
264
*self as u8
265
}
266
}
267
268
/// Subclasses of the WirelessController class.
269
#[allow(dead_code)]
270
#[derive(Copy, Clone)]
271
pub enum PciWirelessControllerSubclass {
272
Other = 0x80,
273
}
274
275
impl PciSubclass for PciWirelessControllerSubclass {
276
fn get_register_value(&self) -> u8 {
277
*self as u8
278
}
279
}
280
281
/// Subclasses for PciClassCode Other.
282
#[allow(dead_code)]
283
#[derive(Copy, Clone)]
284
#[repr(u8)]
285
pub enum PciOtherSubclass {
286
Other = 0xff,
287
}
288
289
impl PciSubclass for PciOtherSubclass {
290
fn get_register_value(&self) -> u8 {
291
*self as u8
292
}
293
}
294
295
/// A PCI class programming interface. Each combination of `PciClassCode` and
296
/// `PciSubclass` can specify a set of register-level programming interfaces.
297
/// This trait is implemented by each programming interface.
298
/// It allows use of a trait object to generate configurations.
299
pub trait PciProgrammingInterface {
300
/// Convert this programming interface to the value used in the PCI specification.
301
fn get_register_value(&self) -> u8;
302
}
303
304
/// Types of PCI capabilities.
305
pub enum PciCapabilityID {
306
ListID = 0,
307
PowerManagement = 0x01,
308
AcceleratedGraphicsPort = 0x02,
309
VitalProductData = 0x03,
310
SlotIdentification = 0x04,
311
MessageSignalledInterrupts = 0x05,
312
CompactPciHotSwap = 0x06,
313
Pcix = 0x07,
314
HyperTransport = 0x08,
315
VendorSpecific = 0x09,
316
Debugport = 0x0A,
317
CompactPciCentralResourceControl = 0x0B,
318
PciStandardHotPlugController = 0x0C,
319
BridgeSubsystemVendorDeviceID = 0x0D,
320
AgpTargetPciPciBridge = 0x0E,
321
SecureDevice = 0x0F,
322
PciExpress = 0x10,
323
Msix = 0x11,
324
SataDataIndexConf = 0x12,
325
PciAdvancedFeatures = 0x13,
326
PciEnhancedAllocation = 0x14,
327
}
328
329
/// A PCI capability list. Devices can optionally specify capabilities in their configuration space.
330
pub trait PciCapability {
331
fn bytes(&self) -> &[u8];
332
fn id(&self) -> PciCapabilityID;
333
fn writable_bits(&self) -> Vec<u32>;
334
}
335
336
pub trait PciCapConfigWriteResult: Downcast {}
337
impl_downcast!(PciCapConfigWriteResult);
338
339
/// A trait for implementing complex PCI capabilities.
340
pub trait PciCapConfig: Send {
341
/// Reads a 32bit register from the capability. Only the bits set in the
342
/// read mask will be used, while the rest of the bits will be taken from
343
/// the `PciConfiguration`'s register data.
344
/// `reg_idx` - index into the capability
345
fn read_reg(&self, reg_idx: usize) -> u32;
346
347
/// Returns the read mask used by `read_reg`.
348
fn read_mask(&self) -> &'static [u32];
349
350
/// Writes data to the capability.
351
/// `reg_idx` - index into PciConfiguration.registers.
352
/// `offset` - PciConfiguration.registers is in unit of DWord, offset define byte
353
/// offset in the DWord.
354
/// `data` - The data to write.
355
fn write_reg(
356
&mut self,
357
reg_idx: usize,
358
offset: u64,
359
data: &[u8],
360
) -> Option<Box<dyn PciCapConfigWriteResult>>;
361
362
/// Used to pass the mmio region for the capability to the implementation.
363
/// If any external events update the capability's registers, then
364
/// `PciCapMapping.set_reg` must be called to make the changes visible
365
/// to the guest.
366
fn set_cap_mapping(&mut self, _mapping: PciCapMapping) {}
367
368
fn num_regs(&self) -> usize {
369
self.read_mask().len()
370
}
371
}
372
373
/// Contains the configuration space of a PCI node.
374
/// See the [specification](https://en.wikipedia.org/wiki/PCI_configuration_space).
375
/// The configuration space is accessed with DWORD reads and writes from the guest.
376
pub struct PciConfiguration {
377
registers: [u32; NUM_CONFIGURATION_REGISTERS],
378
writable_bits: [u32; NUM_CONFIGURATION_REGISTERS], // writable bits for each register.
379
bar_used: [bool; NUM_BAR_REGS],
380
bar_configs: [Option<PciBarConfiguration>; NUM_BAR_REGS],
381
// Contains the byte offset and size of the last capability.
382
last_capability: Option<(usize, usize)>,
383
capability_configs: BTreeMap<usize, Box<dyn PciCapConfig>>,
384
mmio_mapping: Option<(Arc<Mutex<MemoryMapping>>, usize)>,
385
}
386
387
#[derive(Serialize, Deserialize)]
388
pub struct PciConfigurationSerialized {
389
#[serde(
390
serialize_with = "serialize_arr",
391
deserialize_with = "deserialize_seq_to_arr"
392
)]
393
registers: [u32; NUM_CONFIGURATION_REGISTERS],
394
#[serde(
395
serialize_with = "serialize_arr",
396
deserialize_with = "deserialize_seq_to_arr"
397
)]
398
writable_bits: [u32; NUM_CONFIGURATION_REGISTERS],
399
bar_used: [bool; NUM_BAR_REGS],
400
bar_configs: [Option<PciBarConfiguration>; NUM_BAR_REGS],
401
last_capability: Option<(usize, usize)>,
402
}
403
404
/// See pci_regs.h in kernel
405
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
406
pub enum PciBarRegionType {
407
Memory32BitRegion = 0,
408
IoRegion = 0x01,
409
Memory64BitRegion = 0x04,
410
}
411
412
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
413
pub enum PciBarPrefetchable {
414
NotPrefetchable = 0,
415
Prefetchable = 0x08,
416
}
417
418
pub type PciBarIndex = usize;
419
420
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
421
pub struct PciBarConfiguration {
422
addr: u64,
423
size: u64,
424
bar_idx: PciBarIndex,
425
region_type: PciBarRegionType,
426
prefetchable: PciBarPrefetchable,
427
}
428
429
pub struct PciBarIter<'a> {
430
config: &'a PciConfiguration,
431
bar_num: PciBarIndex,
432
}
433
434
impl Iterator for PciBarIter<'_> {
435
type Item = PciBarConfiguration;
436
437
fn next(&mut self) -> Option<Self::Item> {
438
while self.bar_num < NUM_BAR_REGS {
439
let bar_config = self.config.get_bar_configuration(self.bar_num);
440
self.bar_num += 1;
441
if let Some(bar_config) = bar_config {
442
return Some(bar_config);
443
}
444
}
445
446
None
447
}
448
}
449
450
#[sorted]
451
#[derive(Error, Debug, PartialEq, Eq)]
452
pub enum Error {
453
#[error("address {0} size {1} too big")]
454
BarAddressInvalid(u64, u64),
455
#[error("address {0} is not aligned to size {1}")]
456
BarAlignmentInvalid(u64, u64),
457
#[error("bar {0} already used")]
458
BarInUse(PciBarIndex),
459
#[error("64bit bar {0} already used (requires two regs)")]
460
BarInUse64(PciBarIndex),
461
#[error("bar {0} invalid, max {max}", max = NUM_BAR_REGS - 1)]
462
BarInvalid(PciBarIndex),
463
#[error("64bitbar {0} invalid, requires two regs, max {max}", max = ROM_BAR_IDX - 1)]
464
BarInvalid64(PciBarIndex),
465
#[error("expansion rom bar must be a memory region")]
466
BarInvalidRomType,
467
#[error("bar address {0} not a power of two")]
468
BarSizeInvalid(u64),
469
#[error("empty capabilities are invalid")]
470
CapabilityEmpty,
471
#[error("Invalid capability length {0}")]
472
CapabilityLengthInvalid(usize),
473
#[error("capability of size {0} doesn't fit")]
474
CapabilitySpaceFull(usize),
475
}
476
477
pub type Result<T> = std::result::Result<T, Error>;
478
479
impl PciConfiguration {
480
pub fn new(
481
vendor_id: u16,
482
device_id: u16,
483
class_code: PciClassCode,
484
subclass: &dyn PciSubclass,
485
programming_interface: Option<&dyn PciProgrammingInterface>,
486
header_type: PciHeaderType,
487
subsystem_vendor_id: u16,
488
subsystem_id: u16,
489
revision_id: u8,
490
) -> Self {
491
let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
492
let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS];
493
registers[0] = u32::from(device_id) << 16 | u32::from(vendor_id);
494
// TODO(dverkamp): Status should be write-1-to-clear
495
writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w)
496
let pi = if let Some(pi) = programming_interface {
497
pi.get_register_value()
498
} else {
499
0
500
};
501
registers[2] = u32::from(class_code.get_register_value()) << 24
502
| u32::from(subclass.get_register_value()) << 16
503
| u32::from(pi) << 8
504
| u32::from(revision_id);
505
writable_bits[3] = 0x0000_00ff; // Cacheline size (r/w)
506
match header_type {
507
PciHeaderType::Device => {
508
registers[3] = 0x0000_0000; // Header type 0 (device)
509
writable_bits[15] = 0x0000_00ff; // Interrupt line (r/w)
510
registers[11] = u32::from(subsystem_id) << 16 | u32::from(subsystem_vendor_id);
511
}
512
PciHeaderType::Bridge => {
513
registers[3] = 0x0001_0000; // Header type 1 (bridge)
514
writable_bits[6] = 0x00ff_ffff; // Primary/secondary/subordinate bus number,
515
// secondary latency timer
516
registers[7] = 0x0000_00f0; // IO base > IO Limit, no IO address on secondary side at initialize
517
writable_bits[7] = 0xf900_0000; // IO base and limit, secondary status,
518
registers[8] = 0x0000_fff0; // mem base > mem Limit, no MMIO address on secondary side at initialize
519
writable_bits[8] = 0xfff0_fff0; // Memory base and limit
520
registers[9] = 0x0001_fff1; // pmem base > pmem Limit, no prefetch MMIO address on secondary side at initialize
521
writable_bits[9] = 0xfff0_fff0; // Prefetchable base and limit
522
writable_bits[10] = 0xffff_ffff; // Prefetchable base upper 32 bits
523
writable_bits[11] = 0xffff_ffff; // Prefetchable limit upper 32 bits
524
writable_bits[15] = 0xffff_00ff; // Bridge control (r/w), interrupt line (r/w)
525
}
526
};
527
528
PciConfiguration {
529
registers,
530
writable_bits,
531
bar_used: [false; NUM_BAR_REGS],
532
bar_configs: [None; NUM_BAR_REGS],
533
last_capability: None,
534
capability_configs: BTreeMap::new(),
535
mmio_mapping: None,
536
}
537
}
538
539
/// Reads a 32bit register from `reg_idx` in the register map.
540
pub fn read_reg(&self, reg_idx: usize) -> u32 {
541
let mut data = *(self.registers.get(reg_idx).unwrap_or(&0xffff_ffff));
542
if let Some((idx, cfg)) = self.capability_configs.range(..=reg_idx).last() {
543
if reg_idx < idx + cfg.num_regs() {
544
let cap_idx = reg_idx - idx;
545
let mask = cfg.read_mask()[cap_idx];
546
data = (data & !mask) | (cfg.read_reg(cap_idx) & mask);
547
}
548
}
549
data
550
}
551
552
/// Writes data to PciConfiguration.registers.
553
/// `reg_idx` - index into PciConfiguration.registers.
554
/// `offset` - PciConfiguration.registers is in unit of DWord, offset define byte
555
/// offset in the DWord.
556
/// `data` - The data to write.
557
pub fn write_reg(
558
&mut self,
559
reg_idx: usize,
560
offset: u64,
561
data: &[u8],
562
) -> Option<Box<dyn PciCapConfigWriteResult>> {
563
let reg_offset = reg_idx * 4 + offset as usize;
564
match data.len() {
565
1 => self.write_byte(reg_offset, data[0]),
566
2 => self.write_word(reg_offset, u16::from_le_bytes(data.try_into().unwrap())),
567
4 => self.write_dword(reg_offset, u32::from_le_bytes(data.try_into().unwrap())),
568
_ => (),
569
}
570
if let Some((idx, cfg)) = self.capability_configs.range_mut(..=reg_idx).last() {
571
if reg_idx < idx + cfg.num_regs() {
572
let cap_idx = reg_idx - idx;
573
let ret = cfg.write_reg(cap_idx, offset, data);
574
let new_val = cfg.read_reg(cap_idx);
575
let mask = cfg.read_mask()[cap_idx];
576
self.set_reg(reg_idx, new_val, mask);
577
return ret;
578
}
579
}
580
None
581
}
582
583
/// Writes a 32bit dword to `offset`. `offset` must be 32bit aligned.
584
fn write_dword(&mut self, offset: usize, value: u32) {
585
if offset % 4 != 0 {
586
warn!("bad PCI config dword write offset {}", offset);
587
return;
588
}
589
let reg_idx = offset / 4;
590
if reg_idx < NUM_CONFIGURATION_REGISTERS {
591
let old_value = self.registers[reg_idx];
592
let new_value =
593
(old_value & !self.writable_bits[reg_idx]) | (value & self.writable_bits[reg_idx]);
594
self.do_write(reg_idx, new_value)
595
} else {
596
warn!("bad PCI dword write {}", offset);
597
}
598
}
599
600
/// Writes a 16bit word to `offset`. `offset` must be 16bit aligned.
601
fn write_word(&mut self, offset: usize, value: u16) {
602
let shift = match offset % 4 {
603
0 => 0,
604
2 => 16,
605
_ => {
606
warn!("bad PCI config word write offset {}", offset);
607
return;
608
}
609
};
610
let reg_idx = offset / 4;
611
612
if reg_idx < NUM_CONFIGURATION_REGISTERS {
613
let old_value = self.registers[reg_idx];
614
let writable_mask = self.writable_bits[reg_idx];
615
let mask = (0xffffu32 << shift) & writable_mask;
616
let shifted_value = (u32::from(value) << shift) & writable_mask;
617
let new_value = old_value & !mask | shifted_value;
618
self.do_write(reg_idx, new_value)
619
} else {
620
warn!("bad PCI config word write offset {}", offset);
621
}
622
}
623
624
/// Writes a byte to `offset`.
625
fn write_byte(&mut self, offset: usize, value: u8) {
626
self.write_byte_internal(offset, value, true);
627
}
628
629
/// Writes a byte to `offset`, optionally enforcing read-only bits.
630
fn write_byte_internal(&mut self, offset: usize, value: u8, apply_writable_mask: bool) {
631
let shift = (offset % 4) * 8;
632
let reg_idx = offset / 4;
633
634
if reg_idx < NUM_CONFIGURATION_REGISTERS {
635
let writable_mask = if apply_writable_mask {
636
self.writable_bits[reg_idx]
637
} else {
638
0xffff_ffff
639
};
640
let old_value = self.registers[reg_idx];
641
let mask = (0xffu32 << shift) & writable_mask;
642
let shifted_value = (u32::from(value) << shift) & writable_mask;
643
let new_value = old_value & !mask | shifted_value;
644
self.do_write(reg_idx, new_value)
645
} else {
646
warn!("bad PCI config byte write offset {}", offset);
647
}
648
}
649
650
/// Sets the value of a PciConfiguration register. This should be used when
651
/// device-internal events require changing the configuration space - as such,
652
/// the writable bits masks do not apply.
653
/// `reg_idx` - index into PciConfiguration.registers.
654
/// `data` - The data to write.
655
/// `mask` - The mask of which bits to modify.
656
pub fn set_reg(&mut self, reg_idx: usize, data: u32, mask: u32) {
657
if reg_idx >= NUM_CONFIGURATION_REGISTERS {
658
return;
659
}
660
let new_val = (self.registers[reg_idx] & !mask) | (data & mask);
661
self.do_write(reg_idx, new_val);
662
}
663
664
/// Adds a region specified by `config`. Configures the specified BAR(s) to
665
/// report this region and size to the guest kernel. Enforces a few constraints
666
/// (i.e, region size must be power of two, register not already used). Returns 'None' on
667
/// failure all, `Some(BarIndex)` on success.
668
pub fn add_pci_bar(&mut self, config: PciBarConfiguration) -> Result<PciBarIndex> {
669
if config.bar_idx >= NUM_BAR_REGS {
670
return Err(Error::BarInvalid(config.bar_idx));
671
}
672
673
if self.bar_used[config.bar_idx] {
674
return Err(Error::BarInUse(config.bar_idx));
675
}
676
677
if config.size.count_ones() != 1 {
678
return Err(Error::BarSizeInvalid(config.size));
679
}
680
681
if config.is_expansion_rom() && config.region_type != PciBarRegionType::Memory32BitRegion {
682
return Err(Error::BarInvalidRomType);
683
}
684
685
let min_size = if config.is_expansion_rom() {
686
BAR_ROM_MIN_SIZE
687
} else if config.region_type == PciBarRegionType::IoRegion {
688
BAR_IO_MIN_SIZE
689
} else {
690
BAR_MEM_MIN_SIZE
691
};
692
693
if config.size < min_size {
694
return Err(Error::BarSizeInvalid(config.size));
695
}
696
697
if config.addr % config.size != 0 {
698
return Err(Error::BarAlignmentInvalid(config.addr, config.size));
699
}
700
701
let reg_idx = config.reg_index();
702
let end_addr = config
703
.addr
704
.checked_add(config.size)
705
.ok_or(Error::BarAddressInvalid(config.addr, config.size))?;
706
match config.region_type {
707
PciBarRegionType::Memory32BitRegion | PciBarRegionType::IoRegion => {
708
if end_addr > u64::from(u32::MAX) {
709
return Err(Error::BarAddressInvalid(config.addr, config.size));
710
}
711
}
712
PciBarRegionType::Memory64BitRegion => {
713
// The expansion ROM BAR cannot be used for part of a 64-bit BAR.
714
if config.bar_idx + 1 >= ROM_BAR_IDX {
715
return Err(Error::BarInvalid64(config.bar_idx));
716
}
717
718
if self.bar_used[config.bar_idx + 1] {
719
return Err(Error::BarInUse64(config.bar_idx));
720
}
721
722
self.do_write(reg_idx + 1, (config.addr >> 32) as u32);
723
self.writable_bits[reg_idx + 1] = !((config.size - 1) >> 32) as u32;
724
self.bar_used[config.bar_idx + 1] = true;
725
}
726
}
727
728
let (mask, lower_bits) = match config.region_type {
729
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
730
self.registers[COMMAND_REG] |= COMMAND_REG_MEMORY_SPACE_MASK;
731
(
732
BAR_MEM_ADDR_MASK,
733
config.prefetchable as u32 | config.region_type as u32,
734
)
735
}
736
PciBarRegionType::IoRegion => {
737
self.registers[COMMAND_REG] |= COMMAND_REG_IO_SPACE_MASK;
738
(BAR_IO_ADDR_MASK, config.region_type as u32)
739
}
740
};
741
742
self.do_write(reg_idx, ((config.addr as u32) & mask) | lower_bits);
743
self.writable_bits[reg_idx] = !(config.size - 1) as u32;
744
if config.is_expansion_rom() {
745
self.writable_bits[reg_idx] |= 1; // Expansion ROM enable bit.
746
}
747
self.bar_used[config.bar_idx] = true;
748
self.bar_configs[config.bar_idx] = Some(config);
749
Ok(config.bar_idx)
750
}
751
752
/// Returns an iterator of the currently configured base address registers.
753
#[allow(dead_code)] // TODO(dverkamp): remove this once used
754
pub fn get_bars(&self) -> PciBarIter {
755
PciBarIter {
756
config: self,
757
bar_num: 0,
758
}
759
}
760
761
/// Returns the configuration of a base address register, if present.
762
pub fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> {
763
let config = self.bar_configs.get(bar_num)?;
764
765
if let Some(mut config) = config {
766
let command = self.read_reg(COMMAND_REG);
767
if (config.is_memory() && (command & COMMAND_REG_MEMORY_SPACE_MASK == 0))
768
|| (config.is_io() && (command & COMMAND_REG_IO_SPACE_MASK == 0))
769
{
770
return None;
771
}
772
773
// The address may have been modified by the guest, so the value in bar_configs
774
// may be outdated. Replace it with the current value.
775
config.addr = self.get_bar_addr(bar_num);
776
Some(config)
777
} else {
778
None
779
}
780
}
781
782
/// Returns the type of the given BAR region.
783
pub fn get_bar_type(&self, bar_num: PciBarIndex) -> Option<PciBarRegionType> {
784
self.bar_configs.get(bar_num)?.map(|c| c.region_type)
785
}
786
787
/// Returns the address of the given BAR region.
788
pub fn get_bar_addr(&self, bar_num: PciBarIndex) -> u64 {
789
let bar_idx = if bar_num == ROM_BAR_IDX {
790
ROM_BAR_REG
791
} else {
792
BAR0_REG + bar_num
793
};
794
795
let bar_type = match self.get_bar_type(bar_num) {
796
Some(t) => t,
797
None => return 0,
798
};
799
800
match bar_type {
801
PciBarRegionType::IoRegion => u64::from(self.registers[bar_idx] & BAR_IO_ADDR_MASK),
802
PciBarRegionType::Memory32BitRegion => {
803
u64::from(self.registers[bar_idx] & BAR_MEM_ADDR_MASK)
804
}
805
PciBarRegionType::Memory64BitRegion => {
806
u64::from(self.registers[bar_idx] & BAR_MEM_ADDR_MASK)
807
| u64::from(self.registers[bar_idx + 1]) << 32
808
}
809
}
810
}
811
812
/// Configures the IRQ line and pin used by this device.
813
pub fn set_irq(&mut self, line: u8, pin: PciInterruptPin) {
814
// `pin` is 1-based in the pci config space.
815
let pin_idx = (pin as u32) + 1;
816
let new_val = (self.registers[INTERRUPT_LINE_PIN_REG] & 0xffff_0000)
817
| (pin_idx << 8)
818
| u32::from(line);
819
self.do_write(INTERRUPT_LINE_PIN_REG, new_val)
820
}
821
822
/// Adds the capability `cap_data` to the list of capabilities.
823
/// `cap_data` should include the two-byte PCI capability header (type, next),
824
/// but not populate it. Correct values will be generated automatically based
825
/// on `cap_data.id()`.
826
pub fn add_capability(
827
&mut self,
828
cap_data: &dyn PciCapability,
829
cap_config: Option<Box<dyn PciCapConfig>>,
830
) -> Result<()> {
831
let total_len = cap_data.bytes().len();
832
// Check that the length is valid.
833
if cap_data.bytes().is_empty() {
834
return Err(Error::CapabilityEmpty);
835
}
836
let (cap_offset, tail_offset) = match self.last_capability {
837
Some((offset, len)) => (Self::next_dword(offset, len), offset + 1),
838
None => (FIRST_CAPABILITY_OFFSET, CAPABILITY_LIST_HEAD_OFFSET),
839
};
840
let end_offset = cap_offset
841
.checked_add(total_len)
842
.ok_or(Error::CapabilitySpaceFull(total_len))?;
843
if end_offset > CAPABILITY_MAX_OFFSET {
844
return Err(Error::CapabilitySpaceFull(total_len));
845
}
846
self.do_write(
847
STATUS_REG,
848
self.registers[STATUS_REG] | STATUS_REG_CAPABILITIES_USED_MASK,
849
);
850
self.write_byte_internal(tail_offset, cap_offset as u8, false);
851
self.write_byte_internal(cap_offset, cap_data.id() as u8, false);
852
self.write_byte_internal(cap_offset + 1, 0, false); // Next pointer.
853
for (i, byte) in cap_data.bytes().iter().enumerate().skip(2) {
854
self.write_byte_internal(cap_offset + i, *byte, false);
855
}
856
let reg_idx = cap_offset / 4;
857
for (i, dword) in cap_data.writable_bits().iter().enumerate() {
858
self.writable_bits[reg_idx + i] = *dword;
859
}
860
self.last_capability = Some((cap_offset, total_len));
861
if let Some(mut cap_config) = cap_config {
862
if let Some((mapping, offset)) = &self.mmio_mapping {
863
cap_config.set_cap_mapping(PciCapMapping {
864
mapping: mapping.clone(),
865
offset: reg_idx * 4 + offset,
866
num_regs: total_len / 4,
867
});
868
}
869
self.capability_configs.insert(cap_offset / 4, cap_config);
870
}
871
Ok(())
872
}
873
874
// Find the next aligned offset after the one given.
875
fn next_dword(offset: usize, len: usize) -> usize {
876
let next = offset + len;
877
(next + 3) & !3
878
}
879
880
fn do_write(&mut self, reg_idx: usize, value: u32) {
881
self.registers[reg_idx] = value;
882
if let Some((mmio_mapping, offset)) = self.mmio_mapping.as_ref() {
883
let mmio_mapping = mmio_mapping.lock();
884
let reg_offset = offset + reg_idx * 4;
885
if reg_idx == HEADER_TYPE_REG {
886
// Skip writing the header type byte (reg_idx=2/offset=3) as
887
// per the requirements of PciDevice.setup_pci_config_mapping.
888
mmio_mapping
889
.write_obj_volatile((value & 0xffff) as u16, reg_offset)
890
.expect("bad register offset");
891
// Skip HEADER_TYPE_REG_OFFSET (i.e. header+mfd byte)
892
mmio_mapping
893
.write_obj_volatile(((value >> 24) & 0xff) as u8, reg_offset + 3)
894
.expect("bad register offset");
895
} else {
896
mmio_mapping
897
.write_obj_volatile(value, reg_offset)
898
.expect("bad register offset");
899
}
900
if let Err(err) = mmio_mapping.flush_region(reg_offset, 4) {
901
error!(
902
"failed to flush write to pci mmio register ({}): {}",
903
reg_idx, err
904
);
905
}
906
}
907
}
908
909
pub fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
910
AnySnapshot::to_any(PciConfigurationSerialized {
911
registers: self.registers,
912
writable_bits: self.writable_bits,
913
bar_used: self.bar_used,
914
bar_configs: self.bar_configs,
915
last_capability: self.last_capability,
916
})
917
.context("failed to serialize PciConfiguration")
918
}
919
920
pub fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
921
let deser: PciConfigurationSerialized =
922
AnySnapshot::from_any(data).context("failed to deserialize PciConfiguration")?;
923
self.registers = deser.registers;
924
self.writable_bits = deser.writable_bits;
925
self.bar_used = deser.bar_used;
926
self.bar_configs = deser.bar_configs;
927
self.last_capability = deser.last_capability;
928
// Restore everything via do_write to avoid writing to the header type register
929
// and clobbering the multi-function device bit, as that bit is managed by the
930
// PciRoot. Since restore doesn't change the types or layout of PCI devices, the
931
// header type bits in the register are already correct anyway.
932
for i in 0..NUM_CONFIGURATION_REGISTERS {
933
self.do_write(i, self.registers[i]);
934
}
935
Ok(())
936
}
937
938
pub fn setup_mapping(
939
&mut self,
940
shmem: &SharedMemory,
941
base: usize,
942
len: usize,
943
) -> anyhow::Result<()> {
944
if self.mmio_mapping.is_some() {
945
bail!("PCIe config mmio mapping already initialized");
946
}
947
let mapping = MemoryMappingBuilder::new(base::pagesize())
948
.from_shared_memory(shmem)
949
.build()
950
.context("Failed to create mapping")?;
951
for i in 0..(len / 4) {
952
let val = self.registers.get(i).unwrap_or(&0xffff_ffff);
953
mapping
954
.write_obj_volatile(*val, base + i * 4)
955
.expect("memcpy failed");
956
}
957
let mapping = Arc::new(Mutex::new(mapping));
958
for (idx, cap) in self.capability_configs.iter_mut() {
959
let mut cap_mapping = PciCapMapping {
960
mapping: mapping.clone(),
961
offset: idx * 4 + base,
962
num_regs: cap.num_regs(),
963
};
964
for i in 0..cap.num_regs() {
965
let val = cap.read_reg(i);
966
let mask = cap.read_mask()[i];
967
cap_mapping.set_reg(i, val, mask);
968
}
969
cap.set_cap_mapping(cap_mapping);
970
}
971
self.mmio_mapping = Some((mapping, base));
972
Ok(())
973
}
974
}
975
976
impl PciBarConfiguration {
977
pub fn new(
978
bar_idx: PciBarIndex,
979
size: u64,
980
region_type: PciBarRegionType,
981
prefetchable: PciBarPrefetchable,
982
) -> Self {
983
PciBarConfiguration {
984
bar_idx,
985
addr: 0,
986
size,
987
region_type,
988
prefetchable,
989
}
990
}
991
992
pub fn bar_index(&self) -> PciBarIndex {
993
self.bar_idx
994
}
995
996
pub fn reg_index(&self) -> usize {
997
if self.bar_idx == ROM_BAR_IDX {
998
ROM_BAR_REG
999
} else {
1000
BAR0_REG + self.bar_idx
1001
}
1002
}
1003
1004
pub fn address(&self) -> u64 {
1005
self.addr
1006
}
1007
1008
pub fn address_range(&self) -> std::ops::Range<u64> {
1009
self.addr..self.addr + self.size
1010
}
1011
1012
pub fn set_address(mut self, addr: u64) -> Self {
1013
self.addr = addr;
1014
self
1015
}
1016
1017
pub fn size(&self) -> u64 {
1018
self.size
1019
}
1020
1021
pub fn is_expansion_rom(&self) -> bool {
1022
self.bar_idx == ROM_BAR_IDX
1023
}
1024
1025
pub fn is_memory(&self) -> bool {
1026
matches!(
1027
self.region_type,
1028
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion
1029
)
1030
}
1031
1032
pub fn is_64bit_memory(&self) -> bool {
1033
self.region_type == PciBarRegionType::Memory64BitRegion
1034
}
1035
1036
pub fn is_io(&self) -> bool {
1037
self.region_type == PciBarRegionType::IoRegion
1038
}
1039
1040
pub fn is_prefetchable(&self) -> bool {
1041
self.is_memory() && self.prefetchable == PciBarPrefetchable::Prefetchable
1042
}
1043
}
1044
1045
impl<T: PciCapConfig + ?Sized> PciCapConfig for Arc<Mutex<T>> {
1046
fn read_mask(&self) -> &'static [u32] {
1047
self.lock().read_mask()
1048
}
1049
fn read_reg(&self, reg_idx: usize) -> u32 {
1050
self.lock().read_reg(reg_idx)
1051
}
1052
fn write_reg(
1053
&mut self,
1054
reg_idx: usize,
1055
offset: u64,
1056
data: &[u8],
1057
) -> Option<Box<dyn PciCapConfigWriteResult>> {
1058
self.lock().write_reg(reg_idx, offset, data)
1059
}
1060
fn set_cap_mapping(&mut self, mapping: PciCapMapping) {
1061
self.lock().set_cap_mapping(mapping)
1062
}
1063
}
1064
1065
/// Struct for updating a capabilitiy's mmio mapping.
1066
pub struct PciCapMapping {
1067
mapping: Arc<Mutex<MemoryMapping>>,
1068
offset: usize,
1069
num_regs: usize,
1070
}
1071
1072
impl PciCapMapping {
1073
/// Set the bits of register `reg_idx` specified by `mask` to `data`.
1074
pub fn set_reg(&mut self, reg_idx: usize, data: u32, mask: u32) {
1075
if reg_idx >= self.num_regs {
1076
error!(
1077
"out of bounds register write {} vs {}",
1078
self.num_regs, reg_idx
1079
);
1080
return;
1081
}
1082
let mapping = self.mapping.lock();
1083
let offset = self.offset + reg_idx * 4;
1084
let cur_value = mapping.read_obj::<u32>(offset).expect("memcpy failed");
1085
let new_val = (cur_value & !mask) | (data & mask);
1086
mapping
1087
.write_obj_volatile(new_val, offset)
1088
.expect("memcpy failed");
1089
if let Err(err) = mapping.flush_region(offset, 4) {
1090
error!(
1091
"failed to flush write to pci cap in mmio register ({}): {}",
1092
reg_idx, err
1093
);
1094
}
1095
}
1096
}
1097
1098
#[cfg(test)]
1099
mod tests {
1100
use zerocopy::FromBytes;
1101
use zerocopy::Immutable;
1102
use zerocopy::IntoBytes;
1103
use zerocopy::KnownLayout;
1104
1105
use super::*;
1106
1107
#[repr(C, packed)]
1108
#[derive(Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout)]
1109
#[allow(dead_code)]
1110
struct TestCap {
1111
_vndr: u8,
1112
_next: u8,
1113
len: u8,
1114
foo: u8,
1115
}
1116
1117
impl PciCapability for TestCap {
1118
fn bytes(&self) -> &[u8] {
1119
self.as_bytes()
1120
}
1121
1122
fn id(&self) -> PciCapabilityID {
1123
PciCapabilityID::VendorSpecific
1124
}
1125
1126
fn writable_bits(&self) -> Vec<u32> {
1127
vec![0u32; 1]
1128
}
1129
}
1130
1131
#[test]
1132
fn add_capability() {
1133
let mut cfg = PciConfiguration::new(
1134
0x1234,
1135
0x5678,
1136
PciClassCode::MultimediaController,
1137
&PciMultimediaSubclass::AudioController,
1138
None,
1139
PciHeaderType::Device,
1140
0xABCD,
1141
0x2468,
1142
0,
1143
);
1144
1145
// Add two capabilities with different contents.
1146
let cap1 = TestCap {
1147
_vndr: 0,
1148
_next: 0,
1149
len: 4,
1150
foo: 0xAA,
1151
};
1152
let cap1_offset = 64;
1153
cfg.add_capability(&cap1, None).unwrap();
1154
1155
let cap2 = TestCap {
1156
_vndr: 0,
1157
_next: 0,
1158
len: 0x04,
1159
foo: 0x55,
1160
};
1161
let cap2_offset = 68;
1162
cfg.add_capability(&cap2, None).unwrap();
1163
1164
// The capability list head should be pointing to cap1.
1165
let cap_ptr = cfg.read_reg(CAPABILITY_LIST_HEAD_OFFSET / 4) & 0xFF;
1166
assert_eq!(cap1_offset, cap_ptr as usize);
1167
1168
// Verify the contents of the capabilities.
1169
let cap1_data = cfg.read_reg(cap1_offset / 4);
1170
assert_eq!(cap1_data & 0xFF, 0x09); // capability ID
1171
assert_eq!((cap1_data >> 8) & 0xFF, cap2_offset as u32); // next capability pointer
1172
assert_eq!((cap1_data >> 16) & 0xFF, 0x04); // cap1.len
1173
assert_eq!((cap1_data >> 24) & 0xFF, 0xAA); // cap1.foo
1174
1175
let cap2_data = cfg.read_reg(cap2_offset / 4);
1176
assert_eq!(cap2_data & 0xFF, 0x09); // capability ID
1177
assert_eq!((cap2_data >> 8) & 0xFF, 0x00); // next capability pointer
1178
assert_eq!((cap2_data >> 16) & 0xFF, 0x04); // cap2.len
1179
assert_eq!((cap2_data >> 24) & 0xFF, 0x55); // cap2.foo
1180
}
1181
1182
#[derive(Copy, Clone)]
1183
enum TestPI {
1184
Test = 0x5a,
1185
}
1186
1187
impl PciProgrammingInterface for TestPI {
1188
fn get_register_value(&self) -> u8 {
1189
*self as u8
1190
}
1191
}
1192
1193
#[test]
1194
fn class_code() {
1195
let cfg = PciConfiguration::new(
1196
0x1234,
1197
0x5678,
1198
PciClassCode::MultimediaController,
1199
&PciMultimediaSubclass::AudioController,
1200
Some(&TestPI::Test),
1201
PciHeaderType::Device,
1202
0xABCD,
1203
0x2468,
1204
0,
1205
);
1206
1207
let class_reg = cfg.read_reg(2);
1208
let class_code = (class_reg >> 24) & 0xFF;
1209
let subclass = (class_reg >> 16) & 0xFF;
1210
let prog_if = (class_reg >> 8) & 0xFF;
1211
assert_eq!(class_code, 0x04);
1212
assert_eq!(subclass, 0x01);
1213
assert_eq!(prog_if, 0x5a);
1214
}
1215
1216
#[test]
1217
fn read_only_bits() {
1218
let mut cfg = PciConfiguration::new(
1219
0x1234,
1220
0x5678,
1221
PciClassCode::MultimediaController,
1222
&PciMultimediaSubclass::AudioController,
1223
Some(&TestPI::Test),
1224
PciHeaderType::Device,
1225
0xABCD,
1226
0x2468,
1227
0,
1228
);
1229
1230
// Attempt to overwrite vendor ID and device ID, which are read-only
1231
cfg.write_reg(0, 0, &[0xBA, 0xAD, 0xF0, 0x0D]);
1232
// The original vendor and device ID should remain.
1233
assert_eq!(cfg.read_reg(0), 0x56781234);
1234
}
1235
1236
#[test]
1237
fn query_unused_bar() {
1238
let cfg = PciConfiguration::new(
1239
0x1234,
1240
0x5678,
1241
PciClassCode::MultimediaController,
1242
&PciMultimediaSubclass::AudioController,
1243
Some(&TestPI::Test),
1244
PciHeaderType::Device,
1245
0xABCD,
1246
0x2468,
1247
0,
1248
);
1249
1250
// No BAR 0 has been configured, so these should return None or 0 as appropriate.
1251
assert_eq!(cfg.get_bar_type(0), None);
1252
assert_eq!(cfg.get_bar_addr(0), 0);
1253
1254
let mut bar_iter = cfg.get_bars();
1255
assert_eq!(bar_iter.next(), None);
1256
}
1257
1258
#[test]
1259
fn add_pci_bar_mem_64bit() {
1260
let mut cfg = PciConfiguration::new(
1261
0x1234,
1262
0x5678,
1263
PciClassCode::MultimediaController,
1264
&PciMultimediaSubclass::AudioController,
1265
Some(&TestPI::Test),
1266
PciHeaderType::Device,
1267
0xABCD,
1268
0x2468,
1269
0,
1270
);
1271
1272
cfg.add_pci_bar(
1273
PciBarConfiguration::new(
1274
0,
1275
0x10,
1276
PciBarRegionType::Memory64BitRegion,
1277
PciBarPrefetchable::NotPrefetchable,
1278
)
1279
.set_address(0x0123_4567_89AB_CDE0),
1280
)
1281
.expect("add_pci_bar failed");
1282
1283
assert_eq!(
1284
cfg.get_bar_type(0),
1285
Some(PciBarRegionType::Memory64BitRegion)
1286
);
1287
assert_eq!(cfg.get_bar_addr(0), 0x0123_4567_89AB_CDE0);
1288
assert_eq!(cfg.writable_bits[BAR0_REG + 1], 0xFFFFFFFF);
1289
assert_eq!(cfg.writable_bits[BAR0_REG + 0], 0xFFFFFFF0);
1290
1291
let mut bar_iter = cfg.get_bars();
1292
assert_eq!(
1293
bar_iter.next(),
1294
Some(PciBarConfiguration {
1295
addr: 0x0123_4567_89AB_CDE0,
1296
size: 0x10,
1297
bar_idx: 0,
1298
region_type: PciBarRegionType::Memory64BitRegion,
1299
prefetchable: PciBarPrefetchable::NotPrefetchable
1300
})
1301
);
1302
assert_eq!(bar_iter.next(), None);
1303
}
1304
1305
#[test]
1306
fn add_pci_bar_mem_32bit() {
1307
let mut cfg = PciConfiguration::new(
1308
0x1234,
1309
0x5678,
1310
PciClassCode::MultimediaController,
1311
&PciMultimediaSubclass::AudioController,
1312
Some(&TestPI::Test),
1313
PciHeaderType::Device,
1314
0xABCD,
1315
0x2468,
1316
0,
1317
);
1318
1319
cfg.add_pci_bar(
1320
PciBarConfiguration::new(
1321
0,
1322
0x10,
1323
PciBarRegionType::Memory32BitRegion,
1324
PciBarPrefetchable::NotPrefetchable,
1325
)
1326
.set_address(0x12345670),
1327
)
1328
.expect("add_pci_bar failed");
1329
1330
assert_eq!(
1331
cfg.get_bar_type(0),
1332
Some(PciBarRegionType::Memory32BitRegion)
1333
);
1334
assert_eq!(cfg.get_bar_addr(0), 0x12345670);
1335
assert_eq!(cfg.writable_bits[BAR0_REG], 0xFFFFFFF0);
1336
1337
let mut bar_iter = cfg.get_bars();
1338
assert_eq!(
1339
bar_iter.next(),
1340
Some(PciBarConfiguration {
1341
addr: 0x12345670,
1342
size: 0x10,
1343
bar_idx: 0,
1344
region_type: PciBarRegionType::Memory32BitRegion,
1345
prefetchable: PciBarPrefetchable::NotPrefetchable
1346
})
1347
);
1348
assert_eq!(bar_iter.next(), None);
1349
}
1350
1351
#[test]
1352
fn add_pci_bar_io() {
1353
let mut cfg = PciConfiguration::new(
1354
0x1234,
1355
0x5678,
1356
PciClassCode::MultimediaController,
1357
&PciMultimediaSubclass::AudioController,
1358
Some(&TestPI::Test),
1359
PciHeaderType::Device,
1360
0xABCD,
1361
0x2468,
1362
0,
1363
);
1364
1365
cfg.add_pci_bar(
1366
PciBarConfiguration::new(
1367
0,
1368
0x4,
1369
PciBarRegionType::IoRegion,
1370
PciBarPrefetchable::NotPrefetchable,
1371
)
1372
.set_address(0x1230),
1373
)
1374
.expect("add_pci_bar failed");
1375
1376
assert_eq!(cfg.get_bar_type(0), Some(PciBarRegionType::IoRegion));
1377
assert_eq!(cfg.get_bar_addr(0), 0x1230);
1378
assert_eq!(cfg.writable_bits[BAR0_REG], 0xFFFFFFFC);
1379
1380
let mut bar_iter = cfg.get_bars();
1381
assert_eq!(
1382
bar_iter.next(),
1383
Some(PciBarConfiguration {
1384
addr: 0x1230,
1385
size: 0x4,
1386
bar_idx: 0,
1387
region_type: PciBarRegionType::IoRegion,
1388
prefetchable: PciBarPrefetchable::NotPrefetchable
1389
})
1390
);
1391
assert_eq!(bar_iter.next(), None);
1392
}
1393
1394
#[test]
1395
fn add_pci_bar_multiple() {
1396
let mut cfg = PciConfiguration::new(
1397
0x1234,
1398
0x5678,
1399
PciClassCode::MultimediaController,
1400
&PciMultimediaSubclass::AudioController,
1401
Some(&TestPI::Test),
1402
PciHeaderType::Device,
1403
0xABCD,
1404
0x2468,
1405
0,
1406
);
1407
1408
// bar_num 0-1: 64-bit memory
1409
cfg.add_pci_bar(
1410
PciBarConfiguration::new(
1411
0,
1412
0x10,
1413
PciBarRegionType::Memory64BitRegion,
1414
PciBarPrefetchable::NotPrefetchable,
1415
)
1416
.set_address(0x0123_4567_89AB_CDE0),
1417
)
1418
.expect("add_pci_bar failed");
1419
1420
// bar 2: 32-bit memory
1421
cfg.add_pci_bar(
1422
PciBarConfiguration::new(
1423
2,
1424
0x10,
1425
PciBarRegionType::Memory32BitRegion,
1426
PciBarPrefetchable::NotPrefetchable,
1427
)
1428
.set_address(0x12345670),
1429
)
1430
.expect("add_pci_bar failed");
1431
1432
// bar 3: I/O
1433
cfg.add_pci_bar(
1434
PciBarConfiguration::new(
1435
3,
1436
0x4,
1437
PciBarRegionType::IoRegion,
1438
PciBarPrefetchable::NotPrefetchable,
1439
)
1440
.set_address(0x1230),
1441
)
1442
.expect("add_pci_bar failed");
1443
1444
// Confirm default memory and I/O region configurations.
1445
let mut bar_iter = cfg.get_bars();
1446
assert_eq!(
1447
bar_iter.next(),
1448
Some(PciBarConfiguration {
1449
addr: 0x0123_4567_89AB_CDE0,
1450
size: 0x10,
1451
bar_idx: 0,
1452
region_type: PciBarRegionType::Memory64BitRegion,
1453
prefetchable: PciBarPrefetchable::NotPrefetchable
1454
})
1455
);
1456
assert_eq!(
1457
bar_iter.next(),
1458
Some(PciBarConfiguration {
1459
addr: 0x12345670,
1460
size: 0x10,
1461
bar_idx: 2,
1462
region_type: PciBarRegionType::Memory32BitRegion,
1463
prefetchable: PciBarPrefetchable::NotPrefetchable
1464
})
1465
);
1466
assert_eq!(
1467
bar_iter.next(),
1468
Some(PciBarConfiguration {
1469
addr: 0x1230,
1470
size: 0x4,
1471
bar_idx: 3,
1472
region_type: PciBarRegionType::IoRegion,
1473
prefetchable: PciBarPrefetchable::NotPrefetchable
1474
})
1475
);
1476
assert_eq!(bar_iter.next(), None);
1477
1478
// Reassign the address for BAR 0 and verify that get_memory_regions() matches.
1479
cfg.write_reg(4 + 0, 0, &0xBBAA9980u32.to_le_bytes());
1480
cfg.write_reg(4 + 1, 0, &0xFFEEDDCCu32.to_le_bytes());
1481
1482
let mut bar_iter = cfg.get_bars();
1483
assert_eq!(
1484
bar_iter.next(),
1485
Some(PciBarConfiguration {
1486
addr: 0xFFEE_DDCC_BBAA_9980,
1487
size: 0x10,
1488
bar_idx: 0,
1489
region_type: PciBarRegionType::Memory64BitRegion,
1490
prefetchable: PciBarPrefetchable::NotPrefetchable
1491
})
1492
);
1493
assert_eq!(
1494
bar_iter.next(),
1495
Some(PciBarConfiguration {
1496
addr: 0x12345670,
1497
size: 0x10,
1498
bar_idx: 2,
1499
region_type: PciBarRegionType::Memory32BitRegion,
1500
prefetchable: PciBarPrefetchable::NotPrefetchable
1501
})
1502
);
1503
assert_eq!(
1504
bar_iter.next(),
1505
Some(PciBarConfiguration {
1506
addr: 0x1230,
1507
size: 0x4,
1508
bar_idx: 3,
1509
region_type: PciBarRegionType::IoRegion,
1510
prefetchable: PciBarPrefetchable::NotPrefetchable
1511
})
1512
);
1513
assert_eq!(bar_iter.next(), None);
1514
}
1515
1516
#[test]
1517
fn add_pci_bar_invalid_size() {
1518
let mut cfg = PciConfiguration::new(
1519
0x1234,
1520
0x5678,
1521
PciClassCode::MultimediaController,
1522
&PciMultimediaSubclass::AudioController,
1523
Some(&TestPI::Test),
1524
PciHeaderType::Device,
1525
0xABCD,
1526
0x2468,
1527
0,
1528
);
1529
1530
// I/O BAR with size 2 (too small)
1531
assert_eq!(
1532
cfg.add_pci_bar(
1533
PciBarConfiguration::new(
1534
0,
1535
0x2,
1536
PciBarRegionType::IoRegion,
1537
PciBarPrefetchable::NotPrefetchable,
1538
)
1539
.set_address(0x1230),
1540
),
1541
Err(Error::BarSizeInvalid(0x2))
1542
);
1543
1544
// I/O BAR with size 3 (not a power of 2)
1545
assert_eq!(
1546
cfg.add_pci_bar(
1547
PciBarConfiguration::new(
1548
0,
1549
0x3,
1550
PciBarRegionType::IoRegion,
1551
PciBarPrefetchable::NotPrefetchable,
1552
)
1553
.set_address(0x1230),
1554
),
1555
Err(Error::BarSizeInvalid(0x3))
1556
);
1557
1558
// Memory BAR with size 8 (too small)
1559
assert_eq!(
1560
cfg.add_pci_bar(
1561
PciBarConfiguration::new(
1562
0,
1563
0x8,
1564
PciBarRegionType::Memory32BitRegion,
1565
PciBarPrefetchable::NotPrefetchable,
1566
)
1567
.set_address(0x12345670),
1568
),
1569
Err(Error::BarSizeInvalid(0x8))
1570
);
1571
}
1572
1573
#[test]
1574
fn add_rom_bar() {
1575
let mut cfg = PciConfiguration::new(
1576
0x1234,
1577
0x5678,
1578
PciClassCode::MultimediaController,
1579
&PciMultimediaSubclass::AudioController,
1580
Some(&TestPI::Test),
1581
PciHeaderType::Device,
1582
0xABCD,
1583
0x2468,
1584
0,
1585
);
1586
1587
// Attempt to add a 64-bit memory BAR as the expansion ROM (invalid).
1588
assert_eq!(
1589
cfg.add_pci_bar(PciBarConfiguration::new(
1590
ROM_BAR_IDX,
1591
0x1000,
1592
PciBarRegionType::Memory64BitRegion,
1593
PciBarPrefetchable::NotPrefetchable,
1594
),),
1595
Err(Error::BarInvalidRomType)
1596
);
1597
1598
// Attempt to add an I/O BAR as the expansion ROM (invalid).
1599
assert_eq!(
1600
cfg.add_pci_bar(PciBarConfiguration::new(
1601
ROM_BAR_IDX,
1602
0x1000,
1603
PciBarRegionType::IoRegion,
1604
PciBarPrefetchable::NotPrefetchable,
1605
),),
1606
Err(Error::BarInvalidRomType)
1607
);
1608
1609
// Attempt to add a 1KB memory region as the expansion ROM (too small).
1610
assert_eq!(
1611
cfg.add_pci_bar(PciBarConfiguration::new(
1612
ROM_BAR_IDX,
1613
1024,
1614
PciBarRegionType::Memory32BitRegion,
1615
PciBarPrefetchable::NotPrefetchable,
1616
),),
1617
Err(Error::BarSizeInvalid(1024))
1618
);
1619
1620
// Add a 32-bit memory BAR as the expansion ROM (valid).
1621
cfg.add_pci_bar(
1622
PciBarConfiguration::new(
1623
ROM_BAR_IDX,
1624
0x800,
1625
PciBarRegionType::Memory32BitRegion,
1626
PciBarPrefetchable::NotPrefetchable,
1627
)
1628
.set_address(0x12345000),
1629
)
1630
.expect("add_pci_bar failed");
1631
1632
assert_eq!(
1633
cfg.get_bar_type(ROM_BAR_IDX),
1634
Some(PciBarRegionType::Memory32BitRegion)
1635
);
1636
assert_eq!(cfg.get_bar_addr(ROM_BAR_IDX), 0x12345000);
1637
assert_eq!(cfg.read_reg(ROM_BAR_REG), 0x12345000);
1638
assert_eq!(cfg.writable_bits[ROM_BAR_REG], 0xFFFFF801);
1639
}
1640
1641
#[test]
1642
fn pci_configuration_capability_snapshot_restore() -> anyhow::Result<()> {
1643
let mut cfg = PciConfiguration::new(
1644
0x1234,
1645
0x5678,
1646
PciClassCode::MultimediaController,
1647
&PciMultimediaSubclass::AudioController,
1648
Some(&TestPI::Test),
1649
PciHeaderType::Device,
1650
0xABCD,
1651
0x2468,
1652
0,
1653
);
1654
1655
let snap_init = cfg.snapshot().context("failed to snapshot")?;
1656
1657
// Add a capability.
1658
let cap1 = TestCap {
1659
_vndr: 0,
1660
_next: 0,
1661
len: 4,
1662
foo: 0xAA,
1663
};
1664
cfg.add_capability(&cap1, None).unwrap();
1665
1666
let snap_mod = cfg.snapshot().context("failed to snapshot mod")?;
1667
cfg.restore(snap_init.clone())
1668
.context("failed to restore snap_init")?;
1669
let snap_restore_init = cfg.snapshot().context("failed to snapshot restored_init")?;
1670
assert_eq!(snap_init, snap_restore_init);
1671
assert_ne!(snap_init, snap_mod);
1672
cfg.restore(snap_mod.clone())
1673
.context("failed to restore snap_init")?;
1674
let snap_restore_mod = cfg.snapshot().context("failed to snapshot restored_mod")?;
1675
assert_eq!(snap_mod, snap_restore_mod);
1676
Ok(())
1677
}
1678
1679
#[test]
1680
fn pci_configuration_pci_bar_snapshot_restore() -> anyhow::Result<()> {
1681
let mut cfg = PciConfiguration::new(
1682
0x1234,
1683
0x5678,
1684
PciClassCode::MultimediaController,
1685
&PciMultimediaSubclass::AudioController,
1686
Some(&TestPI::Test),
1687
PciHeaderType::Device,
1688
0xABCD,
1689
0x2468,
1690
0,
1691
);
1692
1693
let snap_init = cfg.snapshot().context("failed to snapshot")?;
1694
1695
// bar_num 0-1: 64-bit memory
1696
cfg.add_pci_bar(
1697
PciBarConfiguration::new(
1698
0,
1699
0x10,
1700
PciBarRegionType::Memory64BitRegion,
1701
PciBarPrefetchable::NotPrefetchable,
1702
)
1703
.set_address(0x0123_4567_89AB_CDE0),
1704
)
1705
.expect("add_pci_bar failed");
1706
1707
let snap_mod = cfg.snapshot().context("failed to snapshot mod")?;
1708
cfg.restore(snap_init.clone())
1709
.context("failed to restore snap_init")?;
1710
let snap_restore_init = cfg.snapshot().context("failed to snapshot restored_init")?;
1711
assert_eq!(snap_init, snap_restore_init);
1712
assert_ne!(snap_init, snap_mod);
1713
cfg.restore(snap_mod.clone())
1714
.context("failed to restore snap_init")?;
1715
let snap_restore_mod = cfg.snapshot().context("failed to snapshot restored_mod")?;
1716
assert_eq!(snap_mod, snap_restore_mod);
1717
Ok(())
1718
}
1719
1720
#[test]
1721
fn pci_configuration_capability_pci_bar_snapshot_restore() -> anyhow::Result<()> {
1722
let mut cfg = PciConfiguration::new(
1723
0x1234,
1724
0x5678,
1725
PciClassCode::MultimediaController,
1726
&PciMultimediaSubclass::AudioController,
1727
Some(&TestPI::Test),
1728
PciHeaderType::Device,
1729
0xABCD,
1730
0x2468,
1731
0,
1732
);
1733
1734
let snap_init = cfg.snapshot().context("failed to snapshot")?;
1735
1736
// Add a capability.
1737
let cap1 = TestCap {
1738
_vndr: 0,
1739
_next: 0,
1740
len: 4,
1741
foo: 0xAA,
1742
};
1743
cfg.add_capability(&cap1, None).unwrap();
1744
1745
// bar_num 0-1: 64-bit memory
1746
cfg.add_pci_bar(
1747
PciBarConfiguration::new(
1748
0,
1749
0x10,
1750
PciBarRegionType::Memory64BitRegion,
1751
PciBarPrefetchable::NotPrefetchable,
1752
)
1753
.set_address(0x0123_4567_89AB_CDE0),
1754
)
1755
.expect("add_pci_bar failed");
1756
1757
let snap_mod = cfg.snapshot().context("failed to snapshot mod")?;
1758
cfg.restore(snap_init.clone())
1759
.context("failed to restore snap_init")?;
1760
let snap_restore_init = cfg.snapshot().context("failed to snapshot restored_init")?;
1761
assert_eq!(snap_init, snap_restore_init);
1762
assert_ne!(snap_init, snap_mod);
1763
cfg.restore(snap_mod.clone())
1764
.context("failed to restore snap_init")?;
1765
let snap_restore_mod = cfg.snapshot().context("failed to snapshot restored_mod")?;
1766
assert_eq!(snap_mod, snap_restore_mod);
1767
Ok(())
1768
}
1769
}
1770
1771