-
-
Notifications
You must be signed in to change notification settings - Fork 173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PCI & interrupts modifications related to USB #1071
Changes from all commits
53bec8c
d716bd7
66e4ddb
ee27c46
84860ad
8ae8816
68c618e
d6f72b3
aec3809
be56bc6
fb937fa
c12a3e4
fdc8a69
fe4e868
26ef6dd
99f1850
998ddde
dfd7766
5484a98
4085bb5
a88b5fe
9d70cc2
7c34a9d
d9d9202
3c983d1
e3e58ec
64cdae2
a52bf0a
fce5474
f0dfb3d
d833752
2511eb2
eb2595b
37f911c
7948c06
ac87fac
0b58f17
3aa2ddf
5c487ea
4949795
9473e75
2467383
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,14 +3,18 @@ | |
//! Note: while pci currently uses port-io on x86 and mmio on aarch64, | ||
//! x86 may also support memory-based PCI configuration in the future; | ||
//! port-io is the legacy way to access the config space. | ||
//! | ||
//! For context on the various interrupt mechanisms (MSI/MSI-X/INTx): | ||
//! - [this StackExchange reply](https://electronics.stackexchange.com/a/343218) | ||
//! - PCI Express Base Specification, Revision 2, Chapter 6.1 - Interrupt & PME Support | ||
|
||
#![no_std] | ||
#![allow(dead_code)] | ||
|
||
extern crate alloc; | ||
|
||
use log::*; | ||
use core::{fmt, ops::{Deref, DerefMut}, mem::size_of}; | ||
use core::{fmt, ops::{Deref, DerefMut}, mem::size_of, task::Waker}; | ||
use alloc::vec::Vec; | ||
use spin::{Once, Mutex}; | ||
use memory::{PhysicalAddress, BorrowedSliceMappedPages, Mutable, MappedPages, map_frame_range, MMIO_FLAGS}; | ||
|
@@ -24,7 +28,10 @@ use interrupts::InterruptNumber; | |
use port_io::Port; | ||
|
||
#[cfg(target_arch = "aarch64")] | ||
use arm_boards::BOARD_CONFIG; | ||
use { | ||
arm_boards::BOARD_CONFIG, | ||
interrupts::{EoiBehaviour, interrupt_handler, init_pci_interrupts}, | ||
}; | ||
|
||
#[derive(Debug, Copy, Clone)] | ||
/// The span of bytes within a 4-byte chunk that a PCI register occupies. | ||
|
@@ -121,6 +128,8 @@ pci_register!(PCI_INTERRUPT_PIN, 0x3D, 1); | |
pci_register!(PCI_MIN_GRANT, 0x3E, 1); | ||
pci_register!(PCI_MAX_LATENCY, 0x3F, 1); | ||
|
||
const PCI_COMMAND_INT_DISABLED: u16 = 1 << 10; | ||
|
||
#[repr(u8)] | ||
pub enum PciCapability { | ||
Msi = 0x05, | ||
|
@@ -207,6 +216,37 @@ pub fn pci_device_iter() -> Result<impl Iterator<Item = &'static PciDevice>, &'s | |
Ok(get_pci_buses()?.iter().flat_map(|b| b.devices.iter())) | ||
} | ||
|
||
static INTX_DEVICES: Mutex<Vec<&'static PciDevice>> = Mutex::new(Vec::new()); | ||
|
||
// Architecture-independent PCI interrupt handler | ||
// Currently aarch64-only, because legacy interrupts aren't supported on x86 yet. | ||
#[cfg(target_arch = "aarch64")] | ||
interrupt_handler!(pci_int_handler, None, _stack_frame, { | ||
let devices = INTX_DEVICES.lock(); | ||
|
||
for device in &*devices { | ||
if device.pci_get_interrupt_status(true) { | ||
device.pci_enable_interrupts(false); | ||
log::info!("Device {} triggered an interrupt", device.location); | ||
|
||
let reader = device.interrupt_waker.lock(); | ||
match &*reader { | ||
Some(waker) => waker.wake_by_ref(), | ||
None => log::error!("Device doesn't have an interrupt waker!"), | ||
NathanRoyer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
} | ||
|
||
EoiBehaviour::HandlerDidNotSendEoi | ||
}); | ||
|
||
/// Initializes the PCI interrupt handler | ||
pub fn init() -> Result<(), &'static str> { | ||
#[cfg(target_arch = "aarch64")] | ||
init_pci_interrupts([pci_int_handler; 4])?; | ||
|
||
Ok(()) | ||
} | ||
|
||
/// A PCI bus, which contains a list of PCI devices on that bus. | ||
#[derive(Debug)] | ||
|
@@ -286,8 +326,12 @@ fn scan_pci() -> Result<Vec<PciBus>, &'static str> { | |
int_pin: location.pci_read_8(PCI_INTERRUPT_PIN), | ||
int_line: location.pci_read_8(PCI_INTERRUPT_LINE), | ||
location, | ||
interrupt_waker: Mutex::new(None), | ||
}; | ||
|
||
// disable legacy interrupts initially | ||
device.pci_enable_interrupts(false); | ||
|
||
device_list.push(device); | ||
} | ||
} | ||
|
@@ -506,17 +550,20 @@ impl PciLocation { | |
} | ||
|
||
/// Sets the PCI device's command bit 10 to disable legacy interrupts | ||
pub fn pci_set_interrupt_disable_bit(&self) { | ||
pub fn pci_set_interrupt_disable_bit(&self, bit: bool) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should rename this function to be more clear about what it actually does, and use the standard "enable_xyz" terminology. We can mention what it actually does (i.e., set the disable bit) in the function documentation, but as it stands now it's quite difficult to understand. I'll accept this now; kindly include that in the next PR about PCI/USB. 👍 |
||
let command = self.pci_read_16(PCI_COMMAND); | ||
trace!("pci_set_interrupt_disable_bit: PciDevice: {}, read value: {:#x}", self, command); | ||
// trace!("pci_set_interrupt_disable_bit: PciDevice: {}, read value: {:#x}", self, command); | ||
|
||
const INTERRUPT_DISABLE: u16 = 1 << 10; | ||
self.pci_write_16(PCI_COMMAND, command | INTERRUPT_DISABLE); | ||
let new_value = match bit { | ||
true => command | PCI_COMMAND_INT_DISABLED, | ||
false => command & !PCI_COMMAND_INT_DISABLED, | ||
}; | ||
self.pci_write_16(PCI_COMMAND, new_value); | ||
|
||
trace!("pci_set_interrupt_disable_bit: PciDevice: {} read value AFTER WRITE CMD: {:#x}", | ||
/*trace!("pci_set_interrupt_disable_bit: PciDevice: {} read value AFTER WRITE CMD: {:#x}", | ||
self, | ||
self.pci_read_16(PCI_COMMAND), | ||
); | ||
);*/ | ||
} | ||
|
||
/// Explores the PCI config space and returns address of requested capability, if present. | ||
|
@@ -589,6 +636,9 @@ pub struct PciDevice { | |
/// the bus, slot, and function number that locates this PCI device in the bus tree. | ||
pub location: PciLocation, | ||
|
||
/// The handling task for legacy PCI interrupts | ||
pub interrupt_waker: Mutex<Option<Waker>>, | ||
|
||
/// The class code, used to determine device type. | ||
pub class: u8, | ||
/// The subclass code, used to determine device type. | ||
|
@@ -829,6 +879,36 @@ impl PciDevice { | |
|
||
Ok((int_line, int_pin)) | ||
} | ||
|
||
/// Enables/Disables legacy (INTx) interrupts for this device | ||
pub fn pci_enable_interrupts(&self, enable: bool) { | ||
self.pci_set_interrupt_disable_bit(!enable); | ||
} | ||
|
||
/// Reads and returns this PCI device's interrupt status flag. | ||
pub fn pci_get_interrupt_status(&self, check_enabled: bool) -> bool { | ||
const PCI_STATUS_INT: u16 = 1 << 3; | ||
|
||
let interrupt_enabled = || (self.pci_read_16(PCI_COMMAND) & PCI_COMMAND_INT_DISABLED) == 0; | ||
let pending_interrupt = || (self.pci_read_16(PCI_STATUS) & PCI_STATUS_INT ) != 0; | ||
|
||
((!check_enabled) || interrupt_enabled()) && pending_interrupt() | ||
} | ||
|
||
/// Sets a task waker to be used when this device triggers an interrupt | ||
/// | ||
/// Returns the previous interrupt waker for this device, if there was one. | ||
pub fn set_interrupt_waker(&'static self, waker: Waker) -> Option<Waker> { | ||
let mut handle = self.interrupt_waker.lock(); | ||
let prev_value = handle.replace(waker); | ||
|
||
if prev_value.is_none() { | ||
let mut intx_devices = INTX_DEVICES.lock(); | ||
intx_devices.push(self) | ||
} | ||
|
||
prev_value | ||
} | ||
} | ||
|
||
impl Deref for PciDevice { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is also confusing, as upon first glance, the reader of this code would expect that you're just temporarily disabling PCI interrupts here while you handle the interrupt and then re-enabling them later. However, that doesn't happen, so this must mean something else.
We should rename and properly document the
pci_enable_interrupts()
function to be more clear about how and why it should be invoked (e.g., perhaps we just always want to disable this type of interrupt?)