瀏覽代碼

Provide interface to PCI routing information

Isaac Woods 5 年之前
父節點
當前提交
cddc2087bb
共有 2 個文件被更改,包括 61 次插入12 次删除
  1. 2 0
      aml/src/lib.rs
  2. 59 12
      aml/src/pci_routing.rs

+ 2 - 0
aml/src/lib.rs

@@ -258,6 +258,8 @@ pub enum AmlError {
     PrtInvalidPin,
     PrtInvalidSource,
     PrtInvalidGsi,
+    /// Produced when the PRT doesn't contain an entry for the requested address + pin
+    PrtNoEntry,
 
     /*
      * Errors produced parsing Resource Descriptors.

+ 59 - 12
aml/src/pci_routing.rs

@@ -1,5 +1,6 @@
 use crate::{
-    namespace::{AmlHandle, AmlName},
+    namespace::AmlName,
+    resource::{self, InterruptPolarity, InterruptTrigger, Resource},
     value::Args,
     AmlContext,
     AmlError,
@@ -8,7 +9,8 @@ use crate::{
 use alloc::vec::Vec;
 use bit_field::BitField;
 use core::convert::TryInto;
-use log::info;
+
+pub use crate::resource::IrqDescriptor;
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub enum Pin {
@@ -18,18 +20,22 @@ pub enum Pin {
     IntD,
 }
 
-#[derive(Clone, Copy, Debug)]
+#[derive(Debug)]
 pub enum PciRouteType {
     /// The interrupt is hard-coded to a specific GSI
-    Gsi(u16),
+    Gsi(u32),
 
     /// The interrupt is linked to a link object. This object will have `_PRS`, `_CRS` fields and a `_SRS` method
     /// that can be used to allocate the interrupt. Note that some platforms (e.g. QEMU's q35 chipset) use link
     /// objects but do not support changing the interrupt that it's linked to (i.e. `_SRS` doesn't do anything).
-    LinkObject(AmlHandle),
+    /*
+     * The actual object itself will just be a `Device`, and we need paths to its children objects to do
+     * anything useful, so we just store the resolved name here.
+     */
+    LinkObject(AmlName),
 }
 
-#[derive(Clone, Copy, Debug)]
+#[derive(Debug)]
 pub struct PciRoute {
     device: u16,
     function: u16,
@@ -54,7 +60,7 @@ impl PciRoutingTable {
     pub fn from_prt_path(prt_path: &AmlName, context: &mut AmlContext) -> Result<PciRoutingTable, AmlError> {
         let mut entries = Vec::new();
 
-        let prt = context.invoke_method(prt_path, Args::default())?;
+        let prt = context.invoke_method(&prt_path, Args::default())?;
         if let AmlValue::Package(ref inner_values) = prt {
             for value in inner_values {
                 if let AmlValue::Package(ref pin_package) = value {
@@ -111,15 +117,13 @@ impl PciRoutingTable {
                             });
                         }
                         AmlValue::String(ref name) => {
-                            let link_object = context.namespace.search(
-                                &AmlName::from_str(name).ok_or(AmlError::ObjectDoesNotExist(name.clone()))?,
-                                prt_path,
-                            )?;
+                            let (link_object_name, _) =
+                                context.namespace.search(&AmlName::from_str(name)?, &prt_path)?;
                             entries.push(PciRoute {
                                 device,
                                 function,
                                 pin,
-                                route_type: PciRouteType::LinkObject(link_object),
+                                route_type: PciRouteType::LinkObject(link_object_name),
                             });
                         }
                         _ => return Err(AmlError::PrtInvalidSource),
@@ -134,4 +138,47 @@ impl PciRoutingTable {
             Err(AmlError::IncompatibleValueConversion)
         }
     }
+
+    /// Get the interrupt input that a given PCI interrupt pin is wired to. Returns `AmlError::PrtNoEntry` if the
+    /// PRT doesn't contain an entry for the given address + pin.
+    pub fn route(
+        &self,
+        device: u16,
+        function: u16,
+        pin: Pin,
+        context: &mut AmlContext,
+    ) -> Result<IrqDescriptor, AmlError> {
+        let entry = self
+            .entries
+            .iter()
+            .find(|entry| {
+                entry.device == device
+                    && (entry.function == 0xffff || entry.function == function)
+                    && entry.pin == pin
+            })
+            .ok_or(AmlError::PrtNoEntry)?;
+
+        match entry.route_type {
+            PciRouteType::Gsi(gsi) => Ok(IrqDescriptor {
+                is_consumer: true,
+                trigger: InterruptTrigger::Level,
+                polarity: InterruptPolarity::ActiveLow,
+                is_shared: true,
+                is_wake_capable: false,
+                irq: gsi,
+            }),
+            PciRouteType::LinkObject(ref name) => {
+                let link_crs =
+                    context.namespace.get_by_path(&AmlName::from_str("_CRS").unwrap().resolve(name)?)?;
+
+                match link_crs {
+                    AmlValue::Name(ref boxed_object) => match resource::resource_descriptor(boxed_object)? {
+                        Resource::Irq(descriptor) => Ok(descriptor),
+                        _ => Err(AmlError::IncompatibleValueConversion),
+                    },
+                    _ => return Err(AmlError::IncompatibleValueConversion),
+                }
+            }
+        }
+    }
 }