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