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