acpi/
builder.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use std::num::Wrapping;
5use zerocopy::IntoBytes;
6
7#[derive(Copy, Clone)]
8pub struct OemInfo {
9    pub oem_id: [u8; 6],
10    pub oem_tableid: [u8; 8],
11    pub oem_revision: u32,
12    pub creator_id: [u8; 4],
13    pub creator_revision: u32,
14}
15
16pub struct Table<'a> {
17    revision: u8,
18    oem_tableid: Option<[u8; 8]>,
19    signature: [u8; 4],
20    base: &'a [u8],
21    extra: &'a [&'a [u8]],
22}
23
24impl<'a> Table<'a> {
25    pub fn new<T: acpi_spec::Table>(revision: u8, oem_tableid: Option<[u8; 8]>, t: &'a T) -> Self {
26        Self {
27            revision,
28            oem_tableid,
29            signature: T::SIGNATURE,
30            base: t.as_bytes(),
31            extra: &[],
32        }
33    }
34
35    pub fn new_dyn<T: acpi_spec::Table>(
36        revision: u8,
37        oem_tableid: Option<[u8; 8]>,
38        t: &'a T,
39        extra: &'a [&'a [u8]],
40    ) -> Self {
41        Self {
42            revision,
43            oem_tableid,
44            signature: T::SIGNATURE,
45            base: t.as_bytes(),
46            extra,
47        }
48    }
49
50    pub fn append_to_vec(&self, oem: &OemInfo, v: &mut Vec<u8>) -> usize {
51        let len = size_of::<acpi_spec::Header>()
52            + self.base.len()
53            + self.extra.iter().fold(0, |x, y| x + y.len());
54        let mut header = acpi_spec::Header {
55            signature: self.signature,
56            length: (len as u32).into(),
57            revision: self.revision,
58            checksum: 0,
59            oem_id: oem.oem_id,
60            oem_tableid: self.oem_tableid.unwrap_or(oem.oem_tableid),
61            oem_revision: oem.oem_revision.into(),
62            creator_id: u32::from_le_bytes(oem.creator_id).into(),
63            creator_revision: oem.creator_revision.into(),
64        };
65        let sum = checksum(header.as_bytes())
66            + checksum(self.base.as_bytes())
67            + self.extra.iter().fold(Wrapping(0), |x, y| x + checksum(y));
68        header.checksum = (-sum).0;
69        let orig_len = v.len();
70        v.extend_from_slice(header.as_bytes());
71        v.extend_from_slice(self.base.as_bytes());
72        for x in self.extra.iter() {
73            v.extend_from_slice(x);
74        }
75        assert_eq!(checksum(&v[orig_len..]), Wrapping(0));
76        len
77    }
78
79    pub fn to_vec(&self, oem: &OemInfo) -> Vec<u8> {
80        let mut v = Vec::new();
81        self.append_to_vec(oem, &mut v);
82        v
83    }
84}
85
86pub struct Builder {
87    v: Vec<u8>,
88    tables: Vec<u64>,
89    base_addr: u64,
90    oem: OemInfo,
91}
92
93fn checksum(data: &[u8]) -> Wrapping<u8> {
94    let mut sum = Wrapping(0u8);
95    for i in data.iter() {
96        sum += Wrapping(*i);
97    }
98    sum
99}
100
101impl Builder {
102    pub fn new(base_addr: u64, oem: OemInfo) -> Self {
103        Builder {
104            v: Vec::new(),
105            tables: Vec::new(),
106            base_addr,
107            oem,
108        }
109    }
110
111    pub fn append(&mut self, table: &Table<'_>) -> u64 {
112        let addr = self.base_addr + self.v.len() as u64;
113        let len = table.append_to_vec(&self.oem, &mut self.v);
114        if len % 8 != 0 {
115            self.v.extend_from_slice(&[0; 8][..8 - len % 8]);
116        }
117        if table.signature != *b"XSDT" && table.signature != *b"DSDT" {
118            self.tables.push(addr);
119        }
120        addr
121    }
122
123    pub fn append_raw(&mut self, data: &[u8]) -> u64 {
124        let offset = self.v.len() as u64;
125        let signature = &data[0..4];
126        if signature != *b"XSDT" && signature != *b"DSDT" {
127            self.tables.push(self.base_addr + offset);
128        }
129        self.v.extend_from_slice(data);
130        if data.len() % 8 != 0 {
131            self.v.extend_from_slice(&[0; 8][..8 - data.len() % 8]);
132        }
133        self.base_addr + offset
134    }
135
136    fn rsdp(&self, xsdt: u64) -> acpi_spec::Rsdp {
137        let mut r = acpi_spec::Rsdp {
138            signature: *b"RSD PTR ",                     // [u8; 8], // "RSD PTR "
139            checksum: 0,                                 // u8, // first 20 bytes
140            oem_id: self.oem.oem_id,                     // [u8; 6],
141            revision: 2,                                 // u8, // 2
142            rsdt: 0,                                     // u32,
143            length: size_of::<acpi_spec::Rsdp>() as u32, // u32,
144            xsdt,                                        // u64,
145            xchecksum: 0,                                // u8, // full checksum
146            rsvd: [0, 0, 0],                             // [u8; 3],
147        };
148        let sum = checksum(&r.as_bytes()[0..20]);
149        r.checksum = (-sum).0;
150        let xsum = checksum(r.as_bytes());
151        r.xchecksum = (-xsum).0;
152        assert_eq!(checksum(&r.as_bytes()[0..20]), Wrapping(0));
153        assert_eq!(checksum(r.as_bytes()), Wrapping(0));
154        r
155    }
156
157    pub fn build(mut self) -> (Vec<u8>, Vec<u8>) {
158        let tables = std::mem::take(&mut self.tables);
159        let xsdt = self.append(&Table {
160            signature: *b"XSDT",
161            revision: 1,
162            oem_tableid: None,
163            base: tables.as_slice().as_bytes(),
164            extra: &[],
165        });
166        let rsdp = self.rsdp(xsdt);
167        (rsdp.as_bytes().to_vec(), self.v)
168    }
169}