2
0

pci_routing.rs 8.3 KB

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