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