acpi/aml/
resources.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Utilities for encoding various resource types into ACPI Machine Language (AML).
5
6use super::objects::*;
7
8/// An AML resource.
9pub trait ResourceObject {
10    fn append_to_vec(&self, byte_stream: &mut Vec<u8>);
11
12    fn to_bytes(&self) -> Vec<u8> {
13        let mut byte_stream = Vec::new();
14        self.append_to_vec(&mut byte_stream);
15        byte_stream
16    }
17}
18
19/// A 32-bit, fixed-address AML memory resource.
20pub struct Memory32Fixed {
21    is_writeable: bool,
22    base_address: u32,
23    length: u32,
24}
25
26impl Memory32Fixed {
27    /// Construct a new [`Memory32Fixed`].
28    pub fn new(base_address: u32, length: u32, is_writeable: bool) -> Self {
29        Self {
30            is_writeable,
31            base_address,
32            length,
33        }
34    }
35}
36
37impl ResourceObject for Memory32Fixed {
38    fn append_to_vec(&self, byte_stream: &mut Vec<u8>) {
39        byte_stream.extend_from_slice(&[0x86, 9, 0]);
40        byte_stream.push(if self.is_writeable { 1 } else { 0 });
41        byte_stream.extend_from_slice(&self.base_address.to_le_bytes());
42        byte_stream.extend_from_slice(&self.length.to_le_bytes());
43    }
44}
45
46#[repr(u8)]
47#[derive(Copy, Clone, Debug)]
48/// Attributes for AML memory resources.
49pub enum MemoryAttribute {
50    Memory = 0,
51    _Reserved = 8,
52    _Acpi = 0x10,
53    _Nvs = 0x18,
54}
55
56#[repr(u8)]
57#[derive(Copy, Clone, Debug)]
58/// Cache types for AML memory resources.
59pub enum MemoryCacheType {
60    _NonCacheable = 0,
61    Cacheable = 2,
62    _CacheableWriteCombining = 4,
63    _CacheableAndPrefetchable = 6,
64}
65
66/// A 32-bit AML memory resource.
67pub struct DwordMemory {
68    pub length: u32,
69    pub translation_offset: u32,
70    pub address_max: u32,
71    pub address_min: u32,
72    pub granularity: u32,
73    pub attributes: MemoryAttribute,
74    pub cacheability: MemoryCacheType,
75    pub is_writeable: bool,
76    pub is_max_address_fixed: bool,
77    pub is_min_address_fixed: bool,
78    pub is_subtractive_decode: bool,
79}
80
81impl ResourceObject for DwordMemory {
82    fn append_to_vec(&self, byte_stream: &mut Vec<u8>) {
83        byte_stream.extend_from_slice(&[0x87, 0x17, 0, 0]);
84        byte_stream.push(
85            if self.is_subtractive_decode { 2 } else { 0 }
86                | if self.is_min_address_fixed { 4 } else { 0 }
87                | if self.is_max_address_fixed { 8 } else { 0 },
88        );
89        byte_stream.push(
90            if self.is_writeable { 1 } else { 0 } | self.cacheability as u8 | self.attributes as u8,
91        );
92        byte_stream.extend_from_slice(&self.granularity.to_le_bytes());
93        byte_stream.extend_from_slice(&self.address_min.to_le_bytes());
94        byte_stream.extend_from_slice(&self.address_max.to_le_bytes());
95        byte_stream.extend_from_slice(&self.translation_offset.to_le_bytes());
96        byte_stream.extend_from_slice(&self.length.to_le_bytes());
97    }
98}
99
100impl DwordMemory {
101    /// Construct a new [`DwordMemory`].
102    pub fn new(address: u32, length: u32) -> Self {
103        assert!(address as u64 + length as u64 - 1 <= u32::MAX as u64);
104        Self {
105            length,
106            translation_offset: 0,
107            address_max: address + (length - 1),
108            address_min: address,
109            granularity: 0,
110            attributes: MemoryAttribute::Memory,
111            cacheability: MemoryCacheType::Cacheable,
112            is_writeable: true,
113            is_max_address_fixed: true,
114            is_min_address_fixed: true,
115            is_subtractive_decode: false,
116        }
117    }
118}
119
120/// A 64-bit AML memory resource.
121pub struct QwordMemory {
122    pub is_io_backed: bool,
123    pub attributes: MemoryAttribute,
124    pub cacheability: MemoryCacheType,
125    pub is_writeable: bool,
126    pub min_address: u64,
127    pub max_address: u64,
128    pub length: u64,
129}
130
131impl QwordMemory {
132    /// Construct a new [`QwordMemory`].
133    pub fn new(address: u64, length: u64) -> Self {
134        assert!(address as u128 + length as u128 - 1 <= u64::MAX as u128);
135        Self {
136            is_io_backed: false,
137            attributes: MemoryAttribute::Memory,
138            cacheability: MemoryCacheType::Cacheable,
139            is_writeable: true,
140            min_address: address,
141            max_address: address + (length - 1),
142            length,
143        }
144    }
145}
146
147impl ResourceObject for QwordMemory {
148    fn append_to_vec(&self, byte_stream: &mut Vec<u8>) {
149        byte_stream.extend_from_slice(&[0x8a, 0x2b, 0, 0, 0xc]);
150        byte_stream.push(
151            if self.is_io_backed { 0x20 } else { 0 }
152                | self.attributes as u8
153                | self.cacheability as u8
154                | if self.is_writeable { 1 } else { 0 },
155        );
156        byte_stream.extend_from_slice(&(0_u64).to_le_bytes()); // granularity
157        byte_stream.extend_from_slice(&self.min_address.to_le_bytes());
158        byte_stream.extend_from_slice(&self.max_address.to_le_bytes());
159        byte_stream.extend_from_slice(&(0_u64).to_le_bytes()); // translation offset
160        byte_stream.extend_from_slice(&self.length.to_le_bytes());
161    }
162}
163
164/// An ACPI bus number.
165pub struct BusNumber {
166    pub attributes: MemoryAttribute,
167    pub min_address: u16,
168    pub max_address: u16,
169    pub length: u16,
170}
171
172impl BusNumber {
173    /// Constructs a new bus number.
174    pub fn new(address: u16, length: u16) -> Self {
175        Self {
176            attributes: MemoryAttribute::Memory,
177            min_address: address,
178            max_address: address + (length - 1),
179            length,
180        }
181    }
182}
183
184impl ResourceObject for BusNumber {
185    fn append_to_vec(&self, byte_stream: &mut Vec<u8>) {
186        byte_stream.extend_from_slice(&[0x88, 0x0d, 0, 2, 0x0c]);
187        byte_stream.push(0);
188        byte_stream.extend_from_slice(&(0u16).to_le_bytes()); // granularity
189        byte_stream.extend_from_slice(&self.min_address.to_le_bytes());
190        byte_stream.extend_from_slice(&self.max_address.to_le_bytes());
191        byte_stream.extend_from_slice(&(0u16).to_le_bytes()); // translation offset
192        byte_stream.extend_from_slice(&self.length.to_le_bytes());
193    }
194}
195
196/// An ACPI interrupt.
197pub struct Interrupt {
198    pub is_wake_capable: bool,
199    pub is_shared: bool,
200    pub is_low_polarity: bool,
201    pub is_edge_triggered: bool,
202    pub is_consumer: bool,
203    number: u32,
204}
205
206impl Interrupt {
207    /// Construct a new [`Interrupt`].
208    pub fn new(number: u32) -> Self {
209        Self {
210            is_wake_capable: false,
211            is_shared: false,
212            is_low_polarity: false,
213            is_edge_triggered: false,
214            is_consumer: true,
215            number,
216        }
217    }
218}
219
220impl ResourceObject for Interrupt {
221    fn append_to_vec(&self, byte_stream: &mut Vec<u8>) {
222        byte_stream.extend_from_slice(&[0x89, 6, 0]);
223        byte_stream.push(
224            if self.is_wake_capable { 0x10 } else { 0 }
225                | if self.is_shared { 8 } else { 0 }
226                | if self.is_low_polarity { 4 } else { 0 }
227                | if self.is_edge_triggered { 2 } else { 0 }
228                | if self.is_consumer { 1 } else { 0 },
229        );
230        byte_stream.push(1);
231        byte_stream.extend_from_slice(&self.number.to_le_bytes());
232    }
233}
234
235/// An ACPI IO port.
236pub struct IoPort {
237    pub is_16bit_aware: bool,
238    pub base_address: u16,
239    pub end_address: u16,
240    pub alignment: u8,
241    pub length: u8,
242}
243
244impl IoPort {
245    /// Construct a new [`IoPort`].
246    pub fn new(start: u16, end: u16, length: u8) -> Self {
247        Self {
248            is_16bit_aware: true,
249            base_address: start,
250            end_address: end,
251            alignment: 1,
252            length,
253        }
254    }
255}
256
257impl ResourceObject for IoPort {
258    fn append_to_vec(&self, byte_stream: &mut Vec<u8>) {
259        byte_stream.push(0x47);
260        byte_stream.push(if self.is_16bit_aware { 1 } else { 0 });
261        byte_stream.extend_from_slice(&self.base_address.to_le_bytes());
262        byte_stream.extend_from_slice(&self.end_address.to_le_bytes());
263        byte_stream.push(self.alignment);
264        byte_stream.push(self.length);
265    }
266}
267
268/// A group of current ACPI resources (CRS)
269pub struct CurrentResourceSettings {
270    resources: Vec<u8>,
271}
272
273impl CurrentResourceSettings {
274    /// Construct a new [`CurrentResourceSettings`].
275    pub fn new() -> Self {
276        Self { resources: vec![] }
277    }
278
279    /// Add a resource to the collection.
280    pub fn add_resource(&mut self, resource: &impl ResourceObject) {
281        resource.append_to_vec(&mut self.resources);
282    }
283}
284
285impl AmlObject for CurrentResourceSettings {
286    fn append_to_vec(&self, byte_stream: &mut Vec<u8>) {
287        let mut resource_bytes = self.resources.clone();
288        // Add end of resource marker
289        resource_bytes.extend_from_slice(&[0x79, 0]);
290
291        // Generate _CRS buffer
292        let nobj = NamedObject::new(b"_CRS", &Buffer(resource_bytes));
293        nobj.append_to_vec(byte_stream);
294    }
295}
296
297#[cfg(test)]
298mod tests {
299    use super::*;
300    use crate::aml::test_helpers::verify_expected_bytes;
301
302    #[test]
303    fn verify_memory_resource_object() {
304        let memory = Memory32Fixed::new(0xfee00000, 0x1000, true);
305        let mut crs = CurrentResourceSettings::new();
306        crs.add_resource(&memory);
307        let bytes = crs.to_bytes();
308        verify_expected_bytes(
309            &bytes,
310            &[
311                0x08, b'_', b'C', b'R', b'S', 0x11, 17, 0x0a, 14, 0x86, 0x09, 0, 1, 0, 0, 0xe0,
312                0xfe, 0, 0x10, 0, 0, 0x79, 0,
313            ],
314        );
315    }
316
317    #[test]
318    fn verify_dword_memory_resource_object() {
319        let memory = DwordMemory::new(0x10000000, 0x10000000);
320        let mut crs = CurrentResourceSettings::new();
321        crs.add_resource(&memory);
322        let bytes = crs.to_bytes();
323        verify_expected_bytes(
324            &bytes,
325            &[
326                0x08, b'_', b'C', b'R', b'S', 0x11, 0x1f, 0x0a, 0x1c, 0x87, 0x17, 0x00, 0x00, 0x0C,
327                0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0x1F, 0x00,
328                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x79, 0x00,
329            ],
330        );
331    }
332
333    #[test]
334    fn verify_qword_memory_resource_object() {
335        let memory = QwordMemory::new(0x100000000, 0x100000000);
336        let mut crs = CurrentResourceSettings::new();
337        crs.add_resource(&memory);
338        let bytes = crs.to_bytes();
339        verify_expected_bytes(
340            &bytes,
341            &[
342                0x08, b'_', b'C', b'R', b'S', 0x11, 51, 0x0a, 48, 0x8A, 0x2B, 0x00, 0x00, 0x0C,
343                0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
344                0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
345                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x79,
346                0x00,
347            ],
348        );
349    }
350
351    #[test]
352    fn verify_ioport_resource_object() {
353        let mut crs = CurrentResourceSettings::new();
354        crs.add_resource(&IoPort::new(0x3f8, 0x3f8, 8));
355        let bytes = crs.to_bytes();
356        verify_expected_bytes(
357            &bytes,
358            &[
359                0x08, b'_', b'C', b'R', b'S', 0x11, 13, 0x0a, 10, 0x47, 0x01, 0xF8, 0x03, 0xF8,
360                0x03, 0x01, 0x08, 0x79, 0x00,
361            ],
362        );
363    }
364
365    #[test]
366    fn verify_interrupt_resource_object() {
367        let mut crs = CurrentResourceSettings::new();
368        let mut interrupt = Interrupt::new(4);
369        interrupt.is_edge_triggered = true;
370        crs.add_resource(&interrupt);
371        let bytes = crs.to_bytes();
372        verify_expected_bytes(
373            &bytes,
374            &[
375                0x08, b'_', b'C', b'R', b'S', 0x11, 14, 0x0a, 11, 0x89, 0x06, 0x00, 0x03, 0x01,
376                0x04, 0x00, 0x00, 0x00, 0x79, 0x00,
377            ],
378        );
379    }
380
381    #[test]
382    fn verify_resource_object_multi() {
383        let mut crs = CurrentResourceSettings::new();
384        crs.add_resource(&Memory32Fixed::new(0xfee00000, 0x1000, true));
385        crs.add_resource(&Memory32Fixed::new(0xfec00000, 0x1000, true));
386        let bytes = crs.to_bytes();
387        verify_expected_bytes(
388            &bytes,
389            &[
390                0x08, b'_', b'C', b'R', b'S', 0x11, 0x1d, 0x0a, 0x1a, 0x86, 0x09, 0x00, 0x01, 0x00,
391                0x00, 0xe0, 0xFE, 0x00, 0x10, 0x00, 0x00, 0x86, 0x09, 0x00, 0x01, 0x00, 0x00, 0xC0,
392                0xFE, 0x00, 0x10, 0x00, 0x00, 0x79, 0x00,
393            ],
394        );
395    }
396
397    #[test]
398    fn verify_resource_object_multi2() {
399        let mut crs = CurrentResourceSettings::new();
400        crs.add_resource(&IoPort::new(0x3f8, 0x3f8, 8));
401        let mut interrupt = Interrupt::new(4);
402        interrupt.is_edge_triggered = true;
403        crs.add_resource(&interrupt);
404        let bytes = crs.to_bytes();
405        verify_expected_bytes(
406            &bytes,
407            &[
408                0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x16, 0x0A, 0x13, 0x47, 0x01, 0xF8, 0x03, 0xF8,
409                0x03, 0x01, 0x08, 0x89, 0x06, 0x00, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00, 0x79, 0x00,
410            ],
411        );
412    }
413}