ACPI AML decompiler w/ CFG recovery and structured pseudocode
1#include "p.h"
2
3#include "mem.h"
4#include "nm.h"
5#include "op.h"
6#include "rd.h"
7#include "str.h"
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <stdarg.h>
12#include <string.h>
13
14#define MAX_DEPTH 64
15#define MAX_TERMS 200000u
16
17typedef struct parser {
18 tobi_diag_list *diag;
19 int strict;
20 char *scope;
21 tobi_ns *ns;
22 size_t terms;
23} parser;
24
25static void diag(parser *p, tobi_diag_level lvl, size_t off, const char *fmt, ...) {
26 va_list ap;
27 va_start(ap, fmt);
28 va_list cp;
29 va_copy(cp, ap);
30 int n = vsnprintf(NULL, 0, fmt, cp);
31 va_end(cp);
32 if (n < 0) {
33 n = 0;
34 }
35 char *msg = tobi_xmalloc((size_t)n + 1);
36 (void)vsnprintf(msg, (size_t)n + 1, fmt, ap);
37 va_end(ap);
38 tobi_diag_add(p->diag, lvl, off, "%s", msg);
39 free(msg);
40}
41
42static uint32_t le32_at(const uint8_t *d) {
43 return (uint32_t)d[0] | ((uint32_t)d[1] << 8) | ((uint32_t)d[2] << 16) | ((uint32_t)d[3] << 24);
44}
45
46static void copy_trim(char *dst, size_t dst_len, const uint8_t *src, size_t src_len) {
47 size_t n = src_len;
48 while (n > 0 && src[n - 1u] == ' ') {
49 n--;
50 }
51 if (n >= dst_len) {
52 n = dst_len - 1u;
53 }
54 memcpy(dst, src, n);
55 dst[n] = '\0';
56}
57
58static uint32_t hash32(const uint8_t *data, size_t len) {
59 uint32_t h = 2166136261u;
60 for (size_t i = 0; i < len; i++) {
61 h ^= data[i];
62 h *= 16777619u;
63 }
64 return h;
65}
66
67static int is_table_sig(const uint8_t *d) {
68 return memcmp(d, "DSDT", 4) == 0 || memcmp(d, "SSDT", 4) == 0;
69}
70
71static void parser_free(parser *p) {
72 free(p->scope);
73}
74
75static tobi_ns_kind ns_kind_from_ir(tobi_ir_kind kind) {
76 switch (kind) {
77 case TOBI_IR_SCOPE: return TOBI_NS_SCOPE;
78 case TOBI_IR_DEVICE: return TOBI_NS_DEVICE;
79 case TOBI_IR_METHOD: return TOBI_NS_METHOD;
80 case TOBI_IR_PROCESSOR: return TOBI_NS_PROCESSOR;
81 case TOBI_IR_NAME: return TOBI_NS_NAME;
82 case TOBI_IR_ALIAS: return TOBI_NS_ALIAS;
83 case TOBI_IR_OPREGION: return TOBI_NS_OPREGION;
84 case TOBI_IR_FIELD:
85 case TOBI_IR_FIELD_ELEM: return TOBI_NS_FIELD;
86 case TOBI_IR_MUTEX: return TOBI_NS_MUTEX;
87 case TOBI_IR_EVENT: return TOBI_NS_EVENT;
88 default: return TOBI_NS_UNKNOWN;
89 }
90}
91
92static const char *region_space_name(unsigned space) {
93 switch (space) {
94 case 0x00: return "SystemMemory";
95 case 0x01: return "SystemIO";
96 case 0x02: return "PCI_Config";
97 case 0x03: return "EmbeddedControl";
98 case 0x04: return "SMBus";
99 case 0x05: return "SystemCMOS";
100 case 0x06: return "PCIBarTarget";
101 case 0x07: return "IPMI";
102 case 0x08: return "GPIO";
103 case 0x09: return "GenericSerialBus";
104 case 0x0a: return "PCC";
105 default: return "OEM";
106 }
107}
108
109static void ns_declare_path(parser *p, tobi_ns_kind kind, const char *path, size_t off,
110 unsigned args, unsigned flags, int external, const char *target) {
111 if (!p->ns || !path || path[0] == '\0') {
112 return;
113 }
114 int duplicate = 0;
115 (void)tobi_ns_add(p->ns, kind, path, p->scope, off, args, flags, external, target, &duplicate);
116 if (duplicate) {
117 diag(p, p->strict ? TOBI_DIAG_ERROR : TOBI_DIAG_WARN, off,
118 "duplicate namespace object %s at %s", tobi_ns_kind_name(kind), path);
119 }
120}
121
122static void ns_set_region_range(parser *p, const char *path, uint64_t off, uint64_t len) {
123 tobi_ns_ent *e = tobi_ns_find(p->ns, path);
124 if (!e) {
125 return;
126 }
127 e->region_offset = off;
128 e->region_len = len;
129 e->has_region_range = 1;
130}
131
132static void ns_declare_name(parser *p, tobi_ns_kind kind, const char *name, size_t off,
133 unsigned args, unsigned flags, int external, const char *target) {
134 char *path = tobi_nm_resolve(p->scope, name);
135 ns_declare_path(p, kind, path, off, args, flags, external, target);
136 free(path);
137}
138
139static uint8_t table_sum8(const uint8_t *data, size_t len) {
140 uint8_t sum = 0;
141 for (size_t i = 0; i < len; i++) {
142 sum = (uint8_t)(sum + data[i]);
143 }
144 return sum;
145}
146
147static int detect_input(const uint8_t *data, size_t len, int strict, tobi_diag_list *diag_list, tobi_input_meta *meta) {
148 memset(meta, 0, sizeof(*meta));
149 meta->aml_off = 0;
150 meta->aml_len = len;
151 memcpy(meta->signature, "RAW", 4);
152 meta->signature[4] = '\0';
153 memcpy(meta->source, "raw", 4);
154 if (len >= 4 && is_table_sig(data)) {
155 memcpy(meta->signature, data, 4);
156 meta->signature[4] = '\0';
157 meta->is_table = 1;
158 if (len < 36) {
159 tobi_diag_add(diag_list, TOBI_DIAG_ERROR, 0, "ACPI table shorter than 36-byte header");
160 return !strict;
161 }
162 uint32_t table_len = le32_at(data + 4);
163 meta->table_len = table_len;
164 if (table_len < 36 || table_len > len) {
165 tobi_diag_add(diag_list, TOBI_DIAG_ERROR, 4, "invalid ACPI table length %u for file size %zu", table_len, len);
166 return !strict;
167 }
168 meta->revision = data[8];
169 meta->checksum_byte = data[9];
170 copy_trim(meta->oem_id, sizeof(meta->oem_id), data + 10, 6);
171 copy_trim(meta->oem_table_id, sizeof(meta->oem_table_id), data + 16, 8);
172 meta->oem_revision = le32_at(data + 24);
173 copy_trim(meta->creator_id, sizeof(meta->creator_id), data + 28, 4);
174 meta->creator_revision = le32_at(data + 32);
175 meta->header_hash = hash32(data, 36);
176 uint8_t sum = table_sum8(data, table_len);
177 meta->checksum_sum = sum;
178 meta->checksum_valid = sum == 0;
179 if (sum != 0) {
180 tobi_diag_add(diag_list, strict ? TOBI_DIAG_ERROR : TOBI_DIAG_WARN, 9,
181 "ACPI table checksum mismatch: byte sum is 0x%02x", sum);
182 if (strict) {
183 return 0;
184 }
185 }
186 meta->aml_off = 36;
187 meta->aml_len = (size_t)table_len - 36;
188 }
189 if (meta->aml_off <= len && meta->aml_len <= len - meta->aml_off) {
190 meta->aml_hash = hash32(data + meta->aml_off, meta->aml_len);
191 }
192 return 1;
193}
194
195static int pkg_window(parser *p, tobi_rd *rd, size_t op_start, size_t *body_start, size_t *body_len, size_t *whole_len) {
196 size_t len = 0;
197 size_t enc = 0;
198 size_t pkg_start = rd->pos;
199 if (!tobi_rd_pkg_len(rd, &len, &enc)) {
200 diag(p, TOBI_DIAG_ERROR, rd->base + pkg_start, "truncated AML package length");
201 return 0;
202 }
203 if (len < enc) {
204 diag(p, TOBI_DIAG_ERROR, rd->base + pkg_start, "invalid AML package length %zu smaller than encoded length %zu", len, enc);
205 return 0;
206 }
207 size_t end = pkg_start + len;
208 if (end > rd->len) {
209 diag(p, TOBI_DIAG_ERROR, rd->base + pkg_start, "AML package body extends past input");
210 end = rd->len;
211 }
212 *body_start = rd->pos;
213 *body_len = end >= rd->pos ? end - rd->pos : 0;
214 *whole_len = (rd->base + end) - op_start;
215 return 1;
216}
217
218static tobi_ir *unknown_node(parser *p, size_t off, uint32_t raw, size_t len, const char *why) {
219 tobi_ir *n = tobi_ir_new(TOBI_IR_UNKNOWN, off, len);
220 n->raw_op = raw;
221 tobi_ir_set_str(n, why);
222 diag(p, p->strict ? TOBI_DIAG_ERROR : TOBI_DIAG_WARN, off, "unsupported or malformed opcode 0x%x: %s", raw, why);
223 return n;
224}
225
226static int parse_term_list(parser *p, tobi_rd *rd, tobi_ir *parent, unsigned depth);
227static tobi_ir *parse_expr(parser *p, tobi_rd *rd, unsigned depth);
228static tobi_ir *parse_term(parser *p, tobi_rd *rd, unsigned depth);
229
230static int parse_namestring_diag(parser *p, tobi_rd *rd, char **name) {
231 size_t off = tobi_rd_off(rd);
232 if (!tobi_nm_namestring(rd, name)) {
233 diag(p, TOBI_DIAG_ERROR, off, "invalid or truncated AML namestring");
234 return 0;
235 }
236 return 1;
237}
238
239static tobi_ir *parse_block_from_reader(parser *p, tobi_rd *sub, unsigned depth, size_t off) {
240 tobi_ir *block = tobi_ir_new(TOBI_IR_BLOCK, off, tobi_rd_left(sub));
241 (void)parse_term_list(p, sub, block, depth + 1);
242 return block;
243}
244
245static tobi_ir *parse_pkg_named(parser *p, tobi_rd *rd, unsigned depth, tobi_ir_kind kind, const char *kind_name, size_t op_start) {
246 size_t body_start = 0;
247 size_t body_len = 0;
248 size_t whole_len = 0;
249 if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) {
250 return unknown_node(p, op_start, rd->data[op_start - rd->base], 1, "bad package length");
251 }
252 tobi_rd body;
253 tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start);
254 char *name = NULL;
255 if (!parse_namestring_diag(p, &body, &name)) {
256 rd->pos = body_start + body_len;
257 return unknown_node(p, op_start, rd->data[op_start - rd->base], whole_len, kind_name);
258 }
259 char *path = tobi_nm_resolve(p->scope, name);
260 tobi_ir *n = tobi_ir_new(kind, op_start, whole_len);
261 tobi_ir_set_name(n, name);
262 tobi_ir_set_path(n, path);
263 ns_declare_path(p, ns_kind_from_ir(kind), path, op_start, 0, 0, 0, NULL);
264 char *old_scope = p->scope;
265 p->scope = tobi_xstrdup(path);
266 tobi_ir *block = parse_block_from_reader(p, &body, depth, tobi_rd_off(&body));
267 free(p->scope);
268 p->scope = old_scope;
269 tobi_ir_add(n, block);
270 rd->pos = body_start + body_len;
271 free(name);
272 free(path);
273 return n;
274}
275
276static tobi_ir *parse_method(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) {
277 size_t body_start = 0;
278 size_t body_len = 0;
279 size_t whole_len = 0;
280 if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) {
281 return unknown_node(p, op_start, 0x14, 1, "bad method package");
282 }
283 tobi_rd body;
284 tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start);
285 char *name = NULL;
286 if (!parse_namestring_diag(p, &body, &name)) {
287 rd->pos = body_start + body_len;
288 return unknown_node(p, op_start, 0x14, whole_len, "bad method name");
289 }
290 uint8_t flags = 0;
291 if (!tobi_rd_u8(&body, &flags)) {
292 diag(p, TOBI_DIAG_ERROR, tobi_rd_off(&body), "truncated method flags");
293 rd->pos = body_start + body_len;
294 free(name);
295 return unknown_node(p, op_start, 0x14, whole_len, "bad method flags");
296 }
297 char *path = tobi_nm_resolve(p->scope, name);
298 tobi_ir *n = tobi_ir_new(TOBI_IR_METHOD, op_start, whole_len);
299 tobi_ir_set_name(n, name);
300 tobi_ir_set_path(n, path);
301 n->method_args = flags & 0x07u;
302 n->method_serialized = (flags >> 3) & 0x01u;
303 n->method_sync = (flags >> 4) & 0x0fu;
304 ns_declare_path(p, TOBI_NS_METHOD, path, op_start, n->method_args, flags, 0, NULL);
305 char *old_scope = p->scope;
306 p->scope = tobi_xstrdup(path);
307 tobi_ir *block = parse_block_from_reader(p, &body, depth, tobi_rd_off(&body));
308 free(p->scope);
309 p->scope = old_scope;
310 tobi_ir_add(n, block);
311 rd->pos = body_start + body_len;
312 free(name);
313 free(path);
314 return n;
315}
316
317static tobi_ir *parse_name(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) {
318 char *name = NULL;
319 if (!parse_namestring_diag(p, rd, &name)) {
320 return unknown_node(p, op_start, 0x08, 1, "bad NameOp name");
321 }
322 char *path = tobi_nm_resolve(p->scope, name);
323 tobi_ir *n = tobi_ir_new(TOBI_IR_NAME, op_start, 0);
324 tobi_ir_set_name(n, name);
325 tobi_ir_set_path(n, path);
326 ns_declare_path(p, TOBI_NS_NAME, path, op_start, 0, 0, 0, NULL);
327 tobi_ir *value = parse_expr(p, rd, depth + 1);
328 if (value) {
329 tobi_ir_add(n, value);
330 }
331 n->len = tobi_rd_off(rd) - op_start;
332 free(name);
333 free(path);
334 return n;
335}
336
337static tobi_ir *parse_alias(parser *p, tobi_rd *rd, size_t op_start) {
338 char *src = NULL;
339 char *dst = NULL;
340 if (!parse_namestring_diag(p, rd, &src) || !parse_namestring_diag(p, rd, &dst)) {
341 free(src);
342 free(dst);
343 return unknown_node(p, op_start, 0x06, 1, "bad AliasOp names");
344 }
345 tobi_ir *n = tobi_ir_new(TOBI_IR_ALIAS, op_start, tobi_rd_off(rd) - op_start);
346 tobi_ir_set_name(n, dst);
347 tobi_ir_set_str(n, src);
348 char *path = tobi_nm_resolve(p->scope, dst);
349 char *target = tobi_nm_resolve(p->scope, src);
350 tobi_ir_set_path(n, path);
351 ns_declare_path(p, TOBI_NS_ALIAS, path, op_start, 0, 0, 0, target);
352 free(target);
353 free(path);
354 free(src);
355 free(dst);
356 return n;
357}
358
359static tobi_ir *parse_external(parser *p, tobi_rd *rd, size_t op_start) {
360 char *name = NULL;
361 if (!parse_namestring_diag(p, rd, &name)) {
362 return unknown_node(p, op_start, 0x15, 1, "bad ExternalOp name");
363 }
364 uint8_t obj_type = 0;
365 uint8_t argc = 0;
366 if (!tobi_rd_u8(rd, &obj_type) || !tobi_rd_u8(rd, &argc)) {
367 free(name);
368 diag(p, TOBI_DIAG_ERROR, tobi_rd_off(rd), "truncated ExternalOp metadata");
369 return unknown_node(p, op_start, 0x15, tobi_rd_off(rd) - op_start, "bad ExternalOp metadata");
370 }
371 tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, op_start, tobi_rd_off(rd) - op_start);
372 n->raw_op = 0x15;
373 n->value = argc;
374 tobi_ir_set_name(n, "external");
375 char *path = tobi_nm_resolve(p->scope, name);
376 tobi_ir_set_path(n, path);
377 tobi_ir *typ = tobi_ir_new(TOBI_IR_INTEGER, op_start + n->len - 2u, 1);
378 typ->value = obj_type;
379 tobi_ir_add(n, typ);
380 ns_declare_path(p, obj_type == 0x08u ? TOBI_NS_METHOD : TOBI_NS_EXTERNAL,
381 path, op_start, argc & 0x07u, obj_type, 1, NULL);
382 free(path);
383 free(name);
384 return n;
385}
386
387static void apply_field_meta(tobi_ir *e, uint64_t bit_off, uint64_t bit_len, uint8_t flags) {
388 e->bit_off = bit_off;
389 e->bit_len = bit_len;
390 e->access_type = flags & 0x0fu;
391 e->lock_rule = (flags >> 4) & 0x01u;
392 e->update_rule = (flags >> 5) & 0x03u;
393}
394
395static char *field_target_meta(parser *p, const char *prefix, const char *path) {
396 const tobi_ns_ent *e = tobi_ns_find_const(p->ns, path);
397 tobi_sb sb;
398 tobi_sb_init(&sb);
399 tobi_sb_add(&sb, prefix);
400 tobi_sb_add(&sb, path);
401 if (e && e->kind == TOBI_NS_OPREGION) {
402 tobi_sb_printf(&sb, " space=%s", region_space_name(e->flags));
403 if (e->has_region_range) {
404 tobi_sb_printf(&sb, " byte_offset=0x%llx byte_length=0x%llx",
405 (unsigned long long)e->region_offset,
406 (unsigned long long)e->region_len);
407 }
408 }
409 return tobi_sb_take(&sb);
410}
411
412static void parse_field_list(parser *p, tobi_rd *body, tobi_ir *n, uint8_t flags,
413 const tobi_ns_ent *region) {
414 uint64_t bit_pos = 0;
415 while (tobi_rd_left(body) > 0) {
416 size_t foff = tobi_rd_off(body);
417 uint8_t b = 0;
418 if (!tobi_rd_peek(body, &b)) {
419 break;
420 }
421 if (b == 0x00) {
422 (void)tobi_rd_u8(body, &b);
423 size_t bits = 0;
424 size_t enc = 0;
425 if (!tobi_rd_pkg_len(body, &bits, &enc)) {
426 diag(p, TOBI_DIAG_ERROR, foff, "truncated reserved field length");
427 break;
428 }
429 tobi_ir *e = tobi_ir_new(TOBI_IR_FIELD_ELEM, foff, tobi_rd_off(body) - foff);
430 tobi_ir_set_name(e, "<reserved>");
431 e->value = bits;
432 apply_field_meta(e, bit_pos, bits, flags);
433 tobi_ir_add(n, e);
434 bit_pos += bits;
435 continue;
436 }
437 if (b == 0x01) {
438 if (!tobi_rd_skip(body, 3)) {
439 diag(p, TOBI_DIAG_ERROR, foff, "truncated AccessField");
440 break;
441 }
442 tobi_ir *e = tobi_ir_new(TOBI_IR_FIELD_ELEM, foff, tobi_rd_off(body) - foff);
443 tobi_ir_set_name(e, "<access>");
444 tobi_ir_add(n, e);
445 continue;
446 }
447 if (b == 0x02) {
448 (void)tobi_rd_u8(body, &b);
449 char *conn = NULL;
450 if (!tobi_nm_namestring(body, &conn)) {
451 diag(p, TOBI_DIAG_ERROR, foff, "bad ConnectField name");
452 break;
453 }
454 tobi_ir *e = tobi_ir_new(TOBI_IR_FIELD_ELEM, foff, tobi_rd_off(body) - foff);
455 tobi_ir_set_name(e, "<connect>");
456 tobi_ir_set_str(e, conn);
457 tobi_ir_add(n, e);
458 free(conn);
459 continue;
460 }
461 if (b == 0x03) {
462 if (!tobi_rd_skip(body, 4)) {
463 diag(p, TOBI_DIAG_ERROR, foff, "truncated ExtendedAccessField");
464 break;
465 }
466 tobi_ir *e = tobi_ir_new(TOBI_IR_FIELD_ELEM, foff, tobi_rd_off(body) - foff);
467 tobi_ir_set_name(e, "<extended_access>");
468 tobi_ir_add(n, e);
469 continue;
470 }
471 char *fname = NULL;
472 if (!tobi_nm_nameseg(body, &fname)) {
473 diag(p, TOBI_DIAG_ERROR, foff, "bad field element");
474 (void)tobi_rd_skip(body, 1);
475 continue;
476 }
477 size_t bits = 0;
478 size_t enc = 0;
479 if (!tobi_rd_pkg_len(body, &bits, &enc)) {
480 diag(p, TOBI_DIAG_ERROR, foff, "truncated field element length");
481 free(fname);
482 break;
483 }
484 tobi_ir *e = tobi_ir_new(TOBI_IR_FIELD_ELEM, foff, tobi_rd_off(body) - foff);
485 tobi_ir_set_name(e, fname);
486 e->value = bits;
487 apply_field_meta(e, bit_pos, bits, flags);
488 if (n->target) {
489 tobi_sb target;
490 tobi_sb_init(&target);
491 tobi_sb_add(&target, n->target);
492 tobi_sb_printf(&target, " field_bit=%llu field_bytes=%llu",
493 (unsigned long long)bit_pos,
494 (unsigned long long)((bits + 7u) / 8u));
495 if (region && region->has_region_range &&
496 region->region_offset <= UINT64_MAX / 8u &&
497 bit_pos <= UINT64_MAX - (region->region_offset * 8u)) {
498 uint64_t abs_bit = (region->region_offset * 8u) + bit_pos;
499 tobi_sb_printf(&target, " absolute_bit=0x%llx absolute_byte=0x%llx absolute_bytes=0x%llx",
500 (unsigned long long)abs_bit,
501 (unsigned long long)(abs_bit / 8u),
502 (unsigned long long)((bits + 7u) / 8u));
503 }
504 tobi_ir_set_target(e, target.buf);
505 tobi_sb_free(&target);
506 }
507 ns_declare_name(p, TOBI_NS_FIELD, fname, foff, 0, 0, 0, NULL);
508 tobi_ir_add(n, e);
509 bit_pos += bits;
510 free(fname);
511 }
512}
513
514static tobi_ir *parse_field_like(parser *p, tobi_rd *rd, unsigned depth, size_t op_start, uint32_t raw, const char *label) {
515 size_t body_start = 0;
516 size_t body_len = 0;
517 size_t whole_len = 0;
518 if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) {
519 return unknown_node(p, op_start, raw, 2, "bad field package");
520 }
521 tobi_rd body;
522 tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start);
523 char *first = NULL;
524 if (!parse_namestring_diag(p, &body, &first)) {
525 rd->pos = body_start + body_len;
526 return unknown_node(p, op_start, raw, whole_len, "bad field name");
527 }
528 tobi_ir *n = tobi_ir_new(TOBI_IR_FIELD, op_start, whole_len);
529 tobi_ir_set_str(n, label);
530 const tobi_ns_ent *backing_region = NULL;
531 if (raw == 0x5b81u) {
532 tobi_ir_set_name(n, first);
533 char *path = tobi_ns_resolve_path(p->ns, p->scope, first);
534 char *target = field_target_meta(p, "region=", path);
535 tobi_ir_set_target(n, target);
536 backing_region = tobi_ns_find_const(p->ns, path);
537 free(target);
538 free(path);
539 } else if (raw == 0x5b86u) {
540 char *data = NULL;
541 if (!parse_namestring_diag(p, &body, &data)) {
542 free(first);
543 rd->pos = body_start + body_len;
544 return unknown_node(p, op_start, raw, whole_len, "bad IndexField data name");
545 }
546 tobi_sb nm;
547 tobi_sb_init(&nm);
548 tobi_sb_add(&nm, first);
549 tobi_sb_add(&nm, ",");
550 tobi_sb_add(&nm, data);
551 tobi_ir_set_name(n, nm.buf);
552 tobi_sb target;
553 tobi_sb_init(&target);
554 char *idx_path = tobi_ns_resolve_path(p->ns, p->scope, first);
555 char *data_path = tobi_ns_resolve_path(p->ns, p->scope, data);
556 char *idx_meta = field_target_meta(p, "index=", idx_path);
557 tobi_sb_add(&target, idx_meta);
558 tobi_sb_add(&target, " data=");
559 tobi_sb_add(&target, data_path);
560 tobi_ir_set_target(n, target.buf);
561 free(idx_meta);
562 free(idx_path);
563 free(data_path);
564 tobi_sb_free(&target);
565 tobi_sb_free(&nm);
566 free(data);
567 } else {
568 char *bank = NULL;
569 if (!parse_namestring_diag(p, &body, &bank)) {
570 free(first);
571 rd->pos = body_start + body_len;
572 return unknown_node(p, op_start, raw, whole_len, "bad BankField bank name");
573 }
574 tobi_sb nm;
575 tobi_sb_init(&nm);
576 tobi_sb_add(&nm, first);
577 tobi_sb_add(&nm, ",");
578 tobi_sb_add(&nm, bank);
579 tobi_ir_set_name(n, nm.buf);
580 tobi_sb target;
581 tobi_sb_init(&target);
582 char *region_path = tobi_ns_resolve_path(p->ns, p->scope, first);
583 char *bank_path = tobi_ns_resolve_path(p->ns, p->scope, bank);
584 char *region_meta = field_target_meta(p, "region=", region_path);
585 backing_region = tobi_ns_find_const(p->ns, region_path);
586 tobi_sb_add(&target, region_meta);
587 tobi_sb_add(&target, " bank=");
588 tobi_sb_add(&target, bank_path);
589 tobi_ir_set_target(n, target.buf);
590 free(region_meta);
591 free(region_path);
592 free(bank_path);
593 tobi_sb_free(&target);
594 tobi_sb_free(&nm);
595 free(bank);
596 tobi_ir *bank_value = parse_expr(p, &body, depth + 1);
597 if (bank_value) {
598 tobi_ir_add(n, bank_value);
599 }
600 }
601 uint8_t flags = 0;
602 if (!tobi_rd_u8(&body, &flags)) {
603 diag(p, TOBI_DIAG_ERROR, tobi_rd_off(&body), "truncated field flags");
604 }
605 n->value = flags;
606 n->access_type = flags & 0x0fu;
607 n->lock_rule = (flags >> 4) & 0x01u;
608 n->update_rule = (flags >> 5) & 0x03u;
609 parse_field_list(p, &body, n, flags, backing_region);
610 rd->pos = body_start + body_len;
611 free(first);
612 return n;
613}
614
615static tobi_ir *ref_from_name(parser *p, tobi_rd *rd, size_t owner_start, const char *why) {
616 size_t off = tobi_rd_off(rd);
617 char *name = NULL;
618 if (!parse_namestring_diag(p, rd, &name)) {
619 return unknown_node(p, owner_start, 0, tobi_rd_off(rd) - owner_start, why);
620 }
621 tobi_ir *n = tobi_ir_new(TOBI_IR_REF, off, tobi_rd_off(rd) - off);
622 tobi_ir_set_name(n, name);
623 char *path = tobi_ns_resolve_path(p->ns, p->scope, name);
624 tobi_ir_set_path(n, path);
625 free(path);
626 free(name);
627 return n;
628}
629
630static void add_expr_children(parser *p, tobi_rd *rd, unsigned depth, tobi_ir *n, unsigned count) {
631 for (unsigned i = 0; i < count && tobi_rd_left(rd) > 0; i++) {
632 tobi_ir *arg = parse_expr(p, rd, depth + 1);
633 if (arg) {
634 tobi_ir_add(n, arg);
635 }
636 }
637}
638
639static tobi_ir *parse_named_ext_expr(parser *p, tobi_rd *rd, unsigned depth, size_t op_start,
640 uint32_t raw, const char *name, unsigned expr_count,
641 unsigned name_count) {
642 tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, op_start, 0);
643 n->raw_op = raw;
644 tobi_ir_set_name(n, name);
645 add_expr_children(p, rd, depth, n, expr_count);
646 for (unsigned i = 0; i < name_count && tobi_rd_left(rd) > 0; i++) {
647 tobi_ir *ref = ref_from_name(p, rd, op_start, "bad extended opcode namestring");
648 if (ref) {
649 tobi_ir_add(n, ref);
650 }
651 }
652 n->len = tobi_rd_off(rd) - op_start;
653 return n;
654}
655
656static tobi_ir *parse_ext_load(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) {
657 tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, op_start, 0);
658 n->raw_op = 0x5b20u;
659 tobi_ir_set_name(n, "load");
660 tobi_ir *src = ref_from_name(p, rd, op_start, "bad LoadOp source name");
661 if (src) {
662 tobi_ir_add(n, src);
663 }
664 tobi_ir *target = parse_expr(p, rd, depth + 1);
665 if (target) {
666 tobi_ir_add(n, target);
667 }
668 n->len = tobi_rd_off(rd) - op_start;
669 return n;
670}
671
672static tobi_ir *parse_ext_load_table(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) {
673 tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, op_start, 0);
674 n->raw_op = 0x5b1fu;
675 tobi_ir_set_name(n, "load_table");
676 add_expr_children(p, rd, depth, n, 6);
677 n->len = tobi_rd_off(rd) - op_start;
678 return n;
679}
680
681static tobi_ir *parse_ext_pkg_scope(parser *p, tobi_rd *rd, unsigned depth, size_t op_start,
682 uint32_t raw, tobi_ir_kind kind, const char *kind_name,
683 unsigned prefix_bytes) {
684 size_t body_start = 0;
685 size_t body_len = 0;
686 size_t whole_len = 0;
687 if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) {
688 return unknown_node(p, op_start, raw, 2, kind_name);
689 }
690 tobi_rd body;
691 tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start);
692 char *name = NULL;
693 if (!parse_namestring_diag(p, &body, &name)) {
694 rd->pos = body_start + body_len;
695 return unknown_node(p, op_start, raw, whole_len, kind_name);
696 }
697 (void)tobi_rd_skip(&body, prefix_bytes);
698 char *path = tobi_nm_resolve(p->scope, name);
699 tobi_ir *n = tobi_ir_new(kind, op_start, whole_len);
700 tobi_ir_set_name(n, name);
701 tobi_ir_set_path(n, path);
702 tobi_ir_set_str(n, kind_name);
703 ns_declare_path(p, ns_kind_from_ir(kind), path, op_start, 0, raw, 0, NULL);
704 char *old_scope = p->scope;
705 p->scope = tobi_xstrdup(path);
706 tobi_ir *block = parse_block_from_reader(p, &body, depth, tobi_rd_off(&body));
707 free(p->scope);
708 p->scope = old_scope;
709 tobi_ir_add(n, block);
710 rd->pos = body_start + body_len;
711 free(path);
712 free(name);
713 return n;
714}
715
716static tobi_ir *parse_ext(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) {
717 uint8_t ext = 0;
718 if (!tobi_rd_u8(rd, &ext)) {
719 return unknown_node(p, op_start, 0x5b, 1, "truncated extended opcode");
720 }
721 uint32_t raw = 0x5b00u | ext;
722 switch (ext) {
723 case 0x82:
724 return parse_pkg_named(p, rd, depth, TOBI_IR_DEVICE, "DeviceOp", op_start);
725 case 0x83: {
726 size_t body_start = 0;
727 size_t body_len = 0;
728 size_t whole_len = 0;
729 if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) {
730 return unknown_node(p, op_start, raw, 2, "bad ProcessorOp package");
731 }
732 tobi_rd body;
733 tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start);
734 char *name = NULL;
735 if (!parse_namestring_diag(p, &body, &name)) {
736 rd->pos = body_start + body_len;
737 return unknown_node(p, op_start, raw, whole_len, "bad processor name");
738 }
739 uint8_t proc_id = 0;
740 uint32_t pblk = 0;
741 uint8_t pblk_len = 0;
742 (void)tobi_rd_u8(&body, &proc_id);
743 (void)tobi_rd_u32(&body, &pblk);
744 (void)tobi_rd_u8(&body, &pblk_len);
745 char *path = tobi_nm_resolve(p->scope, name);
746 tobi_ir *n = tobi_ir_new(TOBI_IR_PROCESSOR, op_start, whole_len);
747 tobi_ir_set_name(n, name);
748 tobi_ir_set_path(n, path);
749 ns_declare_path(p, TOBI_NS_PROCESSOR, path, op_start, 0, 0, 0, NULL);
750 n->value = proc_id;
751 char *old_scope = p->scope;
752 p->scope = tobi_xstrdup(path);
753 tobi_ir_add(n, parse_block_from_reader(p, &body, depth, tobi_rd_off(&body)));
754 free(p->scope);
755 p->scope = old_scope;
756 rd->pos = body_start + body_len;
757 free(path);
758 free(name);
759 return n;
760 }
761 case 0x80: {
762 char *name = NULL;
763 if (!parse_namestring_diag(p, rd, &name)) {
764 return unknown_node(p, op_start, raw, 2, "bad OpRegion name");
765 }
766 uint8_t space = 0;
767 if (!tobi_rd_u8(rd, &space)) {
768 free(name);
769 diag(p, TOBI_DIAG_ERROR, tobi_rd_off(rd), "truncated OpRegion space");
770 return unknown_node(p, op_start, raw, 2, "bad OpRegion");
771 }
772 tobi_ir *n = tobi_ir_new(TOBI_IR_OPREGION, op_start, 0);
773 tobi_ir_set_name(n, name);
774 char *path = tobi_nm_resolve(p->scope, name);
775 tobi_ir_set_path(n, path);
776 ns_declare_path(p, TOBI_NS_OPREGION, path, op_start, 0, space, 0, NULL);
777 n->value = space;
778 tobi_ir_set_str(n, region_space_name(space));
779 tobi_ir *off = parse_expr(p, rd, depth + 1);
780 tobi_ir *len = parse_expr(p, rd, depth + 1);
781 if (off) {
782 tobi_ir_add(n, off);
783 }
784 if (len) {
785 tobi_ir_add(n, len);
786 }
787 if (n->child_len >= 2 && n->child[0]->kind == TOBI_IR_INTEGER && n->child[1]->kind == TOBI_IR_INTEGER) {
788 ns_set_region_range(p, path, n->child[0]->value, n->child[1]->value);
789 }
790 n->len = tobi_rd_off(rd) - op_start;
791 free(path);
792 free(name);
793 return n;
794 }
795 case 0x81:
796 return parse_field_like(p, rd, depth, op_start, raw, "Field");
797 case 0x86:
798 return parse_field_like(p, rd, depth, op_start, raw, "IndexField");
799 case 0x87:
800 return parse_field_like(p, rd, depth, op_start, raw, "BankField");
801 case 0x84:
802 return parse_ext_pkg_scope(p, rd, depth, op_start, raw, TOBI_IR_SCOPE, "PowerResOp", 2);
803 case 0x85:
804 return parse_ext_pkg_scope(p, rd, depth, op_start, raw, TOBI_IR_SCOPE, "ThermalZoneOp", 0);
805 case 0x12:
806 return parse_named_ext_expr(p, rd, depth, op_start, raw, "cond_ref_of", 1, 1);
807 case 0x13:
808 return parse_named_ext_expr(p, rd, depth, op_start, raw, "create_field", 3, 1);
809 case 0x1f:
810 return parse_ext_load_table(p, rd, depth, op_start);
811 case 0x20:
812 return parse_ext_load(p, rd, depth, op_start);
813 case 0x21:
814 return parse_named_ext_expr(p, rd, depth, op_start, raw, "stall", 1, 0);
815 case 0x22:
816 return parse_named_ext_expr(p, rd, depth, op_start, raw, "sleep", 1, 0);
817 case 0x23:
818 return parse_named_ext_expr(p, rd, depth, op_start, raw, "acquire", 2, 0);
819 case 0x24:
820 return parse_named_ext_expr(p, rd, depth, op_start, raw, "signal", 1, 0);
821 case 0x25:
822 return parse_named_ext_expr(p, rd, depth, op_start, raw, "wait", 2, 0);
823 case 0x26:
824 return parse_named_ext_expr(p, rd, depth, op_start, raw, "reset", 1, 0);
825 case 0x27:
826 return parse_named_ext_expr(p, rd, depth, op_start, raw, "release", 1, 0);
827 case 0x28:
828 return parse_named_ext_expr(p, rd, depth, op_start, raw, "from_bcd", 2, 0);
829 case 0x29:
830 return parse_named_ext_expr(p, rd, depth, op_start, raw, "to_bcd", 2, 0);
831 case 0x2a:
832 return parse_named_ext_expr(p, rd, depth, op_start, raw, "unload", 1, 0);
833 case 0x30: {
834 tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, op_start, tobi_rd_off(rd) - op_start);
835 n->value = 2;
836 return n;
837 }
838 case 0x31: {
839 tobi_ir *n = tobi_ir_new(TOBI_IR_REF, op_start, tobi_rd_off(rd) - op_start);
840 n->raw_op = raw;
841 tobi_ir_set_name(n, "Debug");
842 return n;
843 }
844 case 0x32:
845 return parse_named_ext_expr(p, rd, depth, op_start, raw, "fatal", 3, 0);
846 case 0x33: {
847 tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, op_start, tobi_rd_off(rd) - op_start);
848 n->raw_op = raw;
849 tobi_ir_set_name(n, "timer");
850 return n;
851 }
852 case 0x88: {
853 char *name = NULL;
854 if (!parse_namestring_diag(p, rd, &name)) {
855 return unknown_node(p, op_start, raw, 2, "bad DataRegion name");
856 }
857 tobi_ir *n = tobi_ir_new(TOBI_IR_OPREGION, op_start, 0);
858 n->raw_op = raw;
859 tobi_ir_set_name(n, name);
860 char *path = tobi_nm_resolve(p->scope, name);
861 tobi_ir_set_path(n, path);
862 tobi_ir_set_str(n, "DataRegion");
863 ns_declare_path(p, TOBI_NS_OPREGION, path, op_start, 0, 0, 0, NULL);
864 add_expr_children(p, rd, depth, n, 3);
865 n->len = tobi_rd_off(rd) - op_start;
866 free(path);
867 free(name);
868 return n;
869 }
870 case 0x01:
871 case 0x02: {
872 char *name = NULL;
873 if (!parse_namestring_diag(p, rd, &name)) {
874 return unknown_node(p, op_start, raw, 2, ext == 0x01 ? "bad MutexOp" : "bad EventOp");
875 }
876 tobi_ir *n = tobi_ir_new(ext == 0x01 ? TOBI_IR_MUTEX : TOBI_IR_EVENT, op_start, 0);
877 tobi_ir_set_name(n, name);
878 char *path = tobi_nm_resolve(p->scope, name);
879 tobi_ir_set_path(n, path);
880 ns_declare_path(p, ext == 0x01 ? TOBI_NS_MUTEX : TOBI_NS_EVENT, path, op_start, 0, 0, 0, NULL);
881 if (ext == 0x01) {
882 uint8_t sync = 0;
883 (void)tobi_rd_u8(rd, &sync);
884 n->value = sync;
885 }
886 n->len = tobi_rd_off(rd) - op_start;
887 free(path);
888 free(name);
889 return n;
890 }
891 default:
892 return unknown_node(p, op_start, raw, 2, "unsupported extended opcode");
893 }
894}
895
896static tobi_ir *parse_if(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) {
897 size_t body_start = 0;
898 size_t body_len = 0;
899 size_t whole_len = 0;
900 if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) {
901 return unknown_node(p, op_start, 0xa0, 1, "bad IfOp package");
902 }
903 tobi_rd body;
904 tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start);
905 tobi_ir *n = tobi_ir_new(TOBI_IR_IF, op_start, whole_len);
906 tobi_ir *pred = parse_expr(p, &body, depth + 1);
907 if (pred) {
908 tobi_ir_add(n, pred);
909 }
910 tobi_ir_add(n, parse_block_from_reader(p, &body, depth, tobi_rd_off(&body)));
911 rd->pos = body_start + body_len;
912 uint8_t next = 0;
913 if (tobi_rd_peek(rd, &next) && next == 0xa1) {
914 size_t else_start = tobi_rd_off(rd);
915 (void)tobi_rd_u8(rd, &next);
916 size_t eb = 0;
917 size_t el = 0;
918 size_t ew = 0;
919 if (pkg_window(p, rd, else_start, &eb, &el, &ew)) {
920 tobi_rd er;
921 tobi_rd_init(&er, rd->data + eb, el, rd->base + eb);
922 tobi_ir_add(n, parse_block_from_reader(p, &er, depth, tobi_rd_off(&er)));
923 rd->pos = eb + el;
924 n->len = (rd->base + rd->pos) - op_start;
925 } else {
926 tobi_ir_add(n, unknown_node(p, else_start, 0xa1, 1, "bad ElseOp package"));
927 }
928 }
929 return n;
930}
931
932static tobi_ir *parse_while(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) {
933 size_t body_start = 0;
934 size_t body_len = 0;
935 size_t whole_len = 0;
936 if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) {
937 return unknown_node(p, op_start, 0xa2, 1, "bad WhileOp package");
938 }
939 tobi_rd body;
940 tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start);
941 tobi_ir *n = tobi_ir_new(TOBI_IR_WHILE, op_start, whole_len);
942 tobi_ir *pred = parse_expr(p, &body, depth + 1);
943 if (pred) {
944 tobi_ir_add(n, pred);
945 }
946 tobi_ir_add(n, parse_block_from_reader(p, &body, depth, tobi_rd_off(&body)));
947 rd->pos = body_start + body_len;
948 return n;
949}
950
951static int expr_opcode_arity(uint8_t op, int *target_count, const char **name) {
952 *target_count = 0;
953 switch (op) {
954 case 0x71: *name = "ref_of"; return 1;
955 case 0x72: *target_count = 1; *name = "add"; return 2;
956 case 0x73: *target_count = 1; *name = "concat"; return 2;
957 case 0x74: *target_count = 1; *name = "sub"; return 2;
958 case 0x75: *name = "inc"; return 1;
959 case 0x76: *name = "dec"; return 1;
960 case 0x77: *target_count = 1; *name = "mul"; return 2;
961 case 0x78: *target_count = 2; *name = "div"; return 2;
962 case 0x79: *target_count = 1; *name = "shl"; return 2;
963 case 0x7a: *target_count = 1; *name = "shr"; return 2;
964 case 0x7b: *target_count = 1; *name = "and"; return 2;
965 case 0x7c: *target_count = 1; *name = "nand"; return 2;
966 case 0x7d: *target_count = 1; *name = "or"; return 2;
967 case 0x7e: *target_count = 1; *name = "nor"; return 2;
968 case 0x7f: *target_count = 1; *name = "xor"; return 2;
969 case 0x80: *target_count = 1; *name = "not"; return 1;
970 case 0x81: *target_count = 1; *name = "find_set_left_bit"; return 1;
971 case 0x82: *target_count = 1; *name = "find_set_right_bit"; return 1;
972 case 0x83: *name = "deref_of"; return 1;
973 case 0x84: *target_count = 1; *name = "concat_res"; return 2;
974 case 0x85: *target_count = 1; *name = "mod"; return 2;
975 case 0x86: *name = "notify"; return 2;
976 case 0x87: *name = "size_of"; return 1;
977 case 0x88: *target_count = 1; *name = "index"; return 2;
978 case 0x89: *name = "match"; return 6;
979 case 0x8e: *name = "object_type"; return 1;
980 case 0x90: *name = "land"; return 2;
981 case 0x91: *name = "lor"; return 2;
982 case 0x92: *name = "lnot"; return 1;
983 case 0x93: *name = "eq"; return 2;
984 case 0x94: *name = "gt"; return 2;
985 case 0x95: *name = "lt"; return 2;
986 case 0x96: *target_count = 1; *name = "to_buffer"; return 1;
987 case 0x97: *target_count = 1; *name = "to_decimal_string"; return 1;
988 case 0x98: *target_count = 1; *name = "to_hex_string"; return 1;
989 case 0x99: *target_count = 1; *name = "to_integer"; return 1;
990 case 0x9c: *target_count = 1; *name = "to_string"; return 2;
991 case 0x9d: *target_count = 1; *name = "copy_object"; return 1;
992 case 0x9e: *target_count = 1; *name = "mid"; return 3;
993 default: return -1;
994 }
995}
996
997static const char *create_field_name(uint8_t op) {
998 switch (op) {
999 case 0x8a: return "create_dword_field";
1000 case 0x8b: return "create_word_field";
1001 case 0x8c: return "create_byte_field";
1002 case 0x8d: return "create_bit_field";
1003 case 0x8f: return "create_qword_field";
1004 default: return "create_field";
1005 }
1006}
1007
1008static tobi_ir *parse_string(parser *p, tobi_rd *rd, size_t op_start) {
1009 size_t s = rd->pos;
1010 while (rd->pos < rd->len && rd->data[rd->pos] != 0) {
1011 rd->pos++;
1012 }
1013 size_t n = rd->pos - s;
1014 if (rd->pos >= rd->len) {
1015 diag(p, TOBI_DIAG_ERROR, rd->base + s, "unterminated AML string");
1016 } else {
1017 rd->pos++;
1018 }
1019 char *tmp = tobi_xstrndup((const char *)rd->data + s, n);
1020 tobi_ir *node = tobi_ir_new(TOBI_IR_STRING, op_start, tobi_rd_off(rd) - op_start);
1021 tobi_ir_set_str(node, tmp);
1022 free(tmp);
1023 return node;
1024}
1025
1026static const char *small_res_name(unsigned name) {
1027 switch (name) {
1028 case 0x04: return "IRQ";
1029 case 0x05: return "DMA";
1030 case 0x06: return "StartDependentFn";
1031 case 0x07: return "EndDependentFn";
1032 case 0x08: return "IO";
1033 case 0x09: return "FixedIO";
1034 case 0x0e: return "VendorSmall";
1035 case 0x0f: return "EndTag";
1036 default: return NULL;
1037 }
1038}
1039
1040static const char *large_res_name(unsigned name) {
1041 switch (name) {
1042 case 0x01: return "Memory24";
1043 case 0x02: return "GenericRegister";
1044 case 0x04: return "VendorLarge";
1045 case 0x05: return "Memory32";
1046 case 0x06: return "FixedMemory32";
1047 case 0x07: return "DWordAddressSpace";
1048 case 0x08: return "WordAddressSpace";
1049 case 0x09: return "ExtendedIRQ";
1050 case 0x0a: return "QWordAddressSpace";
1051 case 0x0b: return "ExtendedAddressSpace";
1052 case 0x0c: return "GPIO";
1053 case 0x0e: return "SerialBus";
1054 default: return NULL;
1055 }
1056}
1057
1058static uint16_t res_u16(const uint8_t *p) {
1059 return (uint16_t)p[0] | ((uint16_t)p[1] << 8);
1060}
1061
1062static uint32_t res_u32(const uint8_t *p) {
1063 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
1064}
1065
1066static uint64_t res_u64(const uint8_t *p) {
1067 return (uint64_t)res_u32(p) | ((uint64_t)res_u32(p + 4) << 32);
1068}
1069
1070static void add_res_node(tobi_ir *parent, const char *label, size_t off, size_t len,
1071 uint32_t tag, size_t payload_len, const char *detail) {
1072 tobi_ir *r = tobi_ir_new(TOBI_IR_RESOURCE, off, len);
1073 r->raw_op = tag;
1074 r->value = payload_len;
1075 tobi_ir_set_name(r, label);
1076 if (detail) {
1077 tobi_ir_set_str(r, detail);
1078 }
1079 tobi_ir_add(parent, r);
1080}
1081
1082static char *small_res_detail(unsigned name, const uint8_t *p, size_t len) {
1083 tobi_sb sb;
1084 tobi_sb_init(&sb);
1085 if (name == 0x04 && len >= 2) {
1086 tobi_sb_printf(&sb, "irq_mask=0x%x", (unsigned)res_u16(p));
1087 if (len >= 3) {
1088 tobi_sb_printf(&sb, " flags=0x%x", (unsigned)p[2]);
1089 }
1090 } else if (name == 0x05 && len >= 2) {
1091 tobi_sb_printf(&sb, "dma_mask=0x%x flags=0x%x", (unsigned)p[0], (unsigned)p[1]);
1092 } else if (name == 0x08 && len >= 7) {
1093 tobi_sb_printf(&sb, "decode=0x%x min=0x%x max=0x%x align=%u length=%u",
1094 (unsigned)p[0], (unsigned)res_u16(p + 1), (unsigned)res_u16(p + 3),
1095 (unsigned)p[5], (unsigned)p[6]);
1096 } else if (name == 0x09 && len >= 3) {
1097 tobi_sb_printf(&sb, "base=0x%x length=%u", (unsigned)res_u16(p), (unsigned)p[2]);
1098 } else if (name == 0x0f && len >= 1) {
1099 tobi_sb_printf(&sb, "checksum=0x%x", (unsigned)p[0]);
1100 } else {
1101 tobi_sb_printf(&sb, "bytes=%zu", len);
1102 }
1103 return tobi_sb_take(&sb);
1104}
1105
1106static char *large_res_detail(unsigned name, const uint8_t *p, size_t len) {
1107 tobi_sb sb;
1108 tobi_sb_init(&sb);
1109 if (name == 0x02 && len >= 12) {
1110 tobi_sb_printf(&sb, "space=0x%x width=%u offset=%u access=%u address=0x%x",
1111 (unsigned)p[0], (unsigned)p[1], (unsigned)p[2], (unsigned)p[3],
1112 (unsigned)res_u64(p + 4));
1113 } else if ((name == 0x05 || name == 0x06) && len >= 9) {
1114 tobi_sb_printf(&sb, "write=0x%x min=0x%x max=0x%x align=0x%x length=0x%x",
1115 (unsigned)p[0], (unsigned)res_u32(p + 1), (unsigned)res_u32(p + 5),
1116 len >= 13 ? (unsigned)res_u32(p + 9) : 0u,
1117 len >= 17 ? (unsigned)res_u32(p + 13) : 0u);
1118 } else if (name == 0x07 && len >= 23) {
1119 tobi_sb_printf(&sb, "rtype=0x%x flags=0x%x gran=0x%x min=0x%x max=0x%x trans=0x%x length=0x%x",
1120 (unsigned)p[0], (unsigned)p[1], (unsigned)res_u32(p + 3),
1121 (unsigned)res_u32(p + 7), (unsigned)res_u32(p + 11),
1122 (unsigned)res_u32(p + 15), (unsigned)res_u32(p + 19));
1123 } else if (name == 0x08 && len >= 13) {
1124 tobi_sb_printf(&sb, "rtype=0x%x flags=0x%x gran=0x%x min=0x%x max=0x%x",
1125 (unsigned)p[0], (unsigned)p[1], (unsigned)res_u16(p + 3),
1126 (unsigned)res_u16(p + 5), (unsigned)res_u16(p + 7));
1127 } else if (name == 0x09 && len >= 2) {
1128 unsigned count = len >= 2 ? (unsigned)p[1] : 0u;
1129 tobi_sb_printf(&sb, "flags=0x%x interrupts=%u", (unsigned)p[0], count);
1130 } else if (name == 0x0a && len >= 43) {
1131 tobi_sb_printf(&sb, "rtype=0x%x flags=0x%x gran=0x%llx min=0x%llx max=0x%llx length=0x%llx",
1132 (unsigned)p[0], (unsigned)p[1], (unsigned long long)res_u64(p + 3),
1133 (unsigned long long)res_u64(p + 11), (unsigned long long)res_u64(p + 19),
1134 (unsigned long long)res_u64(p + 35));
1135 } else if (name == 0x0c && len >= 2) {
1136 tobi_sb_printf(&sb, "revision=%u type=0x%x", (unsigned)p[0], (unsigned)p[1]);
1137 } else if (name == 0x0e && len >= 2) {
1138 tobi_sb_printf(&sb, "revision=%u type=0x%x", (unsigned)p[0], (unsigned)p[1]);
1139 } else {
1140 tobi_sb_printf(&sb, "bytes=%zu", len);
1141 }
1142 return tobi_sb_take(&sb);
1143}
1144
1145static char *decode_resource_template(tobi_ir *parent, const uint8_t *data, size_t len, size_t base) {
1146 if (!data || len == 0) {
1147 return NULL;
1148 }
1149 tobi_sb sb;
1150 tobi_sb_init(&sb);
1151 size_t pos = 0;
1152 int any = 0;
1153 int ended = 0;
1154 while (pos < len) {
1155 uint8_t tag = data[pos++];
1156 if ((tag & 0x80u) != 0) {
1157 if (pos + 2 > len) {
1158 break;
1159 }
1160 unsigned name = tag & 0x7fu;
1161 size_t item_len = (size_t)data[pos] | ((size_t)data[pos + 1] << 8);
1162 pos += 2;
1163 if (item_len > len - pos) {
1164 break;
1165 }
1166 const char *label = large_res_name(name);
1167 if (!label) {
1168 label = "LargeItem";
1169 }
1170 char *detail = large_res_detail(name, data + pos, item_len);
1171 add_res_node(parent, label, base + pos - 3u, item_len + 3u, tag, item_len, detail);
1172 free(detail);
1173 if (any) {
1174 tobi_sb_add(&sb, "; ");
1175 }
1176 tobi_sb_printf(&sb, "%s(len=%zu)", label, item_len);
1177 any = 1;
1178 pos += item_len;
1179 } else {
1180 unsigned name = (tag >> 3) & 0x0fu;
1181 size_t item_len = tag & 0x07u;
1182 if (item_len > len - pos) {
1183 break;
1184 }
1185 const char *label = small_res_name(name);
1186 if (!label) {
1187 label = "SmallItem";
1188 }
1189 char *detail = small_res_detail(name, data + pos, item_len);
1190 add_res_node(parent, label, base + pos - 1u, item_len + 1u, tag, item_len, detail);
1191 free(detail);
1192 if (any) {
1193 tobi_sb_add(&sb, "; ");
1194 }
1195 tobi_sb_printf(&sb, "%s(len=%zu)", label, item_len);
1196 any = 1;
1197 pos += item_len;
1198 if (name == 0x0f) {
1199 ended = 1;
1200 break;
1201 }
1202 }
1203 }
1204 if (!any || !ended || pos != len) {
1205 tobi_sb_free(&sb);
1206 return NULL;
1207 }
1208 return tobi_sb_take(&sb);
1209}
1210
1211static tobi_ir *parse_buffer(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) {
1212 size_t body_start = 0;
1213 size_t body_len = 0;
1214 size_t whole_len = 0;
1215 if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) {
1216 return unknown_node(p, op_start, 0x11, 1, "bad Buffer package");
1217 }
1218 tobi_rd body;
1219 tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start);
1220 tobi_ir *n = tobi_ir_new(TOBI_IR_BUFFER, op_start, whole_len);
1221 tobi_ir *sz = parse_expr(p, &body, depth + 1);
1222 if (sz) {
1223 tobi_ir_add(n, sz);
1224 }
1225 tobi_sb hex;
1226 tobi_sb_init(&hex);
1227 const uint8_t *bytes = body.data + body.pos;
1228 size_t byte_len = tobi_rd_left(&body);
1229 tobi_hex(&hex, bytes, byte_len);
1230 char *res = decode_resource_template(n, bytes, byte_len, body.base + body.pos);
1231 if (res) {
1232 tobi_sb_add(&hex, " resources=");
1233 tobi_sb_add(&hex, res);
1234 free(res);
1235 }
1236 tobi_ir_set_str(n, hex.buf);
1237 tobi_sb_free(&hex);
1238 rd->pos = body_start + body_len;
1239 return n;
1240}
1241
1242static tobi_ir *parse_package(parser *p, tobi_rd *rd, unsigned depth, size_t op_start, int variable) {
1243 size_t body_start = 0;
1244 size_t body_len = 0;
1245 size_t whole_len = 0;
1246 if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) {
1247 return unknown_node(p, op_start, variable ? 0x13 : 0x12, 1, "bad Package");
1248 }
1249 tobi_rd body;
1250 tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start);
1251 tobi_ir *n = tobi_ir_new(TOBI_IR_PACKAGE, op_start, whole_len);
1252 if (variable) {
1253 tobi_ir *cnt = parse_expr(p, &body, depth + 1);
1254 if (cnt) {
1255 tobi_ir_add(n, cnt);
1256 }
1257 } else {
1258 uint8_t count = 0;
1259 if (tobi_rd_u8(&body, &count)) {
1260 n->value = count;
1261 } else {
1262 diag(p, TOBI_DIAG_ERROR, tobi_rd_off(&body), "truncated package element count");
1263 }
1264 }
1265 while (tobi_rd_left(&body) > 0) {
1266 size_t before = body.pos;
1267 tobi_ir *e = parse_expr(p, &body, depth + 1);
1268 if (e) {
1269 tobi_ir_add(n, e);
1270 }
1271 if (body.pos == before) {
1272 (void)tobi_rd_skip(&body, 1);
1273 }
1274 }
1275 rd->pos = body_start + body_len;
1276 return n;
1277}
1278
1279static tobi_ir *parse_name_or_call(parser *p, tobi_rd *rd, unsigned depth) {
1280 size_t off = tobi_rd_off(rd);
1281 char *name = NULL;
1282 if (!parse_namestring_diag(p, rd, &name)) {
1283 return unknown_node(p, off, 0, 1, "bad namestring expression");
1284 }
1285 const tobi_ns_ent *ent = tobi_ns_lookup(p->ns, p->scope, name);
1286 int argc = ent && ent->kind == TOBI_NS_METHOD ? (int)ent->args : -1;
1287 char *path = ent ? tobi_xstrdup(ent->path) : tobi_nm_resolve(p->scope, name);
1288 tobi_ir *n = tobi_ir_new(argc >= 0 ? TOBI_IR_CALL : TOBI_IR_REF, off, 0);
1289 tobi_ir_set_name(n, name);
1290 tobi_ir_set_path(n, path);
1291 if (argc >= 0) {
1292 n->value = (uint64_t)argc;
1293 for (int i = 0; i < argc && tobi_rd_left(rd) > 0; i++) {
1294 tobi_ir *arg = parse_expr(p, rd, depth + 1);
1295 if (arg) {
1296 tobi_ir_add(n, arg);
1297 }
1298 }
1299 }
1300 n->len = tobi_rd_off(rd) - off;
1301 free(path);
1302 free(name);
1303 return n;
1304}
1305
1306static tobi_ir *parse_expr(parser *p, tobi_rd *rd, unsigned depth) {
1307 if (depth > MAX_DEPTH) {
1308 diag(p, TOBI_DIAG_ERROR, tobi_rd_off(rd), "AML nesting too deep");
1309 return NULL;
1310 }
1311 size_t off = tobi_rd_off(rd);
1312 uint8_t op = 0;
1313 if (!tobi_rd_u8(rd, &op)) {
1314 diag(p, TOBI_DIAG_ERROR, off, "truncated AML expression");
1315 return NULL;
1316 }
1317 if (op == 0x00 || op == 0x01 || op == 0xff) {
1318 tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, off, 1);
1319 n->value = op == 0x00 ? 0 : (op == 0x01 ? 1 : UINT64_MAX);
1320 return n;
1321 }
1322 if (op == 0x0a) {
1323 uint8_t v = 0;
1324 if (!tobi_rd_u8(rd, &v)) {
1325 diag(p, TOBI_DIAG_ERROR, off, "truncated ByteConst");
1326 }
1327 tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, off, tobi_rd_off(rd) - off);
1328 n->value = v;
1329 return n;
1330 }
1331 if (op == 0x0b) {
1332 uint16_t v = 0;
1333 if (!tobi_rd_u16(rd, &v)) {
1334 diag(p, TOBI_DIAG_ERROR, off, "truncated WordConst");
1335 }
1336 tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, off, tobi_rd_off(rd) - off);
1337 n->value = v;
1338 return n;
1339 }
1340 if (op == 0x0c) {
1341 uint32_t v = 0;
1342 if (!tobi_rd_u32(rd, &v)) {
1343 diag(p, TOBI_DIAG_ERROR, off, "truncated DWordConst");
1344 }
1345 tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, off, tobi_rd_off(rd) - off);
1346 n->value = v;
1347 return n;
1348 }
1349 if (op == 0x0e) {
1350 uint64_t v = 0;
1351 if (!tobi_rd_u64(rd, &v)) {
1352 diag(p, TOBI_DIAG_ERROR, off, "truncated QWordConst");
1353 }
1354 tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, off, tobi_rd_off(rd) - off);
1355 n->value = v;
1356 return n;
1357 }
1358 if (op == 0x0d) {
1359 return parse_string(p, rd, off);
1360 }
1361 if (op == 0x5b) {
1362 return parse_ext(p, rd, depth, off);
1363 }
1364 if (op == 0x11) {
1365 return parse_buffer(p, rd, depth, off);
1366 }
1367 if (op == 0x12 || op == 0x13) {
1368 return parse_package(p, rd, depth, off, op == 0x13);
1369 }
1370 if (op >= 0x60 && op <= 0x67) {
1371 tobi_ir *n = tobi_ir_new(TOBI_IR_REF, off, 1);
1372 char name[8];
1373 (void)snprintf(name, sizeof(name), "Local%u", (unsigned)(op - 0x60));
1374 tobi_ir_set_name(n, name);
1375 return n;
1376 }
1377 if (op >= 0x68 && op <= 0x6e) {
1378 tobi_ir *n = tobi_ir_new(TOBI_IR_REF, off, 1);
1379 char name[8];
1380 (void)snprintf(name, sizeof(name), "Arg%u", (unsigned)(op - 0x68));
1381 tobi_ir_set_name(n, name);
1382 return n;
1383 }
1384 if (op == 0x70) {
1385 tobi_ir *n = tobi_ir_new(TOBI_IR_STORE, off, 0);
1386 tobi_ir *src = parse_expr(p, rd, depth + 1);
1387 tobi_ir *dst = parse_expr(p, rd, depth + 1);
1388 if (src) {
1389 tobi_ir_add(n, src);
1390 }
1391 if (dst) {
1392 tobi_ir_add(n, dst);
1393 }
1394 n->len = tobi_rd_off(rd) - off;
1395 return n;
1396 }
1397 if ((op >= 0x8a && op <= 0x8d) || op == 0x8f) {
1398 tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, off, 0);
1399 n->raw_op = op;
1400 tobi_ir_set_name(n, create_field_name(op));
1401 add_expr_children(p, rd, depth, n, 2);
1402 tobi_ir *dst = ref_from_name(p, rd, off, "bad create-field namestring");
1403 if (dst) {
1404 if (dst->path && dst->path[0] == '\\') {
1405 ns_declare_path(p, TOBI_NS_FIELD, dst->path, dst->off, 0, op, 0, NULL);
1406 }
1407 tobi_ir_add(n, dst);
1408 }
1409 n->len = tobi_rd_off(rd) - off;
1410 return n;
1411 }
1412 int target_count = 0;
1413 const char *ename = NULL;
1414 int arity = expr_opcode_arity(op, &target_count, &ename);
1415 if (arity >= 0) {
1416 tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, off, 0);
1417 tobi_ir_set_name(n, ename);
1418 n->raw_op = op;
1419 for (int i = 0; i < arity; i++) {
1420 tobi_ir *arg = parse_expr(p, rd, depth + 1);
1421 if (arg) {
1422 tobi_ir_add(n, arg);
1423 }
1424 }
1425 for (int i = 0; i < target_count; i++) {
1426 tobi_ir *target = parse_expr(p, rd, depth + 1);
1427 if (target) {
1428 tobi_ir_add(n, target);
1429 }
1430 }
1431 n->len = tobi_rd_off(rd) - off;
1432 return n;
1433 }
1434 rd->pos--;
1435 uint8_t b = 0;
1436 (void)tobi_rd_peek(rd, &b);
1437 if (b == '\\' || b == '^' || b == 0x2e || b == 0x2f || tobi_nm_is_lead(b)) {
1438 return parse_name_or_call(p, rd, depth);
1439 }
1440 (void)tobi_rd_u8(rd, &op);
1441 return unknown_node(p, off, op, 1, "unknown expression opcode");
1442}
1443
1444static tobi_ir *parse_return(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) {
1445 tobi_ir *n = tobi_ir_new(TOBI_IR_RETURN, op_start, 0);
1446 if (tobi_rd_left(rd) > 0) {
1447 tobi_ir *v = parse_expr(p, rd, depth + 1);
1448 if (v) {
1449 tobi_ir_add(n, v);
1450 }
1451 }
1452 n->len = tobi_rd_off(rd) - op_start;
1453 return n;
1454}
1455
1456static tobi_ir *parse_term(parser *p, tobi_rd *rd, unsigned depth) {
1457 if (depth > MAX_DEPTH) {
1458 diag(p, TOBI_DIAG_ERROR, tobi_rd_off(rd), "AML nesting too deep");
1459 return NULL;
1460 }
1461 if (++p->terms > MAX_TERMS) {
1462 diag(p, TOBI_DIAG_ERROR, tobi_rd_off(rd), "too many AML terms");
1463 rd->pos = rd->len;
1464 return NULL;
1465 }
1466 size_t off = tobi_rd_off(rd);
1467 uint8_t op = 0;
1468 if (!tobi_rd_u8(rd, &op)) {
1469 return NULL;
1470 }
1471 switch (op) {
1472 case 0x06: return parse_alias(p, rd, off);
1473 case 0x08: return parse_name(p, rd, depth, off);
1474 case 0x10: return parse_pkg_named(p, rd, depth, TOBI_IR_SCOPE, "ScopeOp", off);
1475 case 0x14: return parse_method(p, rd, depth, off);
1476 case 0x15: return parse_external(p, rd, off);
1477 case 0x5b: return parse_ext(p, rd, depth, off);
1478 case 0x70:
1479 rd->pos--;
1480 return parse_expr(p, rd, depth);
1481 case 0xa0: return parse_if(p, rd, depth, off);
1482 case 0xa2: return parse_while(p, rd, depth, off);
1483 case 0xa3: {
1484 tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, off, 1);
1485 tobi_ir_set_name(n, "noop");
1486 return n;
1487 }
1488 case 0xa4: return parse_return(p, rd, depth, off);
1489 case 0xa5: return tobi_ir_new(TOBI_IR_BREAK, off, 1);
1490 case 0x9f: return tobi_ir_new(TOBI_IR_CONTINUE, off, 1);
1491 case 0xcc: {
1492 tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, off, 1);
1493 n->raw_op = 0xcc;
1494 tobi_ir_set_name(n, "breakpoint");
1495 return n;
1496 }
1497 default:
1498 rd->pos--;
1499 return parse_expr(p, rd, depth);
1500 }
1501}
1502
1503static int parse_term_list(parser *p, tobi_rd *rd, tobi_ir *parent, unsigned depth) {
1504 while (tobi_rd_left(rd) > 0) {
1505 size_t before = rd->pos;
1506 tobi_ir *n = parse_term(p, rd, depth);
1507 if (n) {
1508 tobi_ir_add(parent, n);
1509 }
1510 if (rd->pos == before) {
1511 uint8_t b = 0;
1512 if (tobi_rd_u8(rd, &b)) {
1513 tobi_ir_add(parent, unknown_node(p, rd->base + before, b, 1, "parser made no progress"));
1514 } else {
1515 break;
1516 }
1517 }
1518 }
1519 return 1;
1520}
1521
1522static int scan_pkg_window(tobi_rd *rd, size_t *body_start, size_t *body_len) {
1523 size_t pkg_start = rd->pos;
1524 size_t pkg_len = 0;
1525 size_t enc = 0;
1526 if (!tobi_rd_pkg_len(rd, &pkg_len, &enc) || pkg_len < enc) {
1527 return 0;
1528 }
1529 size_t end = pkg_start + pkg_len;
1530 if (end > rd->len || end < rd->pos) {
1531 return 0;
1532 }
1533 *body_start = rd->pos;
1534 *body_len = end - rd->pos;
1535 return 1;
1536}
1537
1538static void scan_methods(parser *p, tobi_rd *rd, unsigned depth) {
1539 if (depth > MAX_DEPTH) {
1540 return;
1541 }
1542 while (tobi_rd_left(rd) > 0) {
1543 size_t start = rd->pos;
1544 size_t abs_start = rd->base + start;
1545 uint8_t op = 0;
1546 if (!tobi_rd_u8(rd, &op)) {
1547 return;
1548 }
1549 if (op == 0x14 || op == 0x10) {
1550 size_t body_start = 0;
1551 size_t body_len = 0;
1552 if (!scan_pkg_window(rd, &body_start, &body_len)) {
1553 rd->pos = rd->len;
1554 return;
1555 }
1556 tobi_rd body;
1557 tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start);
1558 char *name = NULL;
1559 if (!tobi_nm_namestring(&body, &name)) {
1560 rd->pos = body_start + body_len;
1561 continue;
1562 }
1563 char *path = tobi_nm_resolve(p->scope, name);
1564 if (op == 0x14) {
1565 uint8_t flags = 0;
1566 if (tobi_rd_u8(&body, &flags)) {
1567 ns_declare_path(p, TOBI_NS_METHOD, path, abs_start, flags & 0x07u, flags, 0, NULL);
1568 }
1569 } else {
1570 char *old_scope = p->scope;
1571 p->scope = tobi_xstrdup(path);
1572 scan_methods(p, &body, depth + 1);
1573 free(p->scope);
1574 p->scope = old_scope;
1575 }
1576 free(path);
1577 free(name);
1578 rd->pos = body_start + body_len;
1579 continue;
1580 }
1581 if (op == 0x15) {
1582 char *name = NULL;
1583 if (tobi_nm_namestring(rd, &name)) {
1584 uint8_t obj_type = 0;
1585 uint8_t argc = 0;
1586 if (tobi_rd_u8(rd, &obj_type) && tobi_rd_u8(rd, &argc) && obj_type == 0x08u) {
1587 char *path = tobi_nm_resolve(p->scope, name);
1588 ns_declare_path(p, TOBI_NS_METHOD, path, abs_start, argc & 0x07u, obj_type, 1, NULL);
1589 free(path);
1590 }
1591 free(name);
1592 }
1593 continue;
1594 }
1595 if (op == 0x5b) {
1596 uint8_t ext = 0;
1597 if (!tobi_rd_u8(rd, &ext)) {
1598 return;
1599 }
1600 if (ext == 0x82 || ext == 0x83 || ext == 0x84 || ext == 0x85) {
1601 size_t body_start = 0;
1602 size_t body_len = 0;
1603 if (!scan_pkg_window(rd, &body_start, &body_len)) {
1604 rd->pos = rd->len;
1605 return;
1606 }
1607 tobi_rd body;
1608 tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start);
1609 char *name = NULL;
1610 if (tobi_nm_namestring(&body, &name)) {
1611 char *path = tobi_nm_resolve(p->scope, name);
1612 if (ext == 0x83) {
1613 (void)tobi_rd_skip(&body, 6);
1614 } else if (ext == 0x84) {
1615 (void)tobi_rd_skip(&body, 2);
1616 }
1617 char *old_scope = p->scope;
1618 p->scope = tobi_xstrdup(path);
1619 scan_methods(p, &body, depth + 1);
1620 free(p->scope);
1621 p->scope = old_scope;
1622 free(path);
1623 free(name);
1624 }
1625 rd->pos = body_start + body_len;
1626 continue;
1627 }
1628 if (ext == 0x81 || ext == 0x86 || ext == 0x87) {
1629 size_t body_start = 0;
1630 size_t body_len = 0;
1631 if (scan_pkg_window(rd, &body_start, &body_len)) {
1632 rd->pos = body_start + body_len;
1633 }
1634 continue;
1635 }
1636 }
1637 if (op == 0x11 || op == 0x12 || op == 0x13 || op == 0xa0 || op == 0xa1 || op == 0xa2) {
1638 size_t body_start = 0;
1639 size_t body_len = 0;
1640 if (scan_pkg_window(rd, &body_start, &body_len)) {
1641 if (op == 0xa0 || op == 0xa1 || op == 0xa2) {
1642 tobi_rd body;
1643 tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start);
1644 scan_methods(p, &body, depth + 1);
1645 }
1646 rd->pos = body_start + body_len;
1647 continue;
1648 }
1649 }
1650 if (rd->pos == start) {
1651 rd->pos++;
1652 }
1653 }
1654}
1655
1656static void sem_diag(parser *p, size_t off, const char *fmt, ...) {
1657 va_list ap;
1658 va_start(ap, fmt);
1659 va_list cp;
1660 va_copy(cp, ap);
1661 int n = vsnprintf(NULL, 0, fmt, cp);
1662 va_end(cp);
1663 if (n < 0) {
1664 n = 0;
1665 }
1666 char *msg = tobi_xmalloc((size_t)n + 1);
1667 (void)vsnprintf(msg, (size_t)n + 1, fmt, ap);
1668 va_end(ap);
1669 diag(p, p->strict ? TOBI_DIAG_ERROR : TOBI_DIAG_WARN, off, "%s", msg);
1670 free(msg);
1671}
1672
1673static int semantic_alias_cycle_from(const tobi_ns *ns, const tobi_ns_ent *start) {
1674 const tobi_ns_ent *cur = start;
1675 for (unsigned depth = 0; cur && cur->kind == TOBI_NS_ALIAS && cur->target; depth++) {
1676 if (depth > ns->len) {
1677 return 1;
1678 }
1679 const tobi_ns_ent *next = tobi_ns_find_const(ns, cur->target);
1680 if (!next) {
1681 return 0;
1682 }
1683 if (next == start) {
1684 return 1;
1685 }
1686 cur = next;
1687 }
1688 return 0;
1689}
1690
1691static int semantic_ref_is_value(const tobi_ir *parent) {
1692 if (!parent) {
1693 return 1;
1694 }
1695 if (parent->kind == TOBI_IR_EXPR && parent->name) {
1696 if (strncmp(parent->name, "create_", 7) == 0 || strcmp(parent->name, "cond_ref_of") == 0 ||
1697 strcmp(parent->name, "load") == 0 || strcmp(parent->name, "ref_of") == 0 ||
1698 strcmp(parent->name, "acquire") == 0 || strcmp(parent->name, "wait") == 0 ||
1699 strcmp(parent->name, "signal") == 0 || strcmp(parent->name, "reset") == 0 ||
1700 strcmp(parent->name, "release") == 0) {
1701 return 0;
1702 }
1703 }
1704 return 1;
1705}
1706
1707static void semantic_walk(parser *p, const tobi_ir *n, const tobi_ir *parent) {
1708 if (!n) {
1709 return;
1710 }
1711 if (n->kind == TOBI_IR_CALL && n->path && n->path[0] == '\\') {
1712 const tobi_ns_ent *ent = tobi_ns_find_const(p->ns, n->path);
1713 if (!ent || ent->kind != TOBI_NS_METHOD) {
1714 sem_diag(p, n->off, "unresolved AML method call %s", n->path);
1715 } else if (n->child_len != (size_t)ent->args) {
1716 sem_diag(p, n->off, "AML method call %s has %zu args but namespace declares %u",
1717 n->path, n->child_len, ent->args);
1718 }
1719 } else if (n->kind == TOBI_IR_REF && n->path && n->path[0] == '\\' && semantic_ref_is_value(parent)) {
1720 const tobi_ns_ent *ent = tobi_ns_find_const(p->ns, n->path);
1721 if (!ent) {
1722 sem_diag(p, n->off, "unresolved AML namespace reference %s", n->path);
1723 }
1724 }
1725 for (size_t i = 0; i < n->child_len; i++) {
1726 semantic_walk(p, n->child[i], n);
1727 }
1728}
1729
1730typedef struct method_state {
1731 parser *p;
1732 tobi_ir *method;
1733 uint16_t locals;
1734 uint16_t local_reads;
1735 uint16_t local_writes;
1736 uint16_t arg_reads;
1737 uint16_t arg_writes;
1738 unsigned warnings;
1739} method_state;
1740
1741static int ref_index(const tobi_ir *n, const char *prefix, unsigned max, unsigned *idx) {
1742 size_t plen = strlen(prefix);
1743 if (!n || n->kind != TOBI_IR_REF || !n->name || strncmp(n->name, prefix, plen) != 0) {
1744 return 0;
1745 }
1746 char c = n->name[plen];
1747 if (n->name[plen + 1u] != '\0' || c < '0' || c > (char)('0' + max)) {
1748 return 0;
1749 }
1750 *idx = (unsigned)(c - '0');
1751 return 1;
1752}
1753
1754static void method_mark_ref(method_state *st, tobi_ir *n, int write_target) {
1755 unsigned idx = 0;
1756 if (ref_index(n, "Local", 7, &idx)) {
1757 if (write_target) {
1758 st->locals |= (uint16_t)(1u << idx);
1759 st->local_writes |= (uint16_t)(1u << idx);
1760 tobi_ir_set_str(n, "method_symbol=local write=initialised");
1761 } else if ((st->locals & (uint16_t)(1u << idx)) == 0) {
1762 st->local_reads |= (uint16_t)(1u << idx);
1763 st->warnings++;
1764 sem_diag(st->p, n->off, "Local%u may be read before being initialised in method %s",
1765 idx, st->method->path ? st->method->path : st->method->name);
1766 tobi_ir_set_str(n, "method_symbol=local read=uninitialised");
1767 } else {
1768 st->local_reads |= (uint16_t)(1u << idx);
1769 tobi_ir_set_str(n, "method_symbol=local read=initialised");
1770 }
1771 } else if (ref_index(n, "Arg", 6, &idx)) {
1772 if (write_target) {
1773 st->arg_writes |= (uint16_t)(1u << idx);
1774 } else {
1775 st->arg_reads |= (uint16_t)(1u << idx);
1776 }
1777 if (idx >= st->method->method_args) {
1778 st->warnings++;
1779 sem_diag(st->p, n->off, "Arg%u %s exceeds declared argument count %u in method %s",
1780 idx, write_target ? "write" : "read",
1781 st->method->method_args, st->method->path ? st->method->path : st->method->name);
1782 tobi_ir_set_str(n, write_target ? "method_symbol=arg write=out_of_range" :
1783 "method_symbol=arg read=out_of_range");
1784 } else if (write_target) {
1785 tobi_ir_set_str(n, "method_symbol=arg write=declared");
1786 } else {
1787 tobi_ir_set_str(n, "method_symbol=arg read=declared");
1788 }
1789 }
1790}
1791
1792static void method_symbols_node(method_state *st, tobi_ir *n);
1793
1794static void method_symbols_block(method_state *st, tobi_ir *b) {
1795 if (!b) {
1796 return;
1797 }
1798 for (size_t i = 0; i < b->child_len; i++) {
1799 method_symbols_node(st, b->child[i]);
1800 }
1801}
1802
1803static void method_symbols_node(method_state *st, tobi_ir *n) {
1804 if (!n) {
1805 return;
1806 }
1807 if (n->kind == TOBI_IR_STORE) {
1808 if (n->child_len > 0) {
1809 method_symbols_node(st, n->child[0]);
1810 }
1811 if (n->child_len > 1) {
1812 unsigned idx = 0;
1813 if (ref_index(n->child[1], "Local", 7, &idx) || ref_index(n->child[1], "Arg", 6, &idx)) {
1814 method_mark_ref(st, n->child[1], 1);
1815 } else {
1816 method_symbols_node(st, n->child[1]);
1817 }
1818 }
1819 return;
1820 }
1821 if (n->kind == TOBI_IR_REF) {
1822 method_mark_ref(st, n, 0);
1823 return;
1824 }
1825 if (n->kind == TOBI_IR_EXPR && n->name) {
1826 size_t target_start = n->child_len;
1827 if (strcmp(n->name, "div") == 0 && n->child_len >= 4) {
1828 target_start = 2;
1829 } else if ((strcmp(n->name, "add") == 0 || strcmp(n->name, "concat") == 0 ||
1830 strcmp(n->name, "sub") == 0 || strcmp(n->name, "mul") == 0 ||
1831 strcmp(n->name, "shl") == 0 || strcmp(n->name, "shr") == 0 ||
1832 strcmp(n->name, "and") == 0 || strcmp(n->name, "nand") == 0 ||
1833 strcmp(n->name, "or") == 0 || strcmp(n->name, "nor") == 0 ||
1834 strcmp(n->name, "xor") == 0 || strcmp(n->name, "not") == 0 ||
1835 strcmp(n->name, "find_set_left_bit") == 0 ||
1836 strcmp(n->name, "find_set_right_bit") == 0 ||
1837 strcmp(n->name, "concat_res") == 0 || strcmp(n->name, "mod") == 0 ||
1838 strcmp(n->name, "index") == 0 || strcmp(n->name, "to_buffer") == 0 ||
1839 strcmp(n->name, "to_decimal_string") == 0 ||
1840 strcmp(n->name, "to_hex_string") == 0 || strcmp(n->name, "to_integer") == 0 ||
1841 strcmp(n->name, "to_string") == 0 || strcmp(n->name, "copy_object") == 0 ||
1842 strcmp(n->name, "mid") == 0) && n->child_len >= 3) {
1843 target_start = n->child_len - 1u;
1844 }
1845 for (size_t i = 0; i < target_start; i++) {
1846 method_symbols_node(st, n->child[i]);
1847 }
1848 for (size_t i = target_start; i < n->child_len; i++) {
1849 unsigned idx = 0;
1850 if (ref_index(n->child[i], "Local", 7, &idx) || ref_index(n->child[i], "Arg", 6, &idx)) {
1851 method_mark_ref(st, n->child[i], 1);
1852 } else {
1853 method_symbols_node(st, n->child[i]);
1854 }
1855 }
1856 return;
1857 }
1858 if (n->kind == TOBI_IR_IF) {
1859 if (n->child_len > 0) {
1860 method_symbols_node(st, n->child[0]);
1861 }
1862 uint16_t base = st->locals;
1863 uint16_t then_locals = base;
1864 uint16_t else_locals = base;
1865 if (n->child_len > 1) {
1866 st->locals = base;
1867 method_symbols_block(st, n->child[1]);
1868 then_locals = st->locals;
1869 }
1870 if (n->child_len > 2) {
1871 st->locals = base;
1872 method_symbols_block(st, n->child[2]);
1873 else_locals = st->locals;
1874 st->locals = then_locals & else_locals;
1875 } else {
1876 st->locals = base;
1877 }
1878 return;
1879 }
1880 if (n->kind == TOBI_IR_WHILE) {
1881 if (n->child_len > 0) {
1882 method_symbols_node(st, n->child[0]);
1883 }
1884 uint16_t base = st->locals;
1885 if (n->child_len > 1) {
1886 st->locals = base;
1887 method_symbols_block(st, n->child[1]);
1888 }
1889 st->locals = base;
1890 return;
1891 }
1892 for (size_t i = 0; i < n->child_len; i++) {
1893 method_symbols_node(st, n->child[i]);
1894 }
1895}
1896
1897static void semantic_methods(parser *p, tobi_ir *n) {
1898 if (!n) {
1899 return;
1900 }
1901 if (n->kind == TOBI_IR_METHOD) {
1902 method_state st;
1903 memset(&st, 0, sizeof(st));
1904 st.p = p;
1905 st.method = n;
1906 if (n->child_len > 0) {
1907 method_symbols_block(&st, n->child[0]);
1908 }
1909 char summary[160];
1910 (void)snprintf(summary, sizeof(summary),
1911 "method_symbols args=%u local_reads=0x%x local_writes=0x%x final_locals=0x%x arg_reads=0x%x arg_writes=0x%x warnings=%u",
1912 n->method_args, st.local_reads, st.local_writes, st.locals,
1913 st.arg_reads, st.arg_writes, st.warnings);
1914 tobi_ir_set_target(n, summary);
1915 }
1916 for (size_t i = 0; i < n->child_len; i++) {
1917 semantic_methods(p, n->child[i]);
1918 }
1919}
1920
1921static void semantic_validate(parser *p, tobi_ir *root) {
1922 for (size_t i = 0; i < p->ns->len; i++) {
1923 const tobi_ns_ent *e = &p->ns->items[i];
1924 if (e->external && e->kind == TOBI_NS_METHOD) {
1925 sem_diag(p, e->off, "external AML method %s remains unresolved", e->path);
1926 }
1927 if (e->kind == TOBI_NS_ALIAS) {
1928 if (e->target && !tobi_ns_find_const(p->ns, e->target)) {
1929 sem_diag(p, e->off, "AML alias %s targets unresolved object %s", e->path, e->target);
1930 } else if (semantic_alias_cycle_from(p->ns, e)) {
1931 sem_diag(p, e->off, "AML alias cycle includes %s", e->path);
1932 }
1933 }
1934 }
1935 semantic_walk(p, root, NULL);
1936 semantic_methods(p, root);
1937}
1938
1939int tobi_parse(const uint8_t *data, size_t len, int strict, tobi_parse_result *out) {
1940 memset(out, 0, sizeof(*out));
1941 tobi_diag_init(&out->diag);
1942 tobi_ns_init(&out->ns);
1943 if (!detect_input(data, len, strict, &out->diag, &out->meta)) {
1944 return 0;
1945 }
1946 out->root = tobi_ir_new(TOBI_IR_ROOT, out->meta.aml_off, out->meta.aml_len);
1947 parser p;
1948 memset(&p, 0, sizeof(p));
1949 p.diag = &out->diag;
1950 p.strict = strict;
1951 p.ns = &out->ns;
1952 p.scope = tobi_xstrdup("\\");
1953 tobi_rd rd;
1954 if (out->meta.aml_off <= len && out->meta.aml_len <= len - out->meta.aml_off) {
1955 tobi_rd scan;
1956 tobi_rd_init(&scan, data + out->meta.aml_off, out->meta.aml_len, out->meta.aml_off);
1957 scan_methods(&p, &scan, 0);
1958 tobi_rd_init(&rd, data + out->meta.aml_off, out->meta.aml_len, out->meta.aml_off);
1959 (void)parse_term_list(&p, &rd, out->root, 0);
1960 }
1961 semantic_validate(&p, out->root);
1962 parser_free(&p);
1963 return !(strict && tobi_diag_has_error(&out->diag));
1964}
1965
1966int tobi_parse_multi(const tobi_parse_input *inputs, size_t count, int strict, tobi_parse_result *out) {
1967 memset(out, 0, sizeof(*out));
1968 tobi_diag_init(&out->diag);
1969 tobi_ns_init(&out->ns);
1970 memcpy(out->meta.signature, "MULT", 4);
1971 out->meta.signature[4] = '\0';
1972 memcpy(out->meta.source, "multi", 6);
1973 out->root = tobi_ir_new(TOBI_IR_ROOT, 0, 0);
1974 if (!inputs || count == 0) {
1975 tobi_diag_add(&out->diag, TOBI_DIAG_ERROR, 0, "no AML inputs");
1976 return 0;
1977 }
1978 tobi_input_meta *metas = tobi_xcalloc(count, sizeof(metas[0]));
1979 int ok = 1;
1980 parser p;
1981 memset(&p, 0, sizeof(p));
1982 p.diag = &out->diag;
1983 p.strict = strict;
1984 p.ns = &out->ns;
1985 p.scope = tobi_xstrdup("\\");
1986 for (size_t i = 0; i < count; i++) {
1987 if (!detect_input(inputs[i].data, inputs[i].len, strict, &out->diag, &metas[i])) {
1988 ok = 0;
1989 continue;
1990 }
1991 out->meta.aml_len += metas[i].aml_len;
1992 if (metas[i].aml_off <= inputs[i].len && metas[i].aml_len <= inputs[i].len - metas[i].aml_off) {
1993 tobi_rd scan;
1994 tobi_rd_init(&scan, inputs[i].data + metas[i].aml_off, metas[i].aml_len, metas[i].aml_off);
1995 scan_methods(&p, &scan, 0);
1996 }
1997 }
1998 if (!strict || ok) {
1999 for (size_t i = 0; i < count; i++) {
2000 if (metas[i].aml_off > inputs[i].len || metas[i].aml_len > inputs[i].len - metas[i].aml_off) {
2001 continue;
2002 }
2003 tobi_ir *file = tobi_ir_new(TOBI_IR_BLOCK, metas[i].aml_off, metas[i].aml_len);
2004 tobi_ir_set_name(file, inputs[i].name ? inputs[i].name : "<input>");
2005 tobi_rd rd;
2006 tobi_rd_init(&rd, inputs[i].data + metas[i].aml_off, metas[i].aml_len, metas[i].aml_off);
2007 (void)parse_term_list(&p, &rd, file, 0);
2008 tobi_ir_add(out->root, file);
2009 }
2010 }
2011 semantic_validate(&p, out->root);
2012 parser_free(&p);
2013 free(metas);
2014 return ok && !(strict && tobi_diag_has_error(&out->diag));
2015}
2016
2017void tobi_parse_result_free(tobi_parse_result *res) {
2018 tobi_ir_free(res->root);
2019 res->root = NULL;
2020 tobi_ns_free(&res->ns);
2021 tobi_diag_free(&res->diag);
2022}