ACPI AML decompiler w/ CFG recovery and structured pseudocode
29

Configure Feed

Select the types of activity you want to include in your feed.

tobi / src / p.c
75 kB 2028 lines
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}