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