acpi/aml/
helpers.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Utilities for encoding various types into ACPI Machine Language (AML).
5
6pub fn encode_name(name: &[u8]) -> Vec<u8> {
7    let mut encoded_name: Vec<u8> = Vec::new();
8    let mut segments: Vec<[u8; 4]> = Vec::new();
9    let mut i = 0;
10    if name[0] == b'\\' {
11        encoded_name.push(b'\\');
12        i = 1;
13    }
14    loop {
15        if i == name.len() {
16            break;
17        }
18
19        if name[i] == b'^' {
20            assert!(
21                (encoded_name.is_empty() || encoded_name[encoded_name.len() - 1] == b'^')
22                    && segments.is_empty()
23            );
24            encoded_name.push(b'^');
25            i += 1;
26            continue;
27        }
28
29        assert!((name[i] == b'_') || (name[i] >= b'A' && name[i] <= b'Z'));
30        let mut seg: [u8; 4] = [b'_'; 4];
31        let mut seg_i = 0;
32        loop {
33            let off = i + seg_i;
34            if off == name.len() || name[off] == b'.' {
35                break;
36            }
37
38            assert!(seg_i < 4);
39            assert!(
40                (name[off] == b'_')
41                    || (name[off] >= b'A' && name[off] <= b'Z')
42                    || (name[off] >= b'0' && name[off] <= b'9')
43            );
44            seg[seg_i] = name[off];
45            seg_i += 1
46        }
47        assert!(seg_i > 0);
48        segments.push(seg);
49        // advance past the last segment
50        i += seg_i;
51        if i < name.len() {
52            // advance past the segment divider '.'
53            i += 1;
54        }
55    }
56    if segments.len() > 2 {
57        encoded_name.push(0x2f);
58        encoded_name.push(u8::try_from(segments.len()).unwrap());
59    } else if segments.len() > 1 {
60        encoded_name.push(0x2e);
61    }
62    for seg in segments {
63        encoded_name.extend_from_slice(&seg);
64    }
65    encoded_name
66}
67
68pub fn encode_package_len(len: usize) -> Vec<u8> {
69    assert!(len < (1 << 28) - 1);
70    let mut result: Vec<u8> = Vec::new();
71    if len < 63 {
72        result.push(u8::try_from(len).unwrap() + 1);
73    } else {
74        // To store larger values, the length is stored in little-endian format, with the first byte encoding the
75        // number of additional bytes as well as the least-significant nibble. With a maximum of three additional
76        // bytes plus the extra nibble, the length can be up to 28 bits.
77        let len_bytes = if len < 1 << 12 {
78            2
79        } else if len < 1 << 20 {
80            3
81        } else {
82            4
83        };
84
85        let mut encoded_len: [u8; 4] = [0; 4];
86        let mut rem = len + len_bytes;
87        // byte count is in bits 6 and 7 and low nibble is in bits 0-3.
88        encoded_len[0] =
89            u8::try_from((len_bytes - 1) << 6).unwrap() | u8::try_from(rem & 0xf).unwrap();
90        rem >>= 4;
91        for e in encoded_len.iter_mut().take(len_bytes).skip(1) {
92            *e = u8::try_from(rem & 0xff).unwrap();
93            rem >>= 8;
94        }
95
96        result.extend_from_slice(&encoded_len[..len_bytes]);
97    }
98    result
99}
100
101pub fn encode_integer(value: u64) -> Vec<u8> {
102    let mut byte_stream: Vec<u8> = Vec::new();
103    let end;
104    if value == 0 {
105        // 0 has its own op
106        return vec![0];
107    } else if value == 1 {
108        // 1 has its own op
109        return vec![1];
110    } else if value <= 0xff {
111        byte_stream.push(0xa);
112        end = 1;
113    } else if value <= 0xffff {
114        byte_stream.push(0xb);
115        end = 2;
116    } else if value <= 0xffffffff {
117        byte_stream.push(0xc);
118        end = 4;
119    } else {
120        byte_stream.push(0xe);
121        end = 8;
122    }
123
124    let bytes = value.to_le_bytes();
125    byte_stream.extend_from_slice(&bytes[..end]);
126    byte_stream
127}
128
129pub fn encode_dword(value: u32) -> Vec<u8> {
130    let mut byte_stream = vec![0xcu8];
131    byte_stream.extend_from_slice(&value.to_le_bytes());
132    while byte_stream.len() < 5 {
133        byte_stream.push(0);
134    }
135    byte_stream
136}
137
138pub fn encode_string(value: &[u8]) -> Vec<u8> {
139    let mut byte_stream: Vec<u8> = Vec::new();
140    byte_stream.push(0xd);
141    byte_stream.extend_from_slice(value);
142    byte_stream.push(0);
143    byte_stream
144}
145
146pub fn char_to_hex(value: u8) -> u8 {
147    match value {
148        b'0'..=b'9' => value - b'0',
149        b'a'..=b'f' => 10 + value - b'a',
150        b'A'..=b'F' => 10 + value - b'A',
151        _ => panic!("Unsupported hex char {}", value),
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158    use crate::aml::test_helpers::verify_expected_bytes;
159
160    #[test]
161    fn verify_simple_name() {
162        let bytes = encode_name(b"FOO");
163        verify_expected_bytes(&bytes, b"FOO_");
164    }
165
166    #[test]
167    fn verify_simple_name_with_root() {
168        let bytes = encode_name(b"\\FOO");
169        verify_expected_bytes(&bytes, b"\\FOO_");
170    }
171
172    #[test]
173    fn verify_simple_name_with_prefix() {
174        let bytes = encode_name(b"^FOO");
175        verify_expected_bytes(&bytes, b"^FOO_");
176    }
177
178    #[test]
179    fn verify_dual_name() {
180        let bytes = encode_name(b"FOO.BAR");
181        verify_expected_bytes(&bytes, b"\x2eFOO_BAR_");
182    }
183
184    #[test]
185    fn verify_dual_name_with_root() {
186        let bytes = encode_name(b"\\_SB.FOO");
187        verify_expected_bytes(&bytes, b"\\\x2e_SB_FOO_");
188    }
189
190    #[test]
191    fn verify_multi_name() {
192        let bytes = encode_name(b"FOO.BAR.BAZ.BLAM");
193        verify_expected_bytes(&bytes, b"\x2f\x04FOO_BAR_BAZ_BLAM");
194    }
195}