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