Skip to content

Commit

Permalink
Restructure init of PCI legacy interrupts (INTx); add lspci app (#1081
Browse files Browse the repository at this point in the history
)

* INTX interrupt handler registration for PCI devices is now done
  lazily on both architectures. 
  * On aarch64, the interrupt numbers are known statically.
  * On x86_64, we expect that the driver or module using the
    PCI device knows which interrupt number to use.
    Technically this information can be discovered through ACPI AML,
    but we do not yet support that because it is tedious and difficult.

* Clarify PCI interrupt information functions, e.g., MSI/MSI-x support, etc.

Co-authored-by: Kevin Boos <1139460+kevinaboos@users.noreply.github.com>
  • Loading branch information
NathanRoyer and kevinaboos authored Dec 22, 2023
1 parent e06c84a commit ac5712c
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 75 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions applications/lspci/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "lspci"
version = "0.1.0"
description = "An application which lists currently connected PCI devices."
authors = ["Nathan Royer <nathan.royer.pro@gmail.com>"]
edition = "2021"

[dependencies]
getopts = "0.2.21"
pci = { path = "../../kernel/pci" }
memory = { path = "../../kernel/memory" }
app_io = { path = "../../kernel/app_io" }
75 changes: 75 additions & 0 deletions applications/lspci/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//! This application lists currently connected PCI devices.

#![no_std]

extern crate alloc;
#[macro_use] extern crate app_io;
extern crate getopts;

use alloc::vec::Vec;
use alloc::string::String;
use getopts::Options;
use pci::pci_device_iter;
use memory::PhysicalAddress;

pub fn main(args: Vec<String>) -> isize {
let mut opts = Options::new();
opts.optflag("h", "help", "print this help menu");

let matches = match opts.parse(args) {
Ok(m) => m,
Err(_f) => {
println!("{}", _f);
print_usage(opts);
return -1;
}
};

if matches.opt_present("h") {
print_usage(opts);
return 0;
}

if let Err(msg) = list_pci_devices() {
println!("Error: {}", msg);
}

0
}

fn list_pci_devices() -> Result<(), &'static str> {
for dev in pci_device_iter()? {
println!("{} -- {:04x}:{:04x}", dev.location, dev.vendor_id, dev.device_id);
println!("- class, subclass, prog_if: {:x}, {:x}, {:x}", dev.class, dev.subclass, dev.prog_if);

for bar_idx in 0..6 {
let base = dev.determine_mem_base(bar_idx)?;
if base != PhysicalAddress::zero() {
let size = dev.determine_mem_size(bar_idx);
println!("- BAR {}: base = 0x{:x}, size = 0x{:x}", bar_idx, base, size);
}
}

let support = dev.modern_interrupt_support();
let supports = |b| match b {
true => "supported",
false => "not supported",
};

println!("- MSI interrupts: {}", supports(support.msi));
println!("- MSI-X interrupts: {}", supports(support.msix));
println!("- INTx enabled: {}", dev.pci_intx_enabled());
println!("- INTx status: {}", dev.pci_get_intx_status(false));
}

Ok(())
}


fn print_usage(opts: Options) {
println!("{}", opts.usage(USAGE));
}


const USAGE: &str = "Usage: lspci
An application which lists currently connected PCI devices.";
2 changes: 0 additions & 2 deletions kernel/device_manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ pub fn init(
}
}

pci::init()?;

// Initialize/scan the PCI bus to discover PCI devices
for dev in pci::pci_device_iter()? {
debug!("Found PCI device: {:X?}", dev);
Expand Down
2 changes: 1 addition & 1 deletion kernel/e1000/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl E1000Nic {
//debug!("e1000_nc bar_type: {0}, mem_base: {1}, io_base: {2}", e1000_nc.bar_type, e1000_nc.mem_base, e1000_nc.io_base);

// Get interrupt number
let interrupt_num = match e1000_pci_dev.pci_get_interrupt_info() {
let interrupt_num = match e1000_pci_dev.pci_get_intx_info() {
Ok((Some(irq), _pin)) => (irq + IRQ_BASE_OFFSET) as InterruptNumber,
_ => return Err("e1000: PCI device had no interrupt number (IRQ vector)"),
};
Expand Down
22 changes: 0 additions & 22 deletions kernel/interrupts/src/aarch64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,28 +217,6 @@ pub fn init_pl011_rx_interrupt() -> Result<(), &'static str> {
int_ctrl.set_destination(PL011_RX_SPI, Some(current_cpu()), u8::MAX)
}

/// Sets an interrupt handler for legacy PCI interrupts: INTA, INTB, INTC, INTD
pub fn init_pci_interrupts(handlers: [InterruptHandler; 4]) -> Result<(), &'static str> {
let int_ctrl = SystemInterruptController::get()
.ok_or("SystemInterruptController was not yet initialized")?;
let dst = Some(cpu::bootstrap_cpu().unwrap());

let pci_intx_nums = BOARD_CONFIG.pci_intx.into_iter();
let pci_intx_handlers = handlers.into_iter();

for (int_num, handler) in pci_intx_nums.zip(pci_intx_handlers) {
if let Err(existing_handler) = register_interrupt(int_num, handler) {
if handler as InterruptHandler != existing_handler {
return Err("A different interrupt handler has already been setup for that PCI interrupt");
}
}

int_ctrl.set_destination(int_num, dst, u8::MAX)?;
}

Ok(())
}

/// Registers an interrupt handler at the given IRQ interrupt number.
///
/// The function fails if the interrupt number is reserved or is already in use.
Expand Down
1 change: 1 addition & 0 deletions kernel/pci/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ port_io = { path = "../../libs/port_io" }

[target.'cfg(target_arch = "aarch64")'.dependencies]
arm_boards = { path = "../arm_boards" }
interrupt_controller = { path = "../interrupt_controller" }

[lib]
crate-type = ["rlib"]
Loading

0 comments on commit ac5712c

Please sign in to comment.