Skip to content

Commit 462f92e

Browse files
committed
aml: re-add old resource descriptor and _PRT parsing
1 parent 4e13b65 commit 462f92e

File tree

5 files changed

+1055
-0
lines changed

5 files changed

+1055
-0
lines changed

aml/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ license = "MIT/Apache-2.0"
1212
[dependencies]
1313
spinning_top = "0.3.0"
1414
bit_field = "0.10.2"
15+
byteorder = { version = "1.5.0", default-features = false }

aml/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ extern crate alloc;
77
pub mod namespace;
88
pub mod object;
99
pub mod op_region;
10+
pub mod pci_routing;
11+
pub mod resource;
1012

1113
use alloc::{
1214
boxed::Box,
@@ -1547,6 +1549,15 @@ pub enum AmlError {
15471549
MethodArgCountIncorrect,
15481550

15491551
InvalidOperationOnObject,
1552+
1553+
InvalidResourceDescriptor,
1554+
UnexpectedResourceType,
1555+
1556+
PrtInvalidAddress,
1557+
PrtInvalidPin,
1558+
PrtInvalidGsi,
1559+
PrtInvalidSource,
1560+
PrtNoEntry,
15501561
}
15511562

15521563
/// This trait represents the interface from the `Interpreter` to the hosting kernel, and allows

aml/src/namespace.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,31 @@ impl Namespace {
125125
}
126126
}
127127

128+
pub fn search_for_level(&self, level_name: &AmlName, starting_scope: &AmlName) -> Result<AmlName, AmlError> {
129+
if level_name.search_rules_apply() {
130+
let mut scope = starting_scope.clone().normalize()?;
131+
assert!(scope.is_absolute());
132+
133+
loop {
134+
let name = level_name.resolve(&scope)?;
135+
if let Ok((level, last_seg)) = self.get_level_for_path(&name) {
136+
if level.children.contains_key(&last_seg) {
137+
return Ok(name);
138+
}
139+
}
140+
141+
// If we don't find it, move the scope up a level and search for it there recursively
142+
match scope.parent() {
143+
Ok(parent) => scope = parent,
144+
Err(AmlError::RootHasNoParent) => return Err(AmlError::LevelDoesNotExist(level_name.clone())),
145+
Err(err) => return Err(err),
146+
}
147+
}
148+
} else {
149+
Ok(level_name.clone())
150+
}
151+
}
152+
128153
/// Split an absolute path into a bunch of level segments (used to traverse the level data structure), and a
129154
/// last segment to index into that level. This must not be called on `\\`.
130155
fn get_level_for_path(&self, path: &AmlName) -> Result<(&NamespaceLevel, NameSeg), AmlError> {

aml/src/pci_routing.rs

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

0 commit comments

Comments
 (0)