Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/pci/pcie/pci_bridge.rs
5394 views
1
// Copyright 2021 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::cmp::max;
6
use std::cmp::min;
7
use std::sync::Arc;
8
9
use base::warn;
10
use base::AsRawDescriptors;
11
use base::RawDescriptor;
12
use base::Tube;
13
use resources::Alloc;
14
use resources::AllocOptions;
15
use resources::SystemAllocator;
16
use sync::Mutex;
17
18
use crate::pci::msi::MsiCap;
19
use crate::pci::msi::MsiConfig;
20
use crate::pci::pci_configuration::PciBridgeSubclass;
21
use crate::pci::pcie::pcie_device::PcieDevice;
22
use crate::pci::BarRange;
23
use crate::pci::PciAddress;
24
use crate::pci::PciBarConfiguration;
25
use crate::pci::PciBarIndex;
26
use crate::pci::PciBus;
27
use crate::pci::PciClassCode;
28
use crate::pci::PciConfiguration;
29
use crate::pci::PciDevice;
30
use crate::pci::PciDeviceError;
31
use crate::pci::PciHeaderType;
32
use crate::pci::PCI_VENDOR_ID_INTEL;
33
use crate::IrqLevelEvent;
34
use crate::PciInterruptPin;
35
use crate::Suspendable;
36
37
pub const BR_BUS_NUMBER_REG: usize = 0x6;
38
pub const BR_BUS_SUBORDINATE_OFFSET: usize = 0x2;
39
pub const BR_MEM_REG: usize = 0x8;
40
// bit[15:4] is memory base[31:20] and alignment to 1MB
41
pub const BR_MEM_BASE_MASK: u32 = 0xFFF0;
42
pub const BR_MEM_BASE_SHIFT: u32 = 16;
43
// bit[31:20] is memory limit[31:20] and alignment to 1MB
44
pub const BR_MEM_LIMIT_MASK: u32 = 0xFFF0_0000;
45
pub const BR_PREF_MEM_LOW_REG: usize = 0x9;
46
// bit[0] and bit[16] is 64bit memory flag
47
pub const BR_PREF_MEM_64BIT: u32 = 0x001_0001;
48
pub const BR_PREF_MEM_BASE_HIGH_REG: usize = 0xa;
49
pub const BR_PREF_MEM_LIMIT_HIGH_REG: usize = 0xb;
50
pub const BR_WINDOW_ALIGNMENT: u64 = 0x10_0000;
51
pub const BR_WINDOW_MASK: u64 = !(BR_WINDOW_ALIGNMENT - 1);
52
// Kernel allocate at least 2MB mmio for each bridge memory window
53
pub const BR_MEM_MINIMUM: u64 = 0x20_0000;
54
55
/// Holds the bus range for a pci bridge
56
///
57
/// * primary - primary bus number
58
/// * secondary - secondary bus number
59
/// * subordinate - subordinate bus number
60
#[derive(Debug, Copy, Clone)]
61
pub struct PciBridgeBusRange {
62
pub primary: u8,
63
pub secondary: u8,
64
pub subordinate: u8,
65
}
66
67
pub struct PciBridge {
68
device: Arc<Mutex<dyn PcieDevice>>,
69
config: PciConfiguration,
70
pci_address: Option<PciAddress>,
71
pci_bus: Arc<Mutex<PciBus>>,
72
bus_range: PciBridgeBusRange,
73
msi_config: Arc<Mutex<MsiConfig>>,
74
interrupt_evt: Option<IrqLevelEvent>,
75
}
76
77
impl PciBridge {
78
pub fn new(device: Arc<Mutex<dyn PcieDevice>>, msi_device_tube: Tube) -> Self {
79
let device_id = device.lock().get_device_id();
80
let msi_config = Arc::new(Mutex::new(MsiConfig::new(
81
true,
82
false,
83
msi_device_tube,
84
(PCI_VENDOR_ID_INTEL as u32) | (device_id as u32) << 16,
85
device.lock().debug_label(),
86
)));
87
88
let mut config = PciConfiguration::new(
89
PCI_VENDOR_ID_INTEL,
90
device_id,
91
PciClassCode::BridgeDevice,
92
&PciBridgeSubclass::PciToPciBridge,
93
None,
94
PciHeaderType::Bridge,
95
0,
96
0,
97
0,
98
);
99
let msi_cap = MsiCap::new(true, false);
100
config
101
.add_capability(&msi_cap, Some(Box::new(msi_config.clone())))
102
.map_err(PciDeviceError::CapabilitiesSetup)
103
.unwrap();
104
let bus_range = device
105
.lock()
106
.get_bus_range()
107
.expect("PciBridge's backend device must implement get_bus_range()");
108
109
let data = [
110
bus_range.primary,
111
bus_range.secondary,
112
bus_range.subordinate,
113
0,
114
];
115
config.write_reg(BR_BUS_NUMBER_REG, 0, &data[..]);
116
let pci_bus = Arc::new(Mutex::new(PciBus::new(
117
bus_range.secondary,
118
bus_range.primary,
119
device.lock().hotplug_implemented(),
120
)));
121
122
PciBridge {
123
device,
124
config,
125
pci_address: None,
126
pci_bus,
127
bus_range,
128
msi_config,
129
interrupt_evt: None,
130
}
131
}
132
133
fn write_bridge_window(
134
&mut self,
135
window_base: u32,
136
window_size: u32,
137
pref_window_base: u64,
138
pref_window_size: u64,
139
) {
140
// both window_base and window_size should be aligned to 1M
141
if window_base & (BR_WINDOW_ALIGNMENT as u32 - 1) == 0
142
&& window_size != 0
143
&& window_size & (BR_WINDOW_ALIGNMENT as u32 - 1) == 0
144
{
145
// the top of memory will be one less than a 1MB boundary
146
let limit = window_base + window_size - BR_WINDOW_ALIGNMENT as u32;
147
let value = (window_base >> BR_MEM_BASE_SHIFT) | limit;
148
self.write_config_register(BR_MEM_REG, 0, &value.to_le_bytes());
149
}
150
151
// both pref_window_base and pref_window_size should be aligned to 1M
152
if pref_window_base & (BR_WINDOW_ALIGNMENT - 1) == 0
153
&& pref_window_size != 0
154
&& pref_window_size & (BR_WINDOW_ALIGNMENT - 1) == 0
155
{
156
// the top of memory will be one less than a 1MB boundary
157
let limit = pref_window_base + pref_window_size - BR_WINDOW_ALIGNMENT;
158
let low_value = ((pref_window_base as u32) >> BR_MEM_BASE_SHIFT)
159
| (limit as u32)
160
| BR_PREF_MEM_64BIT;
161
self.write_config_register(BR_PREF_MEM_LOW_REG, 0, &low_value.to_le_bytes());
162
let high_base_value = (pref_window_base >> 32) as u32;
163
self.write_config_register(
164
BR_PREF_MEM_BASE_HIGH_REG,
165
0,
166
&high_base_value.to_le_bytes(),
167
);
168
let high_top_value = (limit >> 32) as u32;
169
self.write_config_register(
170
BR_PREF_MEM_LIMIT_HIGH_REG,
171
0,
172
&high_top_value.to_le_bytes(),
173
);
174
}
175
}
176
177
pub fn get_secondary_num(&self) -> u8 {
178
self.bus_range.secondary
179
}
180
181
pub fn get_subordinate_num(&self) -> u8 {
182
self.bus_range.subordinate
183
}
184
}
185
186
fn finalize_window(
187
resources: &mut SystemAllocator,
188
prefetchable: bool,
189
alloc: Alloc,
190
mut base: u64,
191
mut size: u64,
192
) -> std::result::Result<(u64, u64), PciDeviceError> {
193
if size == 0 {
194
// Allocate at least 2MB bridge winodw
195
size = BR_MEM_MINIMUM;
196
}
197
// if base isn't set, allocate a new one
198
if base == u64::MAX {
199
// align size to 1MB
200
if size & (BR_WINDOW_ALIGNMENT - 1) != 0 {
201
size = (size + BR_WINDOW_ALIGNMENT - 1) & BR_WINDOW_MASK;
202
}
203
match resources.allocate_mmio(
204
size,
205
alloc,
206
"pci_bridge_window".to_string(),
207
AllocOptions::new()
208
.prefetchable(prefetchable)
209
.align(BR_WINDOW_ALIGNMENT),
210
) {
211
Ok(addr) => Ok((addr, size)),
212
Err(e) => Err(PciDeviceError::PciBusWindowAllocationFailure(format!(
213
"failed to allocate bridge window: {e}"
214
))),
215
}
216
} else {
217
// align base to 1MB
218
if base & (BR_WINDOW_ALIGNMENT - 1) != 0 {
219
size += base - (base & BR_WINDOW_MASK);
220
// align size to 1MB
221
if size & (BR_WINDOW_ALIGNMENT - 1) != 0 {
222
size = (size + BR_WINDOW_ALIGNMENT - 1) & BR_WINDOW_MASK;
223
}
224
base &= BR_WINDOW_MASK;
225
}
226
Ok((base, size))
227
}
228
}
229
230
impl PciDevice for PciBridge {
231
fn debug_label(&self) -> String {
232
self.device.lock().debug_label()
233
}
234
235
fn preferred_address(&self) -> Option<PciAddress> {
236
self.device.lock().preferred_address()
237
}
238
239
fn allocate_address(
240
&mut self,
241
resources: &mut SystemAllocator,
242
) -> std::result::Result<PciAddress, PciDeviceError> {
243
let address = self.device.lock().allocate_address(resources)?;
244
self.pci_address = Some(address);
245
self.msi_config.lock().set_pci_address(address);
246
Ok(address)
247
}
248
249
fn keep_rds(&self) -> Vec<RawDescriptor> {
250
let mut rds = Vec::new();
251
if let Some(interrupt_evt) = &self.interrupt_evt {
252
rds.extend(interrupt_evt.as_raw_descriptors());
253
}
254
let descriptor = self.msi_config.lock().get_msi_socket();
255
rds.push(descriptor);
256
rds
257
}
258
259
fn assign_irq(&mut self, irq_evt: IrqLevelEvent, pin: PciInterruptPin, irq_num: u32) {
260
self.interrupt_evt = Some(irq_evt);
261
let msi_config_clone = self.msi_config.clone();
262
self.device.lock().clone_interrupt(msi_config_clone);
263
self.config.set_irq(irq_num as u8, pin);
264
}
265
266
fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> {
267
self.config.get_bar_configuration(bar_num)
268
}
269
270
fn register_device_capabilities(&mut self) -> std::result::Result<(), PciDeviceError> {
271
let caps = self.device.lock().get_caps();
272
for (cap, cfg) in caps {
273
self.config
274
.add_capability(&*cap, cfg)
275
.map_err(PciDeviceError::CapabilitiesSetup)?;
276
}
277
278
Ok(())
279
}
280
281
fn read_config_register(&self, reg_idx: usize) -> u32 {
282
let mut data: u32 = self.config.read_reg(reg_idx);
283
self.device.lock().read_config(reg_idx, &mut data);
284
data
285
}
286
287
fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
288
// Suppose kernel won't modify primary/secondary/subordinate bus number,
289
// if it indeed modify, print a warning
290
if reg_idx == BR_BUS_NUMBER_REG {
291
let len = data.len();
292
if offset == 0 && len == 1 && data[0] != self.bus_range.primary {
293
warn!(
294
"kernel modify primary bus number: {} -> {}",
295
self.bus_range.primary, data[0]
296
);
297
} else if offset == 0 && len == 2 {
298
if data[0] != self.bus_range.primary {
299
warn!(
300
"kernel modify primary bus number: {} -> {}",
301
self.bus_range.primary, data[0]
302
);
303
}
304
if data[1] != self.bus_range.secondary {
305
warn!(
306
"kernel modify secondary bus number: {} -> {}",
307
self.bus_range.secondary, data[1]
308
);
309
}
310
} else if offset == 1 && len == 1 && data[0] != self.bus_range.secondary {
311
warn!(
312
"kernel modify secondary bus number: {} -> {}",
313
self.bus_range.secondary, data[0]
314
);
315
} else if offset == 2 && len == 1 && data[0] != self.bus_range.subordinate {
316
warn!(
317
"kernel modify subordinate bus number: {} -> {}",
318
self.bus_range.subordinate, data[0]
319
);
320
}
321
}
322
323
self.device.lock().write_config(reg_idx, offset, data);
324
325
if let Some(res) = self.config.write_reg(reg_idx, offset, data) {
326
self.device.lock().handle_cap_write_result(res);
327
}
328
}
329
330
fn read_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &mut [u8]) {}
331
332
fn write_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &[u8]) {}
333
334
fn get_removed_children_devices(&self) -> Vec<PciAddress> {
335
if !self.device.lock().get_removed_devices().is_empty() {
336
self.pci_bus.lock().get_downstream_devices()
337
} else {
338
Vec::new()
339
}
340
}
341
342
fn get_new_pci_bus(&self) -> Option<Arc<Mutex<PciBus>>> {
343
Some(self.pci_bus.clone())
344
}
345
346
fn configure_bridge_window(
347
&mut self,
348
resources: &mut SystemAllocator,
349
bar_ranges: &[BarRange],
350
) -> std::result::Result<Vec<BarRange>, PciDeviceError> {
351
let address = self
352
.pci_address
353
.expect("allocate_address must be called prior to configure_bridge_window");
354
let mut window_base: u64 = u64::MAX;
355
let mut window_size: u64 = 0;
356
let mut pref_window_base: u64 = u64::MAX;
357
let mut pref_window_size: u64 = 0;
358
let hotplug_implemented = self.device.lock().hotplug_implemented();
359
let hotplugged = self.device.lock().hotplugged();
360
361
if hotplug_implemented || hotplugged {
362
// If bridge is for children hotplug, get desired bridge window size and reserve
363
// it for guest OS use.
364
// If bridge is hotplugged into the system, get the desired bridge window size
365
// from host.
366
let (win_size, pref_win_size) = self.device.lock().get_bridge_window_size();
367
window_size = win_size;
368
pref_window_size = pref_win_size;
369
} else {
370
// Bridge has children connected, get bridge window size from children
371
let mut window_end: u64 = 0;
372
let mut pref_window_end: u64 = 0;
373
374
for &BarRange {
375
addr,
376
size,
377
prefetchable,
378
} in bar_ranges.iter()
379
{
380
if prefetchable {
381
pref_window_base = min(pref_window_base, addr);
382
pref_window_end = max(pref_window_end, addr + size);
383
} else {
384
window_base = min(window_base, addr);
385
window_end = max(window_end, addr + size);
386
}
387
}
388
if window_end > 0 {
389
window_size = window_end - window_base;
390
}
391
if pref_window_end > 0 {
392
pref_window_size = pref_window_end - pref_window_base;
393
}
394
}
395
396
if !hotplugged {
397
// Only static bridge needs to locate their window's position. Hotplugged bridge's
398
// window will be handled by guest kernel.
399
let window = finalize_window(
400
resources,
401
false, // prefetchable
402
Alloc::PciBridgeWindow {
403
bus: address.bus,
404
dev: address.dev,
405
func: address.func,
406
},
407
window_base,
408
window_size,
409
)?;
410
window_base = window.0;
411
window_size = window.1;
412
413
match finalize_window(
414
resources,
415
true, // prefetchable
416
Alloc::PciBridgePrefetchWindow {
417
bus: address.bus,
418
dev: address.dev,
419
func: address.func,
420
},
421
pref_window_base,
422
pref_window_size,
423
) {
424
Ok(pref_window) => {
425
pref_window_base = pref_window.0;
426
pref_window_size = pref_window.1;
427
}
428
Err(e) => {
429
warn!("failed to allocate PCI bridge prefetchable window: {}", e);
430
}
431
}
432
} else {
433
// 0 is Ok here because guest will relocate the bridge window
434
if window_size > 0 {
435
window_base = 0;
436
}
437
if pref_window_size > 0 {
438
pref_window_base = 0;
439
}
440
}
441
442
self.write_bridge_window(
443
window_base as u32,
444
window_size as u32,
445
pref_window_base,
446
pref_window_size,
447
);
448
449
let mut windows = Vec::new();
450
if window_size > 0 {
451
windows.push(BarRange {
452
addr: window_base,
453
size: window_size,
454
prefetchable: false,
455
})
456
}
457
if pref_window_size > 0 {
458
windows.push(BarRange {
459
addr: pref_window_base,
460
size: pref_window_size,
461
prefetchable: true,
462
})
463
}
464
Ok(windows)
465
}
466
467
fn set_subordinate_bus(&mut self, bus_no: u8) {
468
let bus_reg = self.read_config_register(BR_BUS_NUMBER_REG);
469
// Keep the maxmium bus number here because this bridge could have reserved
470
// subordinate bus number earlier
471
let subordinate_bus = u8::max((bus_reg >> (BR_BUS_SUBORDINATE_OFFSET * 8)) as u8, bus_no);
472
self.write_config_register(
473
BR_BUS_NUMBER_REG,
474
BR_BUS_SUBORDINATE_OFFSET as u64,
475
&[subordinate_bus],
476
);
477
}
478
479
fn destroy_device(&mut self) {
480
self.msi_config.lock().destroy()
481
}
482
}
483
484
impl Suspendable for PciBridge {}
485
486