ACPI AML decompiler w/ CFG recovery and structured pseudocode
1#include "t.h"
2
3#include "p.h"
4
5static int parse_ok(const uint8_t *b, size_t n, tobi_parse_result *r) {
6 return tobi_parse(b, n, 0, r) && r->root != NULL;
7}
8
9static void fix_checksum(uint8_t *b, size_t n) {
10 unsigned sum = 0;
11 b[9] = 0;
12 for (size_t i = 0; i < n; i++) {
13 sum = (sum + b[i]) & 0xffu;
14 }
15 b[9] = (uint8_t)((256u - sum) & 0xffu);
16}
17
18int t_p(void) {
19 uint8_t m0[] = {0x14,0x0c,'M','T','H','0',0x01,0x70,0x0a,0x2a,0x60,0xa4,0x60};
20 tobi_parse_result r;
21 T_CHECK(parse_ok(m0, sizeof(m0), &r));
22 T_CHECK(r.root->child_len == 1);
23 tobi_ir *m = r.root->child[0];
24 T_CHECK(m->kind == TOBI_IR_METHOD && m->method_args == 1);
25 T_CHECK(m->child_len == 1 && m->child[0]->child_len == 2);
26 T_CHECK(m->child[0]->child[0]->kind == TOBI_IR_STORE);
27 T_CHECK(m->child[0]->child[1]->kind == TOBI_IR_RETURN);
28 tobi_parse_result_free(&r);
29
30 uint8_t table[36 + sizeof(m0)];
31 memset(table, 0, sizeof(table));
32 memcpy(table, "DSDT", 4);
33 uint32_t table_len = (uint32_t)sizeof(table);
34 table[4] = (uint8_t)(table_len & 0xffu);
35 table[5] = (uint8_t)((table_len >> 8) & 0xffu);
36 table[6] = (uint8_t)((table_len >> 16) & 0xffu);
37 table[7] = (uint8_t)((table_len >> 24) & 0xffu);
38 memcpy(table + 36, m0, sizeof(m0));
39 fix_checksum(table, sizeof(table));
40 T_CHECK(parse_ok(table, sizeof(table), &r));
41 T_CHECK(r.meta.is_table && r.meta.aml_off == 36 && r.meta.aml_len == sizeof(m0));
42 T_CHECK(r.meta.checksum_valid && r.meta.checksum_sum == 0);
43 T_CHECK(r.root->child_len == 1 && r.root->child[0]->kind == TOBI_IR_METHOD);
44 T_CHECK(r.root->child[0]->off == 36);
45 T_CHECK(r.diag.len == 0);
46 tobi_parse_result_free(&r);
47
48 uint8_t nested[] = {0x10,0x14,'\\','_','S','B','_',0x5b,0x82,0x0c,'D','E','V','0',0x14,0x06,'M','0','0','0',0x00};
49 T_CHECK(parse_ok(nested, sizeof(nested), &r));
50 T_CHECK(r.root->child[0]->kind == TOBI_IR_SCOPE);
51 T_CHECK(r.root->child[0]->child[0]->child[0]->kind == TOBI_IR_DEVICE);
52 tobi_parse_result_free(&r);
53
54 uint8_t objs[] = {0x08,'B','U','F','0',0x11,0x05,0x0a,0x02,0xaa,0xbb,0x08,'P','K','G','0',0x12,0x06,0x02,0x0a,0x01,0x0a,0x02};
55 T_CHECK(parse_ok(objs, sizeof(objs), &r));
56 T_CHECK(r.root->child_len == 2);
57 T_CHECK(r.root->child[0]->child[0]->kind == TOBI_IR_BUFFER);
58 T_CHECK(r.root->child[1]->child[0]->kind == TOBI_IR_PACKAGE);
59 tobi_parse_result_free(&r);
60
61 uint8_t crs[] = {0x08,'C','R','S','0',0x11,0x05,0x0a,0x02,0x79,0x00};
62 T_CHECK(parse_ok(crs, sizeof(crs), &r));
63 T_CHECK(r.root->child_len == 1);
64 T_CHECK(r.root->child[0]->child[0]->kind == TOBI_IR_BUFFER);
65 T_STR(r.root->child[0]->child[0]->str, "EndTag");
66 T_CHECK(r.root->child[0]->child[0]->child_len == 2);
67 T_CHECK(r.root->child[0]->child[0]->child[1]->kind == TOBI_IR_RESOURCE);
68 T_CHECK(strcmp(r.root->child[0]->child[0]->child[1]->name, "EndTag") == 0);
69 T_STR(r.root->child[0]->child[0]->child[1]->str, "checksum=0x0");
70 tobi_parse_result_free(&r);
71
72 uint8_t io_res[] = {0x11,0x0d,0x0a,0x09,0x47,0x01,0x00,0x10,0xff,0x10,0x01,0x08,0x79,0x00};
73 T_CHECK(parse_ok(io_res, sizeof(io_res), &r));
74 T_CHECK(r.root->child_len == 1);
75 T_CHECK(r.root->child[0]->kind == TOBI_IR_BUFFER);
76 T_CHECK(r.root->child[0]->child_len == 3);
77 T_CHECK(r.root->child[0]->child[1]->kind == TOBI_IR_RESOURCE);
78 T_CHECK(strcmp(r.root->child[0]->child[1]->name, "IO") == 0);
79 T_STR(r.root->child[0]->child[1]->str, "min=0x1000");
80 tobi_parse_result_free(&r);
81
82 uint8_t fld[] = {0x5b,0x80,'R','E','G','0',0x01,0x0a,0x10,0x0a,0x04,0x5b,0x81,0x0b,'R','E','G','0',0x00,'F','L','D','0',0x08};
83 T_CHECK(parse_ok(fld, sizeof(fld), &r));
84 T_CHECK(r.root->child_len == 2);
85 T_CHECK(r.root->child[1]->kind == TOBI_IR_FIELD);
86 T_CHECK(r.root->child[1]->child_len == 1);
87 T_STR(r.root->child[1]->target, "\\REG0");
88 T_CHECK(r.root->child[1]->access_type == 0);
89 T_CHECK(tobi_ns_find_const(&r.ns, "\\REG0") != NULL);
90 const tobi_ns_ent *fe = tobi_ns_find_const(&r.ns, "\\FLD0");
91 T_CHECK(fe != NULL && fe->kind == TOBI_NS_FIELD);
92 T_CHECK(r.root->child[1]->child[0]->bit_off == 0);
93 T_CHECK(r.root->child[1]->child[0]->bit_len == 8);
94 T_CHECK(r.diag.len == 0);
95 tobi_parse_result_free(&r);
96
97 uint8_t ifld[] = {0x5b,0x86,0x0f,'I','D','X','0','D','A','T','0',0x00,'I','F','L','D',0x08};
98 T_CHECK(parse_ok(ifld, sizeof(ifld), &r));
99 T_CHECK(r.root->child_len == 1);
100 T_CHECK(r.root->child[0]->kind == TOBI_IR_FIELD);
101 T_CHECK(strcmp(r.root->child[0]->str, "IndexField") == 0);
102 T_STR(r.root->child[0]->target, "index=\\IDX0 data=\\DAT0");
103 T_CHECK(r.root->child[0]->child_len == 1);
104 T_CHECK(strcmp(r.root->child[0]->child[0]->name, "IFLD") == 0);
105 T_CHECK(r.root->child[0]->child[0]->bit_len == 8);
106 tobi_parse_result_free(&r);
107
108 uint8_t bfld[] = {0x5b,0x87,0x11,'R','E','G','0','B','N','K','0',0x0a,0x01,0x00,'B','F','L','D',0x08};
109 T_CHECK(parse_ok(bfld, sizeof(bfld), &r));
110 T_CHECK(r.root->child_len == 1);
111 T_CHECK(r.root->child[0]->kind == TOBI_IR_FIELD);
112 T_CHECK(strcmp(r.root->child[0]->str, "BankField") == 0);
113 T_STR(r.root->child[0]->target, "region=\\REG0 bank=\\BNK0");
114 T_CHECK(r.root->child[0]->child_len == 2);
115 T_CHECK(r.root->child[0]->child[0]->kind == TOBI_IR_INTEGER);
116 T_CHECK(r.root->child[0]->child[1]->kind == TOBI_IR_FIELD_ELEM);
117 tobi_parse_result_free(&r);
118
119 uint8_t call[] = {
120 0x14,0x08,'C','A','L','0',0x01,0xa4,0x68,
121 0x14,0x0d,'U','S','E','0',0x00,0xa4,'C','A','L','0',0x0a,0x05
122 };
123 T_CHECK(parse_ok(call, sizeof(call), &r));
124 T_CHECK(r.root->child_len == 2);
125 tobi_ir *use = r.root->child[1];
126 T_CHECK(use->child_len == 1 && use->child[0]->child_len == 1);
127 T_CHECK(use->child[0]->child[0]->kind == TOBI_IR_RETURN);
128 T_CHECK(use->child[0]->child[0]->child_len == 1);
129 T_CHECK(use->child[0]->child[0]->child[0]->kind == TOBI_IR_CALL);
130 T_CHECK(use->child[0]->child[0]->child[0]->child_len == 1);
131 tobi_parse_result_free(&r);
132
133 uint8_t ext_call[] = {
134 0x14,0x0d,'U','S','E','0',0x00,0xa4,'C','A','L','X',0x0a,0x05,
135 0x15,'C','A','L','X',0x08,0x01
136 };
137 T_CHECK(parse_ok(ext_call, sizeof(ext_call), &r));
138 T_CHECK(r.root->child_len == 2);
139 use = r.root->child[0];
140 T_CHECK(use->child[0]->child[0]->child[0]->kind == TOBI_IR_CALL);
141 T_CHECK(use->child[0]->child[0]->child[0]->child_len == 1);
142 T_CHECK(r.root->child[1]->kind == TOBI_IR_EXPR);
143 T_CHECK(strcmp(r.root->child[1]->name, "external") == 0);
144 fe = tobi_ns_find_method(&r.ns, "CALX");
145 T_CHECK(fe != NULL && fe->external && fe->args == 1);
146 tobi_parse_result_free(&r);
147
148 uint8_t use_file[] = {0x14,0x0d,'U','S','E','0',0x00,0xa4,'C','A','L','0',0x0a,0x05};
149 uint8_t cal_file[] = {0x14,0x08,'C','A','L','0',0x01,0xa4,0x68};
150 tobi_parse_input inputs[] = {
151 {use_file, sizeof(use_file), "use.aml"},
152 {cal_file, sizeof(cal_file), "cal.aml"}
153 };
154 T_CHECK(tobi_parse_multi(inputs, 2, 0, &r));
155 T_CHECK(r.root->child_len == 2);
156 T_CHECK(strcmp(r.root->child[0]->name, "use.aml") == 0);
157 use = r.root->child[0]->child[0];
158 T_CHECK(use->child[0]->child[0]->kind == TOBI_IR_RETURN);
159 T_CHECK(use->child[0]->child[0]->child[0]->kind == TOBI_IR_CALL);
160 T_CHECK(use->child[0]->child[0]->child[0]->child_len == 1);
161 fe = tobi_ns_find_method(&r.ns, "CAL0");
162 T_CHECK(fe != NULL && !fe->external && fe->args == 1);
163 tobi_parse_result_free(&r);
164
165 uint8_t scoped_call[] = {
166 0x10,0x23,'\\','_','S','B','_',
167 0x14,0x08,'C','A','L','0',0x01,0xa4,0x68,
168 0x5b,0x82,0x12,'D','E','V','0',
169 0x14,0x0d,'U','S','E','0',0x00,0xa4,'C','A','L','0',0x0a,0x05
170 };
171 T_CHECK(parse_ok(scoped_call, sizeof(scoped_call), &r));
172 use = r.root->child[0]->child[0]->child[1]->child[0]->child[0];
173 T_CHECK(use->kind == TOBI_IR_METHOD);
174 tobi_ir *scall = use->child[0]->child[0]->child[0];
175 T_CHECK(scall->kind == TOBI_IR_CALL);
176 T_CHECK(strcmp(scall->path, "\\_SB_.CAL0") == 0);
177 T_CHECK(scall->child_len == 1);
178 tobi_parse_result_free(&r);
179
180 uint8_t fwd[] = {
181 0x14,0x0d,'U','S','E','0',0x00,0xa4,'C','A','L','0',0x0a,0x05,
182 0x14,0x08,'C','A','L','0',0x01,0xa4,0x68
183 };
184 T_CHECK(parse_ok(fwd, sizeof(fwd), &r));
185 T_CHECK(r.root->child_len == 2);
186 use = r.root->child[0];
187 T_CHECK(use->child[0]->child[0]->kind == TOBI_IR_RETURN);
188 T_CHECK(use->child[0]->child[0]->child[0]->kind == TOBI_IR_CALL);
189 T_CHECK(use->child[0]->child[0]->child[0]->child_len == 1);
190 tobi_parse_result_free(&r);
191
192 uint8_t ops[] = {
193 0x14,0x24,'O','P','S','0',0x00,
194 0x5b,0x22,0x0a,0x01,
195 0x8c,'B','U','F','0',0x0a,0x00,'B','Y','T','0',
196 0x70,0x5b,0x30,0x60,
197 0x5b,0x32,0x0a,0x01,0x0c,0x34,0x12,0x00,0x00,0x0a,0x02
198 };
199 T_CHECK(parse_ok(ops, sizeof(ops), &r));
200 T_CHECK(!tobi_diag_has_error(&r.diag));
201 T_CHECK(r.diag.len == 0);
202 tobi_ir *blk = r.root->child[0]->child[0];
203 T_CHECK(blk->child_len == 4);
204 T_CHECK(blk->child[0]->kind == TOBI_IR_EXPR);
205 T_CHECK(strcmp(blk->child[0]->name, "sleep") == 0);
206 T_CHECK(blk->child[1]->kind == TOBI_IR_EXPR);
207 T_CHECK(strcmp(blk->child[1]->name, "create_byte_field") == 0);
208 T_CHECK(blk->child[2]->kind == TOBI_IR_STORE);
209 T_CHECK(blk->child[2]->child[0]->kind == TOBI_IR_INTEGER);
210 T_CHECK(blk->child[3]->kind == TOBI_IR_EXPR);
211 T_CHECK(strcmp(blk->child[3]->name, "fatal") == 0);
212 tobi_parse_result_free(&r);
213
214 uint8_t ext_obj[] = {
215 0x5b,0x88,'D','R','G','0',0x0d,'S','I','G',0x00,0x0d,'O','E','M',0x00,0x0d,'D','A','T',0x00,
216 0x5b,0x23,'M','T','X','0',0x0b,0xff,0xff,
217 0x5b,0x31
218 };
219 T_CHECK(parse_ok(ext_obj, sizeof(ext_obj), &r));
220 T_CHECK(r.diag.len == 0);
221 T_CHECK(r.root->child_len == 3);
222 T_CHECK(r.root->child[0]->kind == TOBI_IR_OPREGION);
223 T_CHECK(strcmp(r.root->child[0]->str, "DataRegion") == 0);
224 T_CHECK(r.root->child[1]->kind == TOBI_IR_EXPR);
225 T_CHECK(strcmp(r.root->child[1]->name, "acquire") == 0);
226 T_CHECK(r.root->child[2]->kind == TOBI_IR_REF);
227 T_CHECK(strcmp(r.root->child[2]->name, "Debug") == 0);
228 tobi_parse_result_free(&r);
229
230 uint8_t more_ext[] = {
231 0x5b,0x20,'T','B','L','0',0x60,
232 0x5b,0x33,
233 0x5b,0x1f,
234 0x0d,'D','S','D','T',0x00,
235 0x0d,'O','E','M',0x00,
236 0x0d,'T','A','B','L','E',0x00,
237 0x0d,'\\',0x00,
238 0x0d,'P','A','R','M',0x00,
239 0x60
240 };
241 T_CHECK(parse_ok(more_ext, sizeof(more_ext), &r));
242 T_CHECK(r.diag.len == 0);
243 T_CHECK(r.root->child_len == 3);
244 T_CHECK(strcmp(r.root->child[0]->name, "load") == 0);
245 T_CHECK(r.root->child[0]->child_len == 2);
246 T_CHECK(strcmp(r.root->child[1]->name, "timer") == 0);
247 T_CHECK(strcmp(r.root->child[2]->name, "load_table") == 0);
248 T_CHECK(r.root->child[2]->child_len == 6);
249 tobi_parse_result_free(&r);
250
251 uint8_t divop[] = {0x78,0x0a,0x07,0x0a,0x02,0x60,0x61};
252 T_CHECK(parse_ok(divop, sizeof(divop), &r));
253 T_CHECK(r.root->child_len == 1);
254 T_CHECK(r.root->child[0]->kind == TOBI_IR_EXPR);
255 T_CHECK(strcmp(r.root->child[0]->name, "div") == 0);
256 T_CHECK(r.root->child[0]->child_len == 4);
257 tobi_parse_result_free(&r);
258
259 uint8_t dup_name[] = {
260 0x08,'D','U','P','0',0x01,
261 0x08,'D','U','P','0',0x00
262 };
263 T_CHECK(tobi_parse(dup_name, sizeof(dup_name), 0, &r));
264 T_CHECK(r.diag.len == 1);
265 T_CHECK(r.diag.items[0].level == TOBI_DIAG_WARN);
266 tobi_parse_result_free(&r);
267 T_CHECK(!tobi_parse(dup_name, sizeof(dup_name), 1, &r));
268 T_CHECK(tobi_diag_has_error(&r.diag));
269 tobi_parse_result_free(&r);
270
271 uint8_t unresolved_ref[] = {0x14,0x0b,'U','N','R','0',0x00,0xa4,'N','O','P','E'};
272 T_CHECK(tobi_parse(unresolved_ref, sizeof(unresolved_ref), 0, &r));
273 T_CHECK(r.diag.len == 1);
274 T_CHECK(r.diag.items[0].level == TOBI_DIAG_WARN);
275 T_STR(r.diag.items[0].msg, "unresolved AML namespace reference");
276 tobi_parse_result_free(&r);
277 T_CHECK(!tobi_parse(unresolved_ref, sizeof(unresolved_ref), 1, &r));
278 T_CHECK(tobi_diag_has_error(&r.diag));
279 tobi_parse_result_free(&r);
280
281 uint8_t alias_cycle[] = {
282 0x06,'A','L','B','0','A','L','A','0',
283 0x06,'A','L','A','0','A','L','B','0'
284 };
285 T_CHECK(tobi_parse(alias_cycle, sizeof(alias_cycle), 0, &r));
286 T_CHECK(r.diag.len >= 1);
287 T_STR(r.diag.items[0].msg, "alias cycle");
288 tobi_parse_result_free(&r);
289 return 0;
290}