pci_routing.rs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. use crate::{
  2. namespace::AmlName,
  3. resource::{self, InterruptPolarity, InterruptTrigger, Resource},
  4. value::Args,
  5. AmlContext,
  6. AmlError,
  7. AmlType,
  8. AmlValue,
  9. };
  10. use alloc::vec::Vec;
  11. use bit_field::BitField;
  12. use core::convert::TryInto;
  13. pub use crate::resource::IrqDescriptor;
  14. #[derive(Clone, Copy, PartialEq, Eq, Debug)]
  15. pub enum Pin {
  16. IntA,
  17. IntB,
  18. IntC,
  19. IntD,
  20. }
  21. #[derive(Debug)]
  22. pub enum PciRouteType {
  23. /// The interrupt is hard-coded to a specific GSI
  24. Gsi(u32),
  25. /// The interrupt is linked to a link object. This object will have `_PRS`, `_CRS` fields and a `_SRS` method
  26. /// that can be used to allocate the interrupt. Note that some platforms (e.g. QEMU's q35 chipset) use link
  27. /// objects but do not support changing the interrupt that it's linked to (i.e. `_SRS` doesn't do anything).
  28. /*
  29. * The actual object itself will just be a `Device`, and we need paths to its children objects to do
  30. * anything useful, so we just store the resolved name here.
  31. */
  32. LinkObject(AmlName),
  33. }
  34. #[derive(Debug)]
  35. pub struct PciRoute {
  36. device: u16,
  37. function: u16,
  38. pin: Pin,
  39. route_type: PciRouteType,
  40. }
  41. /// A `PciRoutingTable` is used to interpret the data in a `_PRT` object, which provides a mapping
  42. /// from PCI interrupt pins to the inputs of the interrupt controller. One of these objects must be
  43. /// present under each PCI root bridge, and consists of a package of packages, each of which describes the
  44. /// mapping of a single PCI interrupt pin.
  45. #[derive(Debug)]
  46. pub struct PciRoutingTable {
  47. entries: Vec<PciRoute>,
  48. }
  49. impl PciRoutingTable {
  50. /// Construct a `PciRoutingTable` from a path to a `_PRT` object. Returns
  51. /// `AmlError::IncompatibleValueConversion` if the value passed is not a package, or if any of the values
  52. /// within it are not packages. Returns the various `AmlError::Prt*` errors if the internal structure of the
  53. /// entries is invalid.
  54. pub fn from_prt_path(prt_path: &AmlName, context: &mut AmlContext) -> Result<PciRoutingTable, AmlError> {
  55. let mut entries = Vec::new();
  56. let prt = context.invoke_method(&prt_path, Args::default())?;
  57. if let AmlValue::Package(ref inner_values) = prt {
  58. for value in inner_values {
  59. if let AmlValue::Package(ref pin_package) = value {
  60. /*
  61. * Each inner package has the following structure:
  62. * | Field | Type | Description |
  63. * | -----------|-----------|-----------------------------------------------------------|
  64. * | Address | Dword | Address of the device. Same format as _ADR objects (high |
  65. * | | | word = #device, low word = #function) |
  66. * | -----------|-----------|-----------------------------------------------------------|
  67. * | Pin | Byte | The PCI pin (0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD) |
  68. * | -----------|-----------|-----------------------------------------------------------|
  69. * | Source | Byte or | Name of the device that allocates the interrupt to which |
  70. * | | NamePath | the above pin is connected. Can be fully qualified, |
  71. * | | | relative, or a simple NameSeg that utilizes namespace |
  72. * | | | search rules. Instead, if this is a byte value of 0, the |
  73. * | | | interrupt is allocated out of the GSI pool, and Source |
  74. * | | | Index should be utilised. |
  75. * | -----------|-----------|-----------------------------------------------------------|
  76. * | Source | Dword | Index that indicates which resource descriptor in the |
  77. * | Index | | resource template of the device pointed to in the Source |
  78. * | | | field this interrupt is allocated from. If the Source |
  79. * | | | is zero, then this field is the GSI number to which the |
  80. * | | | pin is connected. |
  81. * | -----------|-----------|-----------------------------------------------------------|
  82. */
  83. let address = pin_package[0].as_integer(context)?;
  84. let device = address.get_bits(16..32).try_into().map_err(|_| AmlError::PrtInvalidAddress)?;
  85. let function = address.get_bits(0..16).try_into().map_err(|_| AmlError::PrtInvalidAddress)?;
  86. let pin = match pin_package[1].as_integer(context)? {
  87. 0 => Pin::IntA,
  88. 1 => Pin::IntB,
  89. 2 => Pin::IntC,
  90. 3 => Pin::IntD,
  91. _ => return Err(AmlError::PrtInvalidPin),
  92. };
  93. match pin_package[2] {
  94. AmlValue::Integer(0) => {
  95. /*
  96. * The Source Index field contains the GSI number that this interrupt is attached
  97. * to.
  98. */
  99. entries.push(PciRoute {
  100. device,
  101. function,
  102. pin,
  103. route_type: PciRouteType::Gsi(
  104. pin_package[3]
  105. .as_integer(context)?
  106. .try_into()
  107. .map_err(|_| AmlError::PrtInvalidGsi)?,
  108. ),
  109. });
  110. }
  111. AmlValue::String(ref name) => {
  112. let link_object_name =
  113. context.namespace.search_for_level(&AmlName::from_str(name)?, &prt_path)?;
  114. entries.push(PciRoute {
  115. device,
  116. function,
  117. pin,
  118. route_type: PciRouteType::LinkObject(link_object_name),
  119. });
  120. }
  121. _ => return Err(AmlError::PrtInvalidSource),
  122. }
  123. } else {
  124. return Err(AmlError::IncompatibleValueConversion {
  125. current: value.type_of(),
  126. target: AmlType::Package,
  127. });
  128. }
  129. }
  130. Ok(PciRoutingTable { entries })
  131. } else {
  132. Err(AmlError::IncompatibleValueConversion { current: prt.type_of(), target: AmlType::Package })
  133. }
  134. }
  135. /// Get the interrupt input that a given PCI interrupt pin is wired to. Returns `AmlError::PrtNoEntry` if the
  136. /// PRT doesn't contain an entry for the given address + pin.
  137. pub fn route(
  138. &self,
  139. device: u16,
  140. function: u16,
  141. pin: Pin,
  142. context: &mut AmlContext,
  143. ) -> Result<IrqDescriptor, AmlError> {
  144. let entry = self
  145. .entries
  146. .iter()
  147. .find(|entry| {
  148. entry.device == device
  149. && (entry.function == 0xffff || entry.function == function)
  150. && entry.pin == pin
  151. })
  152. .ok_or(AmlError::PrtNoEntry)?;
  153. match entry.route_type {
  154. PciRouteType::Gsi(gsi) => Ok(IrqDescriptor {
  155. is_consumer: true,
  156. trigger: InterruptTrigger::Level,
  157. polarity: InterruptPolarity::ActiveLow,
  158. is_shared: true,
  159. is_wake_capable: false,
  160. irq: gsi,
  161. }),
  162. PciRouteType::LinkObject(ref name) => {
  163. let path = AmlName::from_str("_CRS").unwrap().resolve(name)?;
  164. let link_crs = context.invoke_method(&path, Args::EMPTY)?;
  165. let resources = resource::resource_descriptor_list(&link_crs)?;
  166. match resources.as_slice() {
  167. [Resource::Irq(descriptor)] => Ok(descriptor.clone()),
  168. _ => Err(AmlError::UnexpectedResourceType),
  169. }
  170. }
  171. }
  172. }
  173. }