config.rs92.52%
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::max;16
use std::iter::zip;17
use std::mem::size_of;18
use std::sync::Arc;19
20
use alioth_macros::Layout;21
use parking_lot::RwLock;22
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};23
24
use crate::mem::MemRegionCallback;25
use crate::mem::addressable::SlotBackend;26
use crate::mem::emulated::{Action, ChangeLayout, Mmio};27
use crate::pci::cap::PciCapList;28
use crate::pci::{Bdf, PciBar, Result};29
use crate::{assign_bits, bitflags, consts, impl_mmio_for_zerocopy, mask_bits, mem};30
31
pub trait PciConfigArea: Mmio {32
fn reset(&self) -> Result<()>;33
}34
35
impl Mmio for Box<dyn PciConfigArea> {36
fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {37
Mmio::read(self.as_ref(), offset, size)38
}39
40
fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {41
Mmio::write(self.as_ref(), offset, size, val)42
}43
44
fn size(&self) -> u64 {45
Mmio::size(self.as_ref())46
}47
}48
49
impl SlotBackend for Box<dyn PciConfigArea> {50
fn size(&self) -> u64 {51
Mmio::size(self.as_ref())52
}53
}54
55
bitflags! {56
#[derive(Default, FromBytes, Immutable, KnownLayout, IntoBytes)]57
pub struct Command(u16) {58
INTX_DISABLE = 1 << 10;59
SERR = 1 << 8;60
PARITY_ERR = 1 << 6;61
BUS_MASTER = 1 << 2;62
MEM = 1 << 1;63
IO = 1 << 0;64
WRITABLE_BITS = Self::INTX_DISABLE.bits()65
| Self::SERR.bits()66
| Self::PARITY_ERR.bits()67
| Self::BUS_MASTER.bits()68
| Self::MEM.bits()69
| Self::IO.bits();70
}71
}72
73
bitflags! {74
#[derive(Default, FromBytes, Immutable, KnownLayout, IntoBytes)]75
pub struct Status(u16) {76
PARITY_ERR = 1 << 15;77
SYSTEM_ERR = 1 << 14;78
RECEIVED_MASTER_ABORT = 1 << 13;79
RECEIVED_TARGET_ABORT = 1 << 12;80
SIGNALED_TARGET_ABORT = 1 << 11;81
MASTER_PARITY_ERR = 1 << 8;82
CAP = 1 << 4;83
INTX = 1 << 3;84
IMMEDIATE_READINESS = 1 << 0;85
RW1C_BITS = Self::PARITY_ERR.bits()86
| Self::SYSTEM_ERR.bits()87
| Self::RECEIVED_MASTER_ABORT.bits()88
| Self::RECEIVED_TARGET_ABORT.bits()89
| Self::SIGNALED_TARGET_ABORT.bits()90
| Self::MASTER_PARITY_ERR.bits();91
}92
}93
94
consts! {95
#[derive(Default, FromBytes, Immutable, KnownLayout, IntoBytes)]96
pub struct HeaderType(u8) {97
DEVICE = 0;98
BRIDGE = 1;99
}100
}101
102
#[derive(Debug, Clone, Default, FromBytes, Immutable, KnownLayout, IntoBytes, Layout)]103
#[repr(C, align(8))]104
pub struct CommonHeader {105
pub vendor: u16,106
pub device: u16,107
pub command: Command,108
pub status: Status,109
pub revision: u8,110
pub prog_if: u8,111
pub subclass: u8,112
pub class: u8,113
pub cache_line_size: u8,114
pub latency_timer: u8,115
pub header_type: HeaderType,116
pub bist: u8,117
}118
119
#[derive(Debug, Clone, Default, FromBytes, Immutable, KnownLayout, IntoBytes, Layout)]120
#[repr(C, align(8))]121
pub struct DeviceHeader {122
pub common: CommonHeader,123
pub bars: [u32; 6],124
pub cardbus_cis_pointer: u32,125
pub subsystem_vendor: u16,126
pub subsystem: u16,127
pub expansion_rom: u32,128
pub capability_pointer: u8,129
pub reserved: [u8; 7],130
pub intx_line: u8,131
pub intx_pin: u8,132
pub min_gnt: u8,133
pub max_lat: u8,134
}135
impl_mmio_for_zerocopy!(DeviceHeader);136
137
pub const fn offset_bar(index: usize) -> usize {108x138
DeviceHeader::OFFSET_BARS + index * size_of::<u32>()108x139
}108x140
141
pub const OFFSET_BAR0: usize = offset_bar(0);142
pub const OFFSET_BAR5: usize = offset_bar(5);143
144
pub const BAR_PREFETCHABLE: u32 = 0b1000;145
pub const BAR_MEM64: u32 = 0b0100;146
pub const BAR_MEM32: u32 = 0b0000;147
pub const BAR_IO: u32 = 0b01;148
149
pub const BAR_IO_MASK: u32 = 0b11;150
pub const BAR_MEM_MASK: u32 = 0b1111;151
152
#[derive(Debug)]153
pub enum ConfigHeader {154
Device(DeviceHeader),155
}156
157
impl ConfigHeader {158
pub fn bars(&self) -> [u32; 6] {143x159
match self {143x160
ConfigHeader::Device(header) => header.bars,143x161
}162
}143x163
}164
165
#[derive(Debug)]166
struct UpdateCommandCallback {167
pci_bars: [PciBar; 6],168
bars: [u32; 6],169
changed: Command,170
current: Command,171
}172
173
impl ChangeLayout for UpdateCommandCallback {174
fn change(&self, memory: &mem::Memory) -> mem::Result<()> {12x175
for (i, (pci_bar, bar)) in zip(&self.pci_bars, self.bars).enumerate() {72x176
match pci_bar {72x177
PciBar::Empty => {}36x178
PciBar::Mem(region) => {24x179
if !self.changed.contains(Command::MEM) {24x180
continue;12x181
}12x182
let mut addr = (bar & !BAR_MEM_MASK) as u64;12x183
if bar & BAR_MEM64 == BAR_MEM64 {12x184
addr |= (self.bars[i + 1] as u64) << 32;6x185
}6x186
if self.current.contains(Command::MEM) {12x187
memory.add_region(addr, region.clone())?;6x188
} else {189
memory.remove_region(addr)?;6x190
}191
}192
PciBar::Io(region) => {12x193
if !self.changed.contains(Command::IO) {12x194
continue;6x195
}6x196
let port = (bar & !BAR_IO_MASK) as u16;6x197
if self.current.contains(Command::IO) {6x198
memory.add_io_region(port, region.clone())?;3x199
} else {200
memory.remove_io_region(port)?;3x201
}202
}203
}204
}205
Ok(())12x206
}12x207
}208
209
#[derive(Debug)]210
struct MoveBarCallback {211
bdf: Bdf,212
src: u64,213
dst: u64,214
}215
216
impl ChangeLayout for MoveBarCallback {217
fn change(&self, memory: &mem::Memory) -> mem::Result<()> {9x218
log::debug!(9x219
"{}: moving bar from {:#x} to {:#x}...",220
self.bdf,221
self.src,222
self.dst223
);224
if self.src as u32 & BAR_IO == BAR_IO {9x225
let src_port = self.src & !(BAR_IO_MASK as u64);3x226
let dst_port = self.dst & !(BAR_IO_MASK as u64);3x227
let region = memory.remove_io_region(src_port as u16)?;3x228
memory.add_io_region(dst_port as u16, region)?;3x229
} else {230
let src_addr = self.src & !(BAR_MEM_MASK as u64);6x231
let dst_addr = self.dst & !(BAR_MEM_MASK as u64);6x232
let region = memory.remove_region(src_addr)?;6x233
memory.add_region(dst_addr, region)?;6x234
}235
Ok(())9x236
}9x237
}238
239
#[derive(Debug)]240
pub struct HeaderData {241
header: ConfigHeader,242
bar_masks: [u32; 6],243
bdf: Bdf,244
}245
246
impl HeaderData {247
pub fn set_bar(&mut self, index: usize, val: u32) -> (u32, u32) {24x248
match &mut self.header {24x249
ConfigHeader::Device(header) => {24x250
let mask = self.bar_masks[index];24x251
let old_val = header.bars[index];24x252
let masked_val = mask_bits!(old_val, val, mask);24x253
header.bars[index] = masked_val;24x254
log::info!(24x255
"{}: bar {index}: set to {val:#010x}, update: {old_val:#010x} -> {masked_val:#010x}",256
self.bdf257
);258
(old_val, masked_val)24x259
}260
}261
}24x262
263
pub fn get_bar(&self, index: usize) -> (u32, u32) {18x264
match &self.header {18x265
ConfigHeader::Device(header) => (header.bars[index], self.bar_masks[index]),18x266
}267
}18x268
269
pub fn set_command(&mut self, command: Command) {39x270
match &mut self.header {39x271
ConfigHeader::Device(header) => header.common.command = command,39x272
}273
}39x274
275
fn write_header(78x276
&mut self,78x277
offset: u64,78x278
size: u8,78x279
val: u64,78x280
pci_bars: &[PciBar; 6],78x281
) -> Option<Box<dyn ChangeLayout>> {78x282
let bdf = self.bdf;78x283
let offset = offset as usize;78x284
match &mut self.header {78x285
ConfigHeader::Device(header) => match (offset, size as usize) {78x286
CommonHeader::LAYOUT_COMMAND => {287
let val = Command::from_bits_retain(val as u16);24x288
let old = header.common.command;24x289
assign_bits!(header.common.command, val, Command::WRITABLE_BITS);24x290
let current = header.common.command;24x291
log::trace!("{bdf}: write command: {val:x?}\n {old:x?}\n-> {current:x?}",);24x292
let changed = old ^ current;24x293
if !(changed & (Command::MEM | Command::IO)).is_empty() {24x294
Some(Box::new(UpdateCommandCallback {21x295
pci_bars: pci_bars.clone(),21x296
bars: header.bars,21x297
changed,21x298
current,21x299
}))21x300
} else {301
None3x302
}303
}304
CommonHeader::LAYOUT_STATUS => {305
let val = Status::from_bits_retain(val as u16);12x306
let old = header.common.status;12x307
header.common.status &= !(val & Status::RW1C_BITS);12x308
log::trace!(12x309
"{bdf}: write status: {val:x?}\n {old:x?}\n-> {:x?}",310
header.common.status,311
);312
None12x313
}314
(OFFSET_BAR0..=OFFSET_BAR5, 4) => {42x315
let bar_index = (offset - OFFSET_BAR0) >> 2;42x316
317
let mask = self.bar_masks[bar_index];42x318
let old_val = header.bars[bar_index];42x319
let masked_val = mask_bits!(old_val, val as u32, mask);42x320
if old_val == masked_val {42x321
return None;9x322
}33x323
log::info!(33x324
"{bdf}: updating bar {bar_index}: {old_val:#010x} -> {masked_val:#010x}, mask={mask:#010x}",325
);326
let command = header.common.command;33x327
match &pci_bars[bar_index] {33x328
PciBar::Io(_) if command.contains(Command::IO) => {9x329
Some(Box::new(MoveBarCallback {6x330
bdf,6x331
src: old_val as u64,6x332
dst: masked_val as u64,6x333
}))6x334
}335
PciBar::Mem(_) if command.contains(Command::MEM) => {18x336
let hi_32 = if old_val & BAR_MEM64 == BAR_MEM64 {9x337
(header.bars[bar_index + 1] as u64) << 323x338
} else {339
06x340
};341
Some(Box::new(MoveBarCallback {9x342
bdf,9x343
src: old_val as u64 | hi_32,9x344
dst: masked_val as u64 | hi_32,9x345
}))9x346
}347
PciBar::Empty348
if command.contains(Command::MEM)6x349
&& bar_index > 06x350
&& header.bars[bar_index - 1] & BAR_MEM64 == BAR_MEM64 =>6x351
{352
let lo_32 = header.bars[bar_index - 1] as u64;6x353
Some(Box::new(MoveBarCallback {6x354
bdf,6x355
src: lo_32 | ((old_val as u64) << 32),6x356
dst: lo_32 | ((masked_val as u64) << 32),6x357
}))6x358
}359
_ => {360
header.bars[bar_index] = masked_val;12x361
log::info!(12x362
"{bdf}: bar {bar_index}: write {val:#010x}, update: {old_val:#010x} -> {masked_val:#010x}"363
);364
None12x365
}366
}367
}368
DeviceHeader::LAYOUT_EXPANSION_ROM => {369
log::info!("{bdf}: write {val:#010x} to expansion_rom: ignored");370
None371
}372
_ => {373
log::warn!(374
"{bdf}: unknown write: offset = {offset:#x}, size = {size}, val = {val:#x}"375
);376
None377
}378
},379
}380
}78x381
}382
383
#[derive(Debug)]384
struct BarCallback {385
index: u8,386
header: Arc<RwLock<HeaderData>>,387
}388
389
impl MemRegionCallback for BarCallback {390
fn mapped(&self, addr: u64) -> mem::Result<()> {18x391
let mut header = self.header.write();18x392
let (old, _) = header.get_bar(self.index as usize);18x393
header.set_bar(self.index as usize, addr as u32);18x394
if old & BAR_MEM64 == BAR_MEM64 {18x395
header.set_bar(self.index as usize + 1, (addr >> 32) as u32);6x396
}12x397
Ok(())18x398
}18x399
}400
401
#[derive(Debug)]402
pub struct EmulatedHeader {403
pub data: Arc<RwLock<HeaderData>>,404
pub bars: [PciBar; 6],405
}406
407
impl EmulatedHeader {408
pub fn new(header: ConfigHeader, bars: [PciBar; 6]) -> Self {143x409
let mut bar_masks = [0u32; 6];143x410
let mut mask_hi = 0;143x411
for ((bar, val), mask) in bars.iter().zip(header.bars()).zip(&mut bar_masks) {858x412
match bar {858x413
PciBar::Empty => {603x414
*mask = mask_hi;603x415
mask_hi = 0;603x416
}603x417
PciBar::Io(region) => {60x418
debug_assert_eq!(val & BAR_IO_MASK, BAR_IO);60x419
let size = max(region.size().next_power_of_two(), 1 << 2);60x420
*mask = !(size - 1) as u32;60x421
}422
PciBar::Mem(region) => {195x423
debug_assert_ne!(val & BAR_IO_MASK, BAR_IO);195x424
let size = max(region.size().next_power_of_two(), 4 << 10);195x425
let mask_64 = !(size - 1);195x426
*mask = mask_64 as u32;195x427
if BAR_MEM64 & val == BAR_MEM64 {195x428
mask_hi = (mask_64 >> 32) as u32;135x429
}135x430
}431
}432
}433
434
let data = Arc::new(RwLock::new(HeaderData {143x435
header,143x436
bar_masks,143x437
bdf: Bdf(0),143x438
}));143x439
440
for (index, bar) in bars.iter().enumerate() {858x441
let callbacks = match bar {858x442
PciBar::Empty => continue,603x443
PciBar::Mem(region) => ®ion.callbacks,195x444
PciBar::Io(region) => ®ion.callbacks,60x445
};446
callbacks.lock().push(Box::new(BarCallback {255x447
index: index as u8,255x448
header: data.clone(),255x449
}));255x450
}451
452
Self { data, bars }143x453
}143x454
455
pub fn set_bdf(&self, bdf: Bdf) {24x456
self.data.write().bdf = bdf24x457
}24x458
459
pub fn set_command(&self, command: Command) {36x460
let mut header = self.data.write();36x461
header.set_command(command)36x462
}36x463
}464
465
impl Mmio for EmulatedHeader {466
fn size(&self) -> u64 {467
0x40468
}469
470
fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {133x471
let data = self.data.read();133x472
match &data.header {133x473
ConfigHeader::Device(header) => Mmio::read(header, offset, size),133x474
}475
}133x476
477
fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {78x478
let mut data = self.data.write();78x479
if let Some(callback) = data.write_header(offset, size, val, &self.bars) {78x480
Ok(Action::ChangeLayout { callback })42x481
} else {482
Ok(Action::None)36x483
}484
}78x485
}486
487
impl PciConfigArea for EmulatedHeader {488
fn reset(&self) -> Result<()> {3x489
let mut header = self.data.write();3x490
header.set_command(Command::empty());3x491
Ok(())3x492
}3x493
}494
495
pub trait PciConfig: Mmio {496
fn get_header(&self) -> &EmulatedHeader;497
fn reset(&self) -> Result<()>;498
}499
500
#[derive(Debug)]501
pub struct EmulatedConfig {502
pub header: EmulatedHeader,503
pub caps: PciCapList,504
}505
506
impl Mmio for EmulatedConfig {507
fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {82x508
if offset < size_of::<DeviceHeader>() as u64 {82x509
self.header.read(offset, size)64x510
} else {511
self.caps.read(offset, size)18x512
}513
}82x514
515
fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {12x516
if offset < size_of::<DeviceHeader>() as u64 {12x517
self.header.write(offset, size, val)9x518
} else {519
self.caps.write(offset, size, val)3x520
}521
}12x522
523
fn size(&self) -> u64 {3x524
40963x525
}3x526
}527
528
impl EmulatedConfig {529
pub fn new_device(83x530
mut header: DeviceHeader,83x531
bars: [PciBar; 6],83x532
caps: PciCapList,83x533
) -> EmulatedConfig {83x534
if !caps.is_empty() {83x535
header.common.status |= Status::CAP;3x536
header.capability_pointer = size_of::<DeviceHeader>() as u8;3x537
}80x538
EmulatedConfig {83x539
header: EmulatedHeader::new(ConfigHeader::Device(header), bars),83x540
caps,83x541
}83x542
}83x543
}544
545
impl PciConfig for EmulatedConfig {546
fn get_header(&self) -> &EmulatedHeader {3x547
&self.header3x548
}3x549
550
fn reset(&self) -> Result<()> {3x551
self.header.reset()?;3x552
self.caps.reset()3x553
}3x554
}555
556
#[cfg(test)]557
#[path = "config_test.rs"]558
mod tests;559