ACPI AML decompiler w/ CFG recovery and structured pseudocode
29

Configure Feed

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

Initial commit

author vm.fail date (Jun 28, 2026, 5:29 AM UTC) commit e6dff78f
+4548
+10
.gitignore
··· 1 + build*/ 2 + *.o 3 + *.a 4 + *.gcda 5 + *.gcno 6 + *.gcov 7 + tobi 8 + tobi_test 9 + fz 10 + README.md~
+67
CMakeLists.txt
··· 1 + cmake_minimum_required(VERSION 3.16) 2 + project(tobi VERSION 0.1.0 LANGUAGES C) 3 + 4 + option(TOBI_WERROR "Build with warnings as errors" ON) 5 + option(TOBI_SAN "Build with AddressSanitizer and UndefinedBehaviorSanitizer" OFF) 6 + option(TOBI_FZ "Build fuzz smoke target" ON) 7 + 8 + set(CMAKE_C_STANDARD 11) 9 + set(CMAKE_C_STANDARD_REQUIRED ON) 10 + set(CMAKE_C_EXTENSIONS OFF) 11 + 12 + set(TOBI_COMMON 13 + src/rd.c src/p.c src/op.c src/nm.c src/ir.c src/cf.c src/dc.c 14 + src/da.c src/dg.c src/js.c src/mem.c src/str.c) 15 + 16 + add_library(tobilib ${TOBI_COMMON}) 17 + target_include_directories(tobilib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) 18 + target_compile_definitions(tobilib PUBLIC _POSIX_C_SOURCE=200809L) 19 + 20 + add_executable(tobi src/main.c) 21 + target_link_libraries(tobi PRIVATE tobilib) 22 + 23 + set(TOBI_WARN -Wall -Wextra -Wpedantic) 24 + if(TOBI_WERROR) 25 + list(APPEND TOBI_WARN -Werror) 26 + endif() 27 + target_compile_options(tobilib PRIVATE ${TOBI_WARN}) 28 + target_compile_options(tobi PRIVATE ${TOBI_WARN}) 29 + 30 + if(TOBI_SAN) 31 + set(SAN_FLAGS -fsanitize=address,undefined -fno-omit-frame-pointer) 32 + target_compile_definitions(tobilib PUBLIC TOBI_SAN_BUILD=1) 33 + target_compile_options(tobilib PRIVATE ${SAN_FLAGS}) 34 + target_compile_options(tobi PRIVATE ${SAN_FLAGS}) 35 + target_link_options(tobi PRIVATE ${SAN_FLAGS}) 36 + target_link_options(tobilib INTERFACE ${SAN_FLAGS}) 37 + endif() 38 + 39 + enable_testing() 40 + 41 + add_executable(tobi_test 42 + test/main.c test/t_rd.c test/t_nm.c test/t_p.c test/t_ir.c 43 + test/t_cf.c test/t_da.c test/t_dc.c test/t_js.c test/t_bad.c test/t_cli.c) 44 + target_link_libraries(tobi_test PRIVATE tobilib) 45 + target_compile_options(tobi_test PRIVATE ${TOBI_WARN}) 46 + target_compile_definitions(tobi_test PRIVATE 47 + TOBI_BIN="$<TARGET_FILE:tobi>" 48 + TOBI_SRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}") 49 + if(TOBI_SAN) 50 + target_compile_options(tobi_test PRIVATE ${SAN_FLAGS}) 51 + target_link_options(tobi_test PRIVATE ${SAN_FLAGS}) 52 + endif() 53 + 54 + foreach(t rd nm p ir cf da dc js bad cli) 55 + add_test(NAME ${t} COMMAND tobi_test ${t}) 56 + endforeach() 57 + 58 + if(TOBI_FZ) 59 + add_executable(fz fuzz/fz.c) 60 + target_link_libraries(fz PRIVATE tobilib) 61 + target_compile_options(fz PRIVATE ${TOBI_WARN}) 62 + target_compile_definitions(fz PRIVATE TOBI_SRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}") 63 + if(TOBI_SAN) 64 + target_compile_options(fz PRIVATE ${SAN_FLAGS}) 65 + target_link_options(fz PRIVATE ${SAN_FLAGS}) 66 + endif() 67 + endif()
+21
LICENSE
··· 1 + MIT License 2 + 3 + Copyright (c) 2026 tobi contributors 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy 6 + of this software and associated documentation files (the "Software"), to deal 7 + in the Software without restriction, including without limitation the rights 8 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 + copies of the Software, and to permit persons to whom the Software is 10 + furnished to do so, subject to the following conditions: 11 + 12 + The above copyright notice and this permission notice shall be included in all 13 + copies or substantial portions of the Software. 14 + 15 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 + SOFTWARE.
+120
README.md
··· 1 + # tobi 2 + 3 + ACPI AML decompiler that reads raw AML blobs and DSDT/SSDT tables, skips validated ACPI headers, and builds its own IR while doing conservative CFG recovery over package bounded control operations, then emits pseudocode, opcode disassembly, JSON IR, raw table metadata/Graphviz CFG view 4 + 5 + Unsupported bytes stay in the tree as explicit IR nodes with offsets anyway and malformed input goes through diagnostics rather than blowing up whereas the parser keeps bounded reader state as well so hostile or truncated input just fails as data instead of turning into out of bounds reads 6 + 7 + ## Build 8 + 9 + ```sh 10 + cmake -S . -B build -DTOBI_WERROR=ON -DTOBI_FZ=ON 11 + cmake --build build 12 + ctest --test-dir build --output-on-failure 13 + ``` 14 + 15 + The recommended validation path before landing parser changes is the sanitised configuration. ASan/UBSan are enabled to surface memory safety violations and UB during parser execution. LSan is intentionally disabled in the default environment because the test harness executes under ptrace and causes LeakSanitizer to abort before it can report meaningful allocation diagnostics: 16 + 17 + ```sh 18 + cmake -S . -B build-san -DCMAKE_C_COMPILER=clang -DTOBI_SAN=ON -DTOBI_WERROR=ON -DTOBI_FZ=ON 19 + cmake --build build-san 20 + ctest --test-dir build-san --output-on-failure 21 + ./build-san/fz 22 + ``` 23 + 24 + ## Usage 25 + 26 + ```sh 27 + tobi [opts] <file> [file...] 28 + ``` 29 + 30 + ```text 31 + --dis print opcode disam 32 + --json print parsed IR as json 33 + --dot print recovered CF as Graphviz DOT 34 + --raw print input/table metadata and AML offsets 35 + --strict treat malformed/unsupported constructs as hard errors 36 + --plain disable coloured diagnostics 37 + --help print usage 38 + --version print version 39 + ``` 40 + 41 + Default mode prints pseudocode: 42 + 43 + ```sh 44 + ./build/tobi sam/m0.aml 45 + ``` 46 + 47 + ```text 48 + method MTH0(args=1, serialized=false, sync=0) { 49 + Local0 = 0x2a; 50 + return Local0; 51 + } 52 + ``` 53 + 54 + Opcode view: 55 + 56 + ```sh 57 + ./build/tobi --dis sam/m0.aml 58 + ``` 59 + 60 + JSON IR: 61 + 62 + ```sh 63 + ./build/tobi --json sam/m0.aml 64 + ``` 65 + 66 + CFG view: 67 + 68 + ```sh 69 + ./build/tobi --dot sam/m1.aml 70 + ``` 71 + 72 + Multiple AML inputs share one namespace pre pass before parsing: 73 + 74 + ```sh 75 + ./build/tobi dsdt.aml ssdt1.aml ssdt2.aml 76 + ``` 77 + 78 + `--dis` and `--raw` still report each input independently because those modes are byte stream oriented and dont need the aggregate IR 79 + 80 + ## Input 81 + 82 + Accepted inputs: 83 + 84 + ```text 85 + raw AML bytecode 86 + DSDT tables with a 36 byte ACPI header 87 + SSDT tables with a 36 byte ACPI header 88 + ``` 89 + 90 + For tables it checks the signature and then validates the length at offset 4 before touching the AML body and verifies the ACPI checksum across the declared table length. Bad lengths are errors (obviously) and checksum mismatches are warnings by default and hard errors under `--strict` 91 + 92 + Every IR node stores: 93 + 94 + ```text 95 + kind 96 + source byte offset 97 + source byte length where known 98 + owned name/path/string payloads 99 + raw opcode for unknown or opcode expression nodes 100 + deterministic child array 101 + ``` 102 + 103 + ## Control flow 104 + 105 + AML IfOp, ElseOp, and WhileOp carry package bounded bodies so tobi reconstructs those directly while preserving the original lexical nesting. It doesnt invent higher level source constructs from control bytes whose semantics cant be established with confidence 106 + 107 + Any ambiguous branch residue is kept as explicit fallback labels or annotated comments tied to the originating byte offset instead. Break and Continue are only emitted when the surrounding lexical context makes them unambiguously valid loop control operations. Outside loops they remain explicit IR nodes with diagnostics rather than being rewritten into incorrect high level control flow 108 + 109 + ## Samples 110 + 111 + ```text 112 + m0 14 0c 4d 54 48 30 01 70 0a 2a 60 a4 60 113 + m1 14 15 49 46 45 30 01 a0 06 68 70 0a 01 60 a1 05 70 0a 02 60 a4 60 114 + m2 14 20 57 4c 4f 30 01 70 0a 03 60 a2 13 60 a0 06 68 70 0a 01 61 a1 05 70 0a 02 61 74 60 01 60 a4 61 115 + dev0 10 31 5c 5f 53 42 5f 5b 82 29 44 45 56 30 5b 80 52 45 47 30 01 0a 10 0a 04 5b 81 0b 52 45 47 30 00 46 4c 44 30 08 14 0b 52 44 4d 30 00 a4 46 4c 44 116 + ``` 117 + 118 + ## Limits 119 + 120 + This isnt a full ACPICA replacement. Namespace resolution is enough for method-call arity and readable paths but unresolved names can remain refs. Resource descriptors are recognised before theyre fully decoded. Less-structured AML stays as labelled IR/commentary instead of getting rewritten into invented source constructs. Unknown opcode coverage should shrink by adding metadata and operand parsers, not by special casing sample bytes
+70
fuzz/fz.c
··· 1 + #include "cf.h" 2 + #include "p.h" 3 + 4 + #include <stdint.h> 5 + #include <stdio.h> 6 + #include <stdlib.h> 7 + #include <string.h> 8 + 9 + #ifndef TOBI_SRC_DIR 10 + #define TOBI_SRC_DIR "." 11 + #endif 12 + 13 + static int run_one(const uint8_t *data, size_t len) { 14 + tobi_parse_result r; 15 + (void)tobi_parse(data, len, 0, &r); 16 + if (r.root) { 17 + (void)tobi_cf_recover(r.root, &r.diag); 18 + } 19 + tobi_parse_result_free(&r); 20 + return 0; 21 + } 22 + 23 + static void file_one(const char *path) { 24 + FILE *fp = fopen(path, "rb"); 25 + if (!fp) { 26 + return; 27 + } 28 + (void)fseek(fp, 0, SEEK_END); 29 + long n = ftell(fp); 30 + rewind(fp); 31 + if (n < 0) { 32 + fclose(fp); 33 + return; 34 + } 35 + uint8_t *buf = malloc((size_t)n ? (size_t)n : 1); 36 + if (!buf) { 37 + fclose(fp); 38 + abort(); 39 + } 40 + if ((size_t)n == fread(buf, 1, (size_t)n, fp)) { 41 + (void)run_one(buf, (size_t)n); 42 + for (size_t i = 0; i <= (size_t)n; i++) { 43 + (void)run_one(buf, i); 44 + } 45 + } 46 + free(buf); 47 + fclose(fp); 48 + } 49 + 50 + int main(void) { 51 + const char *seed[] = { 52 + TOBI_SRC_DIR "/fuzz/seed/m0.aml", 53 + TOBI_SRC_DIR "/fuzz/seed/m1.aml", 54 + TOBI_SRC_DIR "/fuzz/seed/m2.aml", 55 + TOBI_SRC_DIR "/fuzz/seed/bad0.aml", 56 + }; 57 + for (size_t i = 0; i < sizeof(seed) / sizeof(seed[0]); i++) { 58 + file_one(seed[i]); 59 + } 60 + uint32_t x = 0x12345678u; 61 + for (size_t n = 0; n < 512; n++) { 62 + uint8_t buf[256]; 63 + for (size_t i = 0; i < sizeof(buf); i++) { 64 + x = x * 1664525u + 1013904223u; 65 + buf[i] = (uint8_t)(x >> 24); 66 + } 67 + (void)run_one(buf, n % sizeof(buf)); 68 + } 69 + return 0; 70 + }
+1
fuzz/seed/bad0.aml
··· 1 + �MBAD�
+2
fuzz/seed/m0.aml
··· 1 +  MTH0p 2 + *`�`
+3
fuzz/seed/m1.aml
··· 1 + IFE0�hp 2 + `�p 3 + `�`
+4
fuzz/seed/m2.aml
··· 1 +  WLO0p 2 + `�`�hp 3 + a�p 4 + at``�a
+1
sam/bad0.aml
··· 1 + �MBAD�
sam/dev0.aml

This is a binary file and will not be displayed.

+2
sam/m0.aml
··· 1 +  MTH0p 2 + *`�`
+3
sam/m1.aml
··· 1 + IFE0�hp 2 + `�p 3 + `�`
+4
sam/m2.aml
··· 1 +  WLO0p 2 + `�`�hp 3 + a�p 4 + at``�a
+172
src/cf.c
··· 1 + #include "cf.h" 2 + 3 + #include "str.h" 4 + 5 + #include <stdio.h> 6 + #include <string.h> 7 + 8 + static int has_cfg_note(const tobi_ir *n, size_t off) { 9 + for (size_t i = 0; i < n->child_len; i++) { 10 + const tobi_ir *c = n->child[i]; 11 + if (c->kind == TOBI_IR_DIAG && c->off == off && c->str && strstr(c->str, "cfg fallback") != NULL) { 12 + return 1; 13 + } 14 + } 15 + return 0; 16 + } 17 + 18 + static int unclear_control(const tobi_ir *n) { 19 + if (n->kind != TOBI_IR_UNKNOWN) { 20 + return 0; 21 + } 22 + return n->raw_op == 0xa1u || n->raw_op == 0xccu || n->raw_op == 0x5b31u || n->raw_op == 0x5b32u || n->raw_op == 0u; 23 + } 24 + 25 + static void add_cfg_note(tobi_ir *parent, const tobi_ir *unknown) { 26 + if (!parent || !unknown || has_cfg_note(parent, unknown->off)) { 27 + return; 28 + } 29 + char msg[128]; 30 + (void)snprintf(msg, sizeof(msg), "cfg fallback label L_%04zx for unclear opcode 0x%x", unknown->off, unknown->raw_op); 31 + tobi_ir *note = tobi_ir_new(TOBI_IR_DIAG, unknown->off, unknown->len); 32 + note->raw_op = unknown->raw_op; 33 + tobi_ir_set_name(note, "cfg"); 34 + tobi_ir_set_str(note, msg); 35 + tobi_ir_add(parent, note); 36 + } 37 + 38 + static void walk(tobi_ir *n, tobi_diag_list *diag, unsigned loop_depth) { 39 + if (!n) { 40 + return; 41 + } 42 + if (n->kind == TOBI_IR_IF && n->child_len < 2) { 43 + tobi_diag_add(diag, TOBI_DIAG_ERROR, n->off, "if node lacks predicate or body"); 44 + } 45 + if (n->kind == TOBI_IR_WHILE && n->child_len < 2) { 46 + tobi_diag_add(diag, TOBI_DIAG_ERROR, n->off, "while node lacks predicate or body"); 47 + } 48 + if ((n->kind == TOBI_IR_BREAK || n->kind == TOBI_IR_CONTINUE) && loop_depth == 0) { 49 + tobi_diag_add(diag, TOBI_DIAG_WARN, n->off, "%s outside loop kept as explicit statement", 50 + n->kind == TOBI_IR_BREAK ? "break" : "continue"); 51 + } 52 + /* 53 + * AML IfOp, ElseOp, and WhileOp carry bounded package bodies, so those 54 + * become structured directly. Remaining branch-like residue is labelled 55 + * only when there is no bounded package evidence to justify a rewrite. 56 + */ 57 + size_t original_children = n->child_len; 58 + unsigned child_loop_depth = loop_depth + (n->kind == TOBI_IR_WHILE ? 1u : 0u); 59 + for (size_t i = 0; i < original_children; i++) { 60 + if ((n->kind == TOBI_IR_BLOCK || n->kind == TOBI_IR_ROOT) && unclear_control(n->child[i])) { 61 + add_cfg_note(n, n->child[i]); 62 + } 63 + walk(n->child[i], diag, child_loop_depth); 64 + } 65 + } 66 + 67 + int tobi_cf_recover(tobi_ir *root, tobi_diag_list *diag) { 68 + walk(root, diag, 0); 69 + return !tobi_diag_has_error(diag); 70 + } 71 + 72 + static void dot_escape(tobi_sb *sb, const char *s) { 73 + if (!s) { 74 + return; 75 + } 76 + for (const char *p = s; *p; p++) { 77 + if (*p == '"' || *p == '\\') { 78 + tobi_sb_ch(sb, '\\'); 79 + } 80 + if (*p == '\n' || *p == '\r') { 81 + tobi_sb_ch(sb, ' '); 82 + } else { 83 + tobi_sb_ch(sb, *p); 84 + } 85 + } 86 + } 87 + 88 + static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, size_t *next); 89 + 90 + static void dot_edge(tobi_sb *sb, size_t from, size_t to, const char *label) { 91 + tobi_sb_printf(sb, " n%zu -> n%zu", from, to); 92 + if (label && label[0]) { 93 + tobi_sb_add(sb, " [label=\""); 94 + dot_escape(sb, label); 95 + tobi_sb_add(sb, "\"]"); 96 + } 97 + tobi_sb_add(sb, ";\n"); 98 + } 99 + 100 + static void dot_block(tobi_sb *sb, const tobi_ir *b, size_t owner, const char *edge_label, size_t *next) { 101 + if (!b) { 102 + return; 103 + } 104 + size_t prev = owner; 105 + for (size_t i = 0; i < b->child_len; i++) { 106 + size_t id = dot_stmt(sb, b->child[i], next); 107 + dot_edge(sb, prev, id, i == 0 ? edge_label : "next"); 108 + prev = id; 109 + } 110 + } 111 + 112 + static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, size_t *next) { 113 + size_t id = (*next)++; 114 + tobi_sb_printf(sb, " n%zu [label=\"%s", id, tobi_ir_kind_name(n->kind)); 115 + if (n->name && n->name[0]) { 116 + tobi_sb_add(sb, "\\n"); 117 + dot_escape(sb, n->name); 118 + } else if (n->str && n->str[0]) { 119 + tobi_sb_add(sb, "\\n"); 120 + dot_escape(sb, n->str); 121 + } 122 + tobi_sb_printf(sb, "\\noff=0x%zx", n->off); 123 + if (n->kind == TOBI_IR_UNKNOWN) { 124 + tobi_sb_printf(sb, "\\nop=0x%x", n->raw_op); 125 + } 126 + tobi_sb_add(sb, "\"];\n"); 127 + if (n->kind == TOBI_IR_METHOD && n->child_len > 0) { 128 + dot_block(sb, n->child[0], id, "entry", next); 129 + } else if (n->kind == TOBI_IR_SCOPE || n->kind == TOBI_IR_DEVICE || n->kind == TOBI_IR_PROCESSOR) { 130 + if (n->child_len > 0) { 131 + dot_block(sb, n->child[0], id, "body", next); 132 + } 133 + } else if (n->kind == TOBI_IR_IF) { 134 + if (n->child_len > 1) { 135 + dot_block(sb, n->child[1], id, "then", next); 136 + } 137 + if (n->child_len > 2) { 138 + dot_block(sb, n->child[2], id, "else", next); 139 + } 140 + } else if (n->kind == TOBI_IR_WHILE) { 141 + if (n->child_len > 1) { 142 + dot_block(sb, n->child[1], id, "while-body", next); 143 + /* 144 + * The body is package-bounded in AML, so a back edge to the 145 + * loop header is justified even when the body internals are only 146 + * partially understood. 147 + */ 148 + tobi_sb_printf(sb, " n%zu -> n%zu [label=\"back\"];\n", (*next) - 1u, id); 149 + } 150 + } 151 + return id; 152 + } 153 + 154 + char *tobi_cf_dot(const tobi_ir *root) { 155 + tobi_sb sb; 156 + tobi_sb_init(&sb); 157 + tobi_sb_add(&sb, "digraph tobi_cfg {\n"); 158 + tobi_sb_add(&sb, " graph [rankdir=TB];\n"); 159 + tobi_sb_add(&sb, " node [shape=box,fontname=\"monospace\"];\n"); 160 + if (root) { 161 + size_t next = 0; 162 + size_t root_id = dot_stmt(&sb, root, &next); 163 + size_t prev = root_id; 164 + for (size_t i = 0; i < root->child_len; i++) { 165 + size_t id = dot_stmt(&sb, root->child[i], &next); 166 + dot_edge(&sb, prev, id, i == 0 ? "entry" : "next"); 167 + prev = id; 168 + } 169 + } 170 + tobi_sb_add(&sb, "}\n"); 171 + return tobi_sb_take(&sb); 172 + }
+13
src/cf.h
··· 1 + #ifndef TOBI_CF_H 2 + #define TOBI_CF_H 3 + 4 + #include "dg.h" 5 + #include "ir.h" 6 + 7 + /** Recover and validate structured control flow already delimited by AML packages. */ 8 + int tobi_cf_recover(tobi_ir *root, tobi_diag_list *diag); 9 + 10 + /** Emit a deterministic Graphviz DOT view of recovered control flow. */ 11 + char *tobi_cf_dot(const tobi_ir *root); 12 + 13 + #endif
+202
src/da.c
··· 1 + #include "da.h" 2 + 3 + #include "nm.h" 4 + #include "op.h" 5 + #include "rd.h" 6 + #include "str.h" 7 + 8 + #include <stdio.h> 9 + #include <stdlib.h> 10 + #include <string.h> 11 + 12 + static void raw_bytes(tobi_sb *sb, const tobi_rd *rd, size_t start, size_t end) { 13 + tobi_sb_add(sb, " raw="); 14 + size_t n = end > start ? end - start : 0; 15 + size_t max = n > 8 ? 8 : n; 16 + for (size_t i = 0; i < max && start + i < rd->len; i++) { 17 + tobi_sb_printf(sb, "%02x", rd->data[start + i]); 18 + if (i + 1 < max) { 19 + tobi_sb_ch(sb, ' '); 20 + } 21 + } 22 + if (n > max) { 23 + tobi_sb_add(sb, " ..."); 24 + } 25 + } 26 + 27 + static void walk(tobi_sb *sb, tobi_rd *rd, unsigned depth) { 28 + if (depth > 64) { 29 + tobi_sb_printf(sb, "0x%04zx: <nesting limit>\n", tobi_rd_off(rd)); 30 + rd->pos = rd->len; 31 + return; 32 + } 33 + while (tobi_rd_left(rd) > 0) { 34 + size_t start = rd->pos; 35 + size_t off = tobi_rd_off(rd); 36 + uint8_t op = 0; 37 + if (!tobi_rd_u8(rd, &op)) { 38 + break; 39 + } 40 + uint16_t code = op; 41 + const tobi_op *meta = NULL; 42 + if (op == 0x5b) { 43 + uint8_t ext = 0; 44 + if (!tobi_rd_u8(rd, &ext)) { 45 + tobi_sb_printf(sb, "0x%04zx: ExtOpPrefix truncated raw=5b\n", off); 46 + return; 47 + } 48 + code = (uint16_t)(0x5b00u | ext); 49 + meta = tobi_op_find_ext(ext); 50 + } else { 51 + meta = tobi_op_find(op); 52 + } 53 + if (!meta && (op == '\\' || op == '^' || op == 0x2e || op == 0x2f || tobi_nm_is_lead(op))) { 54 + rd->pos = start; 55 + char *nm = NULL; 56 + if (tobi_nm_namestring(rd, &nm)) { 57 + tobi_sb_printf(sb, "0x%04zx: NameString name=%s", off, nm); 58 + raw_bytes(sb, rd, start, rd->pos); 59 + tobi_sb_ch(sb, '\n'); 60 + free(nm); 61 + continue; 62 + } 63 + rd->pos = start + 1; 64 + } 65 + const char *name = meta ? meta->name : "UnknownOp"; 66 + const char *operands = tobi_op_operands(code); 67 + int decoded_pseudo = 0; 68 + char extra[128]; 69 + extra[0] = '\0'; 70 + if (op == 0x0a) { 71 + uint8_t v = 0; 72 + if (tobi_rd_u8(rd, &v)) { 73 + (void)snprintf(extra, sizeof(extra), " value=0x%x", (unsigned)v); 74 + } 75 + } else if (op == 0x0b) { 76 + uint16_t v = 0; 77 + if (tobi_rd_u16(rd, &v)) { 78 + (void)snprintf(extra, sizeof(extra), " value=0x%x", (unsigned)v); 79 + } 80 + } else if (op == 0x0c) { 81 + uint32_t v = 0; 82 + if (tobi_rd_u32(rd, &v)) { 83 + (void)snprintf(extra, sizeof(extra), " value=0x%x", (unsigned)v); 84 + } 85 + } else if (op == 0x0e) { 86 + uint64_t v = 0; 87 + if (tobi_rd_u64(rd, &v)) { 88 + (void)snprintf(extra, sizeof(extra), " value=0x%llx", (unsigned long long)v); 89 + } 90 + } else if (op == 0x0d) { 91 + size_t s = rd->pos; 92 + while (rd->pos < rd->len && rd->data[rd->pos] != 0) { 93 + rd->pos++; 94 + } 95 + size_t n = rd->pos - s; 96 + if (rd->pos < rd->len) { 97 + rd->pos++; 98 + } 99 + (void)snprintf(extra, sizeof(extra), " strlen=%zu", n); 100 + } else if (op >= 0x60 && op <= 0x67) { 101 + (void)snprintf(extra, sizeof(extra), " name=Local%u", (unsigned)(op - 0x60)); 102 + name = "LocalObj"; 103 + decoded_pseudo = 1; 104 + } else if (op >= 0x68 && op <= 0x6e) { 105 + (void)snprintf(extra, sizeof(extra), " name=Arg%u", (unsigned)(op - 0x68)); 106 + name = "ArgObj"; 107 + decoded_pseudo = 1; 108 + } else if (op == 0x08 || op == 0x06 || code == 0x5b80 || code == 0x5b01 || code == 0x5b02) { 109 + char *nm = NULL; 110 + if (tobi_nm_namestring(rd, &nm)) { 111 + (void)snprintf(extra, sizeof(extra), " name=%s", nm); 112 + free(nm); 113 + } 114 + if (op == 0x06) { 115 + char *dst = NULL; 116 + if (tobi_nm_namestring(rd, &dst)) { 117 + size_t used = strlen(extra); 118 + (void)snprintf(extra + used, sizeof(extra) - used, " target=%s", dst); 119 + free(dst); 120 + } 121 + } 122 + } 123 + tobi_sb_printf(sb, "0x%04zx: %s", off, name); 124 + if (operands && operands[0]) { 125 + tobi_sb_printf(sb, " operands=%s", operands); 126 + } 127 + if (meta && meta->has_pkg_len) { 128 + size_t pkg_start = rd->pos; 129 + size_t pkg_len = 0; 130 + size_t enc = 0; 131 + if (!tobi_rd_pkg_len(rd, &pkg_len, &enc)) { 132 + raw_bytes(sb, rd, start, rd->pos); 133 + tobi_sb_add(sb, " malformed-pkglen\n"); 134 + return; 135 + } 136 + size_t body_start = rd->pos; 137 + size_t end = pkg_start + pkg_len; 138 + if (end > rd->len || end < body_start) { 139 + end = rd->len; 140 + tobi_sb_add(sb, " truncated"); 141 + } 142 + tobi_sb_printf(sb, " pkglen=%zu", pkg_len); 143 + if (code == 0x10 || code == 0x14 || code == 0x5b82 || code == 0x5b83 || code == 0x5b81) { 144 + tobi_rd copy = *rd; 145 + char *nm = NULL; 146 + if (tobi_nm_namestring(&copy, &nm)) { 147 + tobi_sb_add(sb, " name="); 148 + tobi_sb_add(sb, nm); 149 + free(nm); 150 + } 151 + } 152 + raw_bytes(sb, rd, start, body_start); 153 + tobi_sb_ch(sb, '\n'); 154 + tobi_rd sub; 155 + tobi_rd_init(&sub, rd->data + body_start, end - body_start, rd->base + body_start); 156 + if (code == 0x14) { 157 + char *nm = NULL; 158 + if (tobi_nm_namestring(&sub, &nm)) { 159 + free(nm); 160 + (void)tobi_rd_skip(&sub, 1); 161 + } 162 + } else if (code == 0x10 || code == 0x5b82) { 163 + char *nm = NULL; 164 + if (tobi_nm_namestring(&sub, &nm)) { 165 + free(nm); 166 + } 167 + } else if (code == 0x5b81) { 168 + char *nm = NULL; 169 + if (tobi_nm_namestring(&sub, &nm)) { 170 + free(nm); 171 + (void)tobi_rd_skip(&sub, 1); 172 + } 173 + } else if (code == 0xa0 || code == 0xa2) { 174 + (void)tobi_rd_skip(&sub, 1); 175 + } 176 + walk(sb, &sub, depth + 1); 177 + rd->pos = end; 178 + } else { 179 + tobi_sb_add(sb, extra); 180 + raw_bytes(sb, rd, start, rd->pos); 181 + if (!meta && !decoded_pseudo) { 182 + tobi_sb_printf(sb, " opcode=0x%x", (unsigned)code); 183 + } 184 + tobi_sb_ch(sb, '\n'); 185 + } 186 + } 187 + } 188 + 189 + char *tobi_da_emit(const uint8_t *data, size_t len, const tobi_input_meta *meta) { 190 + tobi_sb sb; 191 + tobi_sb_init(&sb); 192 + size_t off = meta ? meta->aml_off : 0; 193 + size_t alen = meta ? meta->aml_len : len; 194 + if (off > len || alen > len - off) { 195 + off = 0; 196 + alen = len; 197 + } 198 + tobi_rd rd; 199 + tobi_rd_init(&rd, data + off, alen, off); 200 + walk(&sb, &rd, 0); 201 + return tobi_sb_take(&sb); 202 + }
+12
src/da.h
··· 1 + #ifndef TOBI_DA_H 2 + #define TOBI_DA_H 3 + 4 + #include "p.h" 5 + 6 + #include <stddef.h> 7 + #include <stdint.h> 8 + 9 + /** Emit opcode-level AML disassembly for a raw blob or ACPI table. */ 10 + char *tobi_da_emit(const uint8_t *data, size_t len, const tobi_input_meta *meta); 11 + 12 + #endif
+293
src/dc.c
··· 1 + #include "dc.h" 2 + 3 + #include "mem.h" 4 + #include "str.h" 5 + 6 + #include <stdio.h> 7 + 8 + static void indent(tobi_sb *sb, unsigned n) { 9 + for (unsigned i = 0; i < n; i++) { 10 + tobi_sb_add(sb, " "); 11 + } 12 + } 13 + 14 + static void expr(tobi_sb *sb, const tobi_ir *n); 15 + static void stmt(tobi_sb *sb, const tobi_ir *n, unsigned ind); 16 + 17 + static const char *binop(const char *name) { 18 + if (!name) { 19 + return "?"; 20 + } 21 + if (name[0] == 'e' && name[1] == 'q') return "=="; 22 + if (name[0] == 'g' && name[1] == 't') return ">"; 23 + if (name[0] == 'l' && name[1] == 't') return "<"; 24 + if (name[0] == 'a' && name[1] == 'd') return "+"; 25 + if (name[0] == 's' && name[1] == 'u') return "-"; 26 + if (name[0] == 'm' && name[1] == 'u') return "*"; 27 + if (name[0] == 'd' && name[1] == 'i') return "/"; 28 + if (name[0] == 'a' && name[1] == 'n') return "&"; 29 + if (name[0] == 'o' && name[1] == 'r') return "|"; 30 + if (name[0] == 'x' && name[1] == 'o') return "^"; 31 + if (name[0] == 'l' && name[1] == 'a') return "&&"; 32 + if (name[0] == 'l' && name[1] == 'o') return "||"; 33 + return name; 34 + } 35 + 36 + static void expr(tobi_sb *sb, const tobi_ir *n) { 37 + if (!n) { 38 + tobi_sb_add(sb, "<missing>"); 39 + return; 40 + } 41 + switch (n->kind) { 42 + case TOBI_IR_INTEGER: 43 + tobi_sb_printf(sb, "0x%llx", (unsigned long long)n->value); 44 + break; 45 + case TOBI_IR_STRING: 46 + tobi_sb_ch(sb, '"'); 47 + if (n->str) { 48 + for (const char *p = n->str; *p; p++) { 49 + if (*p == '"' || *p == '\\') { 50 + tobi_sb_ch(sb, '\\'); 51 + } 52 + tobi_sb_ch(sb, *p); 53 + } 54 + } 55 + tobi_sb_ch(sb, '"'); 56 + break; 57 + case TOBI_IR_REF: 58 + tobi_sb_add(sb, n->name ? n->name : "<ref>"); 59 + break; 60 + case TOBI_IR_CALL: 61 + tobi_sb_add(sb, n->name ? n->name : "<call>"); 62 + tobi_sb_ch(sb, '('); 63 + for (size_t i = 0; i < n->child_len; i++) { 64 + if (i) { 65 + tobi_sb_add(sb, ", "); 66 + } 67 + expr(sb, n->child[i]); 68 + } 69 + tobi_sb_ch(sb, ')'); 70 + break; 71 + case TOBI_IR_EXPR: 72 + if (n->child_len == 1) { 73 + tobi_sb_printf(sb, "%s(", n->name ? n->name : "op"); 74 + expr(sb, n->child[0]); 75 + tobi_sb_ch(sb, ')'); 76 + } else if (n->child_len >= 2) { 77 + tobi_sb_ch(sb, '('); 78 + expr(sb, n->child[0]); 79 + tobi_sb_printf(sb, " %s ", binop(n->name)); 80 + expr(sb, n->child[1]); 81 + tobi_sb_ch(sb, ')'); 82 + } else { 83 + tobi_sb_add(sb, n->name ? n->name : "<expr>"); 84 + } 85 + break; 86 + case TOBI_IR_BUFFER: 87 + tobi_sb_printf(sb, "Buffer[%s]", n->str ? n->str : ""); 88 + break; 89 + case TOBI_IR_PACKAGE: 90 + tobi_sb_ch(sb, '{'); 91 + for (size_t i = 0; i < n->child_len; i++) { 92 + if (i) { 93 + tobi_sb_add(sb, ", "); 94 + } 95 + expr(sb, n->child[i]); 96 + } 97 + tobi_sb_ch(sb, '}'); 98 + break; 99 + case TOBI_IR_STORE: 100 + if (n->child_len >= 2) { 101 + expr(sb, n->child[1]); 102 + tobi_sb_add(sb, " = "); 103 + expr(sb, n->child[0]); 104 + } else { 105 + tobi_sb_add(sb, "store(?)"); 106 + } 107 + break; 108 + case TOBI_IR_UNKNOWN: 109 + tobi_sb_printf(sb, "unknown_0x%x", n->raw_op); 110 + break; 111 + default: 112 + tobi_sb_add(sb, n->name ? n->name : tobi_ir_kind_name(n->kind)); 113 + break; 114 + } 115 + } 116 + 117 + static void block(tobi_sb *sb, const tobi_ir *b, unsigned ind) { 118 + if (!b) { 119 + return; 120 + } 121 + for (size_t i = 0; i < b->child_len; i++) { 122 + stmt(sb, b->child[i], ind); 123 + } 124 + } 125 + 126 + static void stmt(tobi_sb *sb, const tobi_ir *n, unsigned ind) { 127 + if (!n) { 128 + return; 129 + } 130 + switch (n->kind) { 131 + case TOBI_IR_SCOPE: 132 + indent(sb, ind); 133 + tobi_sb_printf(sb, "scope %s {\n", n->path ? n->path : n->name); 134 + block(sb, n->child_len ? n->child[0] : NULL, ind + 1); 135 + indent(sb, ind); 136 + tobi_sb_add(sb, "}\n"); 137 + break; 138 + case TOBI_IR_DEVICE: 139 + indent(sb, ind); 140 + tobi_sb_printf(sb, "device %s {\n", n->path ? n->path : n->name); 141 + block(sb, n->child_len ? n->child[0] : NULL, ind + 1); 142 + indent(sb, ind); 143 + tobi_sb_add(sb, "}\n"); 144 + break; 145 + case TOBI_IR_METHOD: 146 + indent(sb, ind); 147 + tobi_sb_printf(sb, "method %s(args=%u, serialized=%s, sync=%u) {\n", 148 + n->name ? n->name : "<method>", n->method_args, 149 + n->method_serialized ? "true" : "false", n->method_sync); 150 + block(sb, n->child_len ? n->child[0] : NULL, ind + 1); 151 + indent(sb, ind); 152 + tobi_sb_add(sb, "}\n"); 153 + break; 154 + case TOBI_IR_NAME: 155 + indent(sb, ind); 156 + tobi_sb_printf(sb, "name %s = ", n->path ? n->path : n->name); 157 + if (n->child_len) { 158 + expr(sb, n->child[0]); 159 + } else { 160 + tobi_sb_add(sb, "<missing>"); 161 + } 162 + tobi_sb_add(sb, ";\n"); 163 + break; 164 + case TOBI_IR_OPREGION: 165 + indent(sb, ind); 166 + tobi_sb_printf(sb, "opregion %s space=%llu offset=", n->path ? n->path : n->name, (unsigned long long)n->value); 167 + expr(sb, n->child_len > 0 ? n->child[0] : NULL); 168 + tobi_sb_add(sb, " length="); 169 + expr(sb, n->child_len > 1 ? n->child[1] : NULL); 170 + tobi_sb_add(sb, ";\n"); 171 + break; 172 + case TOBI_IR_FIELD: 173 + indent(sb, ind); 174 + tobi_sb_printf(sb, "%s %s flags=0x%llx {\n", n->str ? n->str : "field", 175 + n->name ? n->name : "<field>", (unsigned long long)n->value); 176 + for (size_t i = 0; i < n->child_len; i++) { 177 + indent(sb, ind + 1); 178 + if (n->child[i]->kind == TOBI_IR_FIELD_ELEM) { 179 + tobi_sb_printf(sb, "%s: %llu", n->child[i]->name ? n->child[i]->name : "<field>", 180 + (unsigned long long)n->child[i]->value); 181 + if (n->child[i]->str) { 182 + tobi_sb_printf(sb, " /* %s */", n->child[i]->str); 183 + } 184 + tobi_sb_add(sb, ";\n"); 185 + } else { 186 + tobi_sb_add(sb, "bank_value = "); 187 + expr(sb, n->child[i]); 188 + tobi_sb_add(sb, ";\n"); 189 + } 190 + } 191 + indent(sb, ind); 192 + tobi_sb_add(sb, "}\n"); 193 + break; 194 + case TOBI_IR_STORE: 195 + indent(sb, ind); 196 + expr(sb, n); 197 + tobi_sb_add(sb, ";\n"); 198 + break; 199 + case TOBI_IR_EXPR: 200 + case TOBI_IR_CALL: 201 + indent(sb, ind); 202 + if (n->child_len >= 3 && n->kind == TOBI_IR_EXPR) { 203 + expr(sb, n->child[2]); 204 + tobi_sb_add(sb, " = "); 205 + tobi_sb_ch(sb, '('); 206 + expr(sb, n->child[0]); 207 + tobi_sb_printf(sb, " %s ", binop(n->name)); 208 + expr(sb, n->child[1]); 209 + tobi_sb_ch(sb, ')'); 210 + } else { 211 + expr(sb, n); 212 + } 213 + tobi_sb_add(sb, ";\n"); 214 + break; 215 + case TOBI_IR_RETURN: 216 + indent(sb, ind); 217 + tobi_sb_add(sb, "return"); 218 + if (n->child_len) { 219 + tobi_sb_ch(sb, ' '); 220 + expr(sb, n->child[0]); 221 + } 222 + tobi_sb_add(sb, ";\n"); 223 + break; 224 + case TOBI_IR_IF: 225 + indent(sb, ind); 226 + tobi_sb_add(sb, "if ("); 227 + expr(sb, n->child_len ? n->child[0] : NULL); 228 + tobi_sb_add(sb, ") {\n"); 229 + block(sb, n->child_len > 1 ? n->child[1] : NULL, ind + 1); 230 + indent(sb, ind); 231 + if (n->child_len > 2) { 232 + tobi_sb_add(sb, "} else {\n"); 233 + block(sb, n->child[2], ind + 1); 234 + indent(sb, ind); 235 + } 236 + tobi_sb_add(sb, "}\n"); 237 + break; 238 + case TOBI_IR_WHILE: 239 + indent(sb, ind); 240 + tobi_sb_add(sb, "while ("); 241 + expr(sb, n->child_len ? n->child[0] : NULL); 242 + tobi_sb_add(sb, ") {\n"); 243 + block(sb, n->child_len > 1 ? n->child[1] : NULL, ind + 1); 244 + indent(sb, ind); 245 + tobi_sb_add(sb, "}\n"); 246 + break; 247 + case TOBI_IR_BREAK: 248 + indent(sb, ind); 249 + tobi_sb_add(sb, "break;\n"); 250 + break; 251 + case TOBI_IR_CONTINUE: 252 + indent(sb, ind); 253 + tobi_sb_add(sb, "continue;\n"); 254 + break; 255 + case TOBI_IR_UNKNOWN: 256 + indent(sb, ind); 257 + tobi_sb_printf(sb, "/* unknown opcode 0x%x at 0x%zx", n->raw_op, n->off); 258 + if (n->str) { 259 + tobi_sb_printf(sb, ": %s", n->str); 260 + } 261 + tobi_sb_add(sb, " */\n"); 262 + break; 263 + case TOBI_IR_DIAG: 264 + indent(sb, ind); 265 + tobi_sb_printf(sb, "/* %s at 0x%zx */\n", n->str ? n->str : "diagnostic", n->off); 266 + break; 267 + case TOBI_IR_BLOCK: 268 + block(sb, n, ind); 269 + break; 270 + default: 271 + indent(sb, ind); 272 + tobi_sb_printf(sb, "/* %s at 0x%zx */\n", tobi_ir_kind_name(n->kind), n->off); 273 + break; 274 + } 275 + } 276 + 277 + char *tobi_dc_emit(const tobi_ir *root, const tobi_diag_list *diag) { 278 + tobi_sb sb; 279 + tobi_sb_init(&sb); 280 + if (diag) { 281 + for (size_t i = 0; i < diag->len; i++) { 282 + if (diag->items[i].level == TOBI_DIAG_WARN) { 283 + tobi_sb_printf(&sb, "/* warning at 0x%zx: %s */\n", diag->items[i].off, diag->items[i].msg); 284 + } 285 + } 286 + } 287 + if (root) { 288 + for (size_t i = 0; i < root->child_len; i++) { 289 + stmt(&sb, root->child[i], 0); 290 + } 291 + } 292 + return tobi_sb_take(&sb); 293 + }
+10
src/dc.h
··· 1 + #ifndef TOBI_DC_H 2 + #define TOBI_DC_H 3 + 4 + #include "dg.h" 5 + #include "ir.h" 6 + 7 + /** Emit deterministic readable pseudocode for an IR tree. */ 8 + char *tobi_dc_emit(const tobi_ir *root, const tobi_diag_list *diag); 9 + 10 + #endif
+60
src/dg.c
··· 1 + #include "dg.h" 2 + 3 + #include "mem.h" 4 + 5 + #include <stdarg.h> 6 + #include <stdio.h> 7 + #include <stdlib.h> 8 + 9 + void tobi_diag_init(tobi_diag_list *dl) { 10 + dl->items = NULL; 11 + dl->len = 0; 12 + dl->cap = 0; 13 + } 14 + 15 + void tobi_diag_free(tobi_diag_list *dl) { 16 + for (size_t i = 0; i < dl->len; i++) { 17 + free(dl->items[i].msg); 18 + } 19 + free(dl->items); 20 + dl->items = NULL; 21 + dl->len = 0; 22 + dl->cap = 0; 23 + } 24 + 25 + void tobi_diag_add(tobi_diag_list *dl, tobi_diag_level level, size_t off, const char *fmt, ...) { 26 + if (dl->len == dl->cap) { 27 + dl->cap = dl->cap ? dl->cap * 2 : 8; 28 + dl->items = tobi_xrealloc(dl->items, dl->cap * sizeof(dl->items[0])); 29 + } 30 + va_list ap; 31 + va_start(ap, fmt); 32 + va_list cp; 33 + va_copy(cp, ap); 34 + int n = vsnprintf(NULL, 0, fmt, cp); 35 + va_end(cp); 36 + if (n < 0) { 37 + va_end(ap); 38 + n = 0; 39 + } 40 + char *msg = tobi_xmalloc((size_t)n + 1); 41 + (void)vsnprintf(msg, (size_t)n + 1, fmt, ap); 42 + va_end(ap); 43 + dl->items[dl->len].level = level; 44 + dl->items[dl->len].off = off; 45 + dl->items[dl->len].msg = msg; 46 + dl->len++; 47 + } 48 + 49 + int tobi_diag_has_error(const tobi_diag_list *dl) { 50 + for (size_t i = 0; i < dl->len; i++) { 51 + if (dl->items[i].level == TOBI_DIAG_ERROR) { 52 + return 1; 53 + } 54 + } 55 + return 0; 56 + } 57 + 58 + const char *tobi_diag_level_name(tobi_diag_level level) { 59 + return level == TOBI_DIAG_ERROR ? "error" : "warning"; 60 + }
+38
src/dg.h
··· 1 + #ifndef TOBI_DG_H 2 + #define TOBI_DG_H 3 + 4 + #include <stddef.h> 5 + 6 + typedef enum tobi_diag_level { 7 + TOBI_DIAG_WARN, 8 + TOBI_DIAG_ERROR 9 + } tobi_diag_level; 10 + 11 + typedef struct tobi_diag { 12 + tobi_diag_level level; 13 + size_t off; 14 + char *msg; 15 + } tobi_diag; 16 + 17 + typedef struct tobi_diag_list { 18 + tobi_diag *items; 19 + size_t len; 20 + size_t cap; 21 + } tobi_diag_list; 22 + 23 + /** Initialise an empty diagnostic list. */ 24 + void tobi_diag_init(tobi_diag_list *dl); 25 + 26 + /** Free all diagnostics and list storage. */ 27 + void tobi_diag_free(tobi_diag_list *dl); 28 + 29 + /** Add a formatted diagnostic at a source offset. */ 30 + void tobi_diag_add(tobi_diag_list *dl, tobi_diag_level level, size_t off, const char *fmt, ...); 31 + 32 + /** Return non-zero when any diagnostic is an error. */ 33 + int tobi_diag_has_error(const tobi_diag_list *dl); 34 + 35 + /** Return a stable textual name for a diagnostic level. */ 36 + const char *tobi_diag_level_name(tobi_diag_level level); 37 + 38 + #endif
+112
src/ir.c
··· 1 + #include "ir.h" 2 + 3 + #include "mem.h" 4 + 5 + #include <stdlib.h> 6 + 7 + tobi_ir *tobi_ir_new(tobi_ir_kind kind, size_t off, size_t len) { 8 + tobi_ir *n = tobi_xcalloc(1, sizeof(*n)); 9 + n->kind = kind; 10 + n->off = off; 11 + n->len = len; 12 + return n; 13 + } 14 + 15 + void tobi_ir_free(tobi_ir *node) { 16 + if (!node) { 17 + return; 18 + } 19 + for (size_t i = 0; i < node->child_len; i++) { 20 + tobi_ir_free(node->child[i]); 21 + } 22 + free(node->child); 23 + free(node->name); 24 + free(node->path); 25 + free(node->str); 26 + free(node); 27 + } 28 + 29 + void tobi_ir_add(tobi_ir *parent, tobi_ir *child) { 30 + if (!parent || !child) { 31 + return; 32 + } 33 + if (parent->child_len == parent->child_cap) { 34 + parent->child_cap = parent->child_cap ? parent->child_cap * 2 : 4; 35 + parent->child = tobi_xrealloc(parent->child, parent->child_cap * sizeof(parent->child[0])); 36 + } 37 + parent->child[parent->child_len++] = child; 38 + } 39 + 40 + void tobi_ir_set_name(tobi_ir *node, const char *s) { 41 + free(node->name); 42 + node->name = tobi_xstrdup(s ? s : ""); 43 + } 44 + 45 + void tobi_ir_set_path(tobi_ir *node, const char *s) { 46 + free(node->path); 47 + node->path = tobi_xstrdup(s ? s : ""); 48 + } 49 + 50 + void tobi_ir_set_str(tobi_ir *node, const char *s) { 51 + free(node->str); 52 + node->str = tobi_xstrdup(s ? s : ""); 53 + } 54 + 55 + const char *tobi_ir_kind_name(tobi_ir_kind kind) { 56 + switch (kind) { 57 + case TOBI_IR_ROOT: return "root"; 58 + case TOBI_IR_BLOCK: return "block"; 59 + case TOBI_IR_SCOPE: return "scope"; 60 + case TOBI_IR_DEVICE: return "device"; 61 + case TOBI_IR_METHOD: return "method"; 62 + case TOBI_IR_PROCESSOR: return "processor"; 63 + case TOBI_IR_NAME: return "name"; 64 + case TOBI_IR_ALIAS: return "alias"; 65 + case TOBI_IR_OPREGION: return "opregion"; 66 + case TOBI_IR_FIELD: return "field"; 67 + case TOBI_IR_FIELD_ELEM: return "field_elem"; 68 + case TOBI_IR_MUTEX: return "mutex"; 69 + case TOBI_IR_EVENT: return "event"; 70 + case TOBI_IR_BUFFER: return "buffer"; 71 + case TOBI_IR_PACKAGE: return "package"; 72 + case TOBI_IR_INTEGER: return "integer"; 73 + case TOBI_IR_STRING: return "string"; 74 + case TOBI_IR_REF: return "ref"; 75 + case TOBI_IR_CALL: return "call"; 76 + case TOBI_IR_EXPR: return "expr"; 77 + case TOBI_IR_STORE: return "store"; 78 + case TOBI_IR_RETURN: return "return"; 79 + case TOBI_IR_IF: return "if"; 80 + case TOBI_IR_WHILE: return "while"; 81 + case TOBI_IR_BREAK: return "break"; 82 + case TOBI_IR_CONTINUE: return "continue"; 83 + case TOBI_IR_UNKNOWN: return "unknown"; 84 + case TOBI_IR_DIAG: return "diag"; 85 + } 86 + return "invalid"; 87 + } 88 + 89 + tobi_ir *tobi_ir_find_child(tobi_ir *node, tobi_ir_kind kind) { 90 + if (!node) { 91 + return NULL; 92 + } 93 + for (size_t i = 0; i < node->child_len; i++) { 94 + if (node->child[i]->kind == kind) { 95 + return node->child[i]; 96 + } 97 + } 98 + return NULL; 99 + } 100 + 101 + size_t tobi_ir_count_kind(const tobi_ir *node, tobi_ir_kind kind) { 102 + size_t n = 0; 103 + if (!node) { 104 + return 0; 105 + } 106 + for (size_t i = 0; i < node->child_len; i++) { 107 + if (node->child[i]->kind == kind) { 108 + n++; 109 + } 110 + } 111 + return n; 112 + }
+82
src/ir.h
··· 1 + #ifndef TOBI_IR_H 2 + #define TOBI_IR_H 3 + 4 + #include <stddef.h> 5 + #include <stdint.h> 6 + 7 + typedef enum tobi_ir_kind { 8 + TOBI_IR_ROOT, 9 + TOBI_IR_BLOCK, 10 + TOBI_IR_SCOPE, 11 + TOBI_IR_DEVICE, 12 + TOBI_IR_METHOD, 13 + TOBI_IR_PROCESSOR, 14 + TOBI_IR_NAME, 15 + TOBI_IR_ALIAS, 16 + TOBI_IR_OPREGION, 17 + TOBI_IR_FIELD, 18 + TOBI_IR_FIELD_ELEM, 19 + TOBI_IR_MUTEX, 20 + TOBI_IR_EVENT, 21 + TOBI_IR_BUFFER, 22 + TOBI_IR_PACKAGE, 23 + TOBI_IR_INTEGER, 24 + TOBI_IR_STRING, 25 + TOBI_IR_REF, 26 + TOBI_IR_CALL, 27 + TOBI_IR_EXPR, 28 + TOBI_IR_STORE, 29 + TOBI_IR_RETURN, 30 + TOBI_IR_IF, 31 + TOBI_IR_WHILE, 32 + TOBI_IR_BREAK, 33 + TOBI_IR_CONTINUE, 34 + TOBI_IR_UNKNOWN, 35 + TOBI_IR_DIAG 36 + } tobi_ir_kind; 37 + 38 + typedef struct tobi_ir { 39 + tobi_ir_kind kind; 40 + size_t off; 41 + size_t len; 42 + char *name; 43 + char *path; 44 + char *str; 45 + uint64_t value; 46 + uint32_t raw_op; 47 + unsigned method_args; 48 + unsigned method_serialized; 49 + unsigned method_sync; 50 + struct tobi_ir **child; 51 + size_t child_len; 52 + size_t child_cap; 53 + } tobi_ir; 54 + 55 + /** Allocate a new IR node with source offset and length. */ 56 + tobi_ir *tobi_ir_new(tobi_ir_kind kind, size_t off, size_t len); 57 + 58 + /** Recursively free an IR tree. */ 59 + void tobi_ir_free(tobi_ir *node); 60 + 61 + /** Append child to parent, transferring ownership. */ 62 + void tobi_ir_add(tobi_ir *parent, tobi_ir *child); 63 + 64 + /** Set node name by copying s. */ 65 + void tobi_ir_set_name(tobi_ir *node, const char *s); 66 + 67 + /** Set resolved namespace path by copying s. */ 68 + void tobi_ir_set_path(tobi_ir *node, const char *s); 69 + 70 + /** Set string payload by copying s. */ 71 + void tobi_ir_set_str(tobi_ir *node, const char *s); 72 + 73 + /** Return a stable textual name for an IR kind. */ 74 + const char *tobi_ir_kind_name(tobi_ir_kind kind); 75 + 76 + /** Return first direct child of kind or NULL when absent. */ 77 + tobi_ir *tobi_ir_find_child(tobi_ir *node, tobi_ir_kind kind); 78 + 79 + /** Count direct children with a specific kind. */ 80 + size_t tobi_ir_count_kind(const tobi_ir *node, tobi_ir_kind kind); 81 + 82 + #endif
+94
src/js.c
··· 1 + #include "js.h" 2 + 3 + #include "str.h" 4 + 5 + static void comma(tobi_sb *sb, int *first) { 6 + if (*first) { 7 + *first = 0; 8 + } else { 9 + tobi_sb_ch(sb, ','); 10 + } 11 + } 12 + 13 + static void node(tobi_sb *sb, const tobi_ir *n) { 14 + int first = 1; 15 + tobi_sb_ch(sb, '{'); 16 + comma(sb, &first); 17 + tobi_sb_add(sb, "\"kind\":"); 18 + tobi_json_string(sb, tobi_ir_kind_name(n->kind)); 19 + comma(sb, &first); 20 + tobi_sb_printf(sb, "\"offset\":%zu", n->off); 21 + comma(sb, &first); 22 + tobi_sb_printf(sb, "\"length\":%zu", n->len); 23 + if (n->name) { 24 + comma(sb, &first); 25 + tobi_sb_add(sb, "\"name\":"); 26 + tobi_json_string(sb, n->name); 27 + } 28 + if (n->path) { 29 + comma(sb, &first); 30 + tobi_sb_add(sb, "\"path\":"); 31 + tobi_json_string(sb, n->path); 32 + } 33 + if (n->str) { 34 + comma(sb, &first); 35 + tobi_sb_add(sb, "\"string\":"); 36 + tobi_json_string(sb, n->str); 37 + } 38 + if (n->kind == TOBI_IR_INTEGER || n->kind == TOBI_IR_PACKAGE || n->kind == TOBI_IR_FIELD || 39 + n->kind == TOBI_IR_FIELD_ELEM || n->kind == TOBI_IR_CALL || n->kind == TOBI_IR_OPREGION) { 40 + comma(sb, &first); 41 + tobi_sb_printf(sb, "\"value\":%llu", (unsigned long long)n->value); 42 + } 43 + if (n->raw_op) { 44 + comma(sb, &first); 45 + tobi_sb_printf(sb, "\"raw_opcode\":%u", n->raw_op); 46 + } 47 + if (n->kind == TOBI_IR_METHOD) { 48 + comma(sb, &first); 49 + tobi_sb_printf(sb, "\"args\":%u", n->method_args); 50 + comma(sb, &first); 51 + tobi_sb_printf(sb, "\"serialized\":%s", n->method_serialized ? "true" : "false"); 52 + comma(sb, &first); 53 + tobi_sb_printf(sb, "\"sync\":%u", n->method_sync); 54 + } 55 + comma(sb, &first); 56 + tobi_sb_add(sb, "\"children\":["); 57 + for (size_t i = 0; i < n->child_len; i++) { 58 + if (i) { 59 + tobi_sb_ch(sb, ','); 60 + } 61 + node(sb, n->child[i]); 62 + } 63 + tobi_sb_add(sb, "]}"); 64 + } 65 + 66 + char *tobi_js_emit(const tobi_parse_result *res) { 67 + tobi_sb sb; 68 + tobi_sb_init(&sb); 69 + tobi_sb_add(&sb, "{\"meta\":{"); 70 + tobi_sb_printf(&sb, "\"is_table\":%s,", res->meta.is_table ? "true" : "false"); 71 + tobi_sb_add(&sb, "\"signature\":"); 72 + tobi_json_string(&sb, res->meta.signature); 73 + tobi_sb_printf(&sb, ",\"table_length\":%zu,\"aml_offset\":%zu,\"aml_length\":%zu},", 74 + res->meta.table_len, res->meta.aml_off, res->meta.aml_len); 75 + tobi_sb_add(&sb, "\"diagnostics\":["); 76 + for (size_t i = 0; i < res->diag.len; i++) { 77 + if (i) { 78 + tobi_sb_ch(&sb, ','); 79 + } 80 + tobi_sb_printf(&sb, "{\"level\":"); 81 + tobi_json_string(&sb, tobi_diag_level_name(res->diag.items[i].level)); 82 + tobi_sb_printf(&sb, ",\"offset\":%zu,\"message\":", res->diag.items[i].off); 83 + tobi_json_string(&sb, res->diag.items[i].msg); 84 + tobi_sb_ch(&sb, '}'); 85 + } 86 + tobi_sb_add(&sb, "],\"ir\":"); 87 + if (res->root) { 88 + node(&sb, res->root); 89 + } else { 90 + tobi_sb_add(&sb, "null"); 91 + } 92 + tobi_sb_add(&sb, "}\n"); 93 + return tobi_sb_take(&sb); 94 + }
+11
src/js.h
··· 1 + #ifndef TOBI_JS_H 2 + #define TOBI_JS_H 3 + 4 + #include "dg.h" 5 + #include "ir.h" 6 + #include "p.h" 7 + 8 + /** Emit valid JSON for parse metadata, diagnostics, and IR. */ 9 + char *tobi_js_emit(const tobi_parse_result *res); 10 + 11 + #endif
+216
src/main.c
··· 1 + #include "cf.h" 2 + #include "da.h" 3 + #include "dc.h" 4 + #include "js.h" 5 + #include "mem.h" 6 + #include "p.h" 7 + 8 + #include <errno.h> 9 + #include <stdio.h> 10 + #include <stdlib.h> 11 + #include <string.h> 12 + 13 + #define TOBI_VERSION "0.1.0" 14 + 15 + static void usage(FILE *f) { 16 + fputs("usage: tobi [opts] <file> [file...]\n" 17 + "\n" 18 + "options:\n" 19 + " --dis print opcode-level disassembly\n" 20 + " --json print parsed ir as json\n" 21 + " --dot print recovered control flow as Graphviz DOT\n" 22 + " --raw print input/table metadata and aml offsets\n" 23 + " --strict treat malformed or unsupported constructs as hard errors\n" 24 + " --plain disable coloured diagnostics\n" 25 + " --help print usage\n" 26 + " --version print version\n", f); 27 + } 28 + 29 + static int read_file(const char *path, uint8_t **data, size_t *len) { 30 + FILE *fp = fopen(path, "rb"); 31 + if (!fp) { 32 + fprintf(stderr, "tobi: %s: %s\n", path, strerror(errno)); 33 + return 0; 34 + } 35 + if (fseek(fp, 0, SEEK_END) != 0) { 36 + fclose(fp); 37 + return 0; 38 + } 39 + long n = ftell(fp); 40 + if (n < 0) { 41 + fclose(fp); 42 + return 0; 43 + } 44 + rewind(fp); 45 + *data = tobi_xmalloc((size_t)n ? (size_t)n : 1); 46 + *len = (size_t)n; 47 + if (*len && fread(*data, 1, *len, fp) != *len) { 48 + fclose(fp); 49 + free(*data); 50 + *data = NULL; 51 + *len = 0; 52 + return 0; 53 + } 54 + fclose(fp); 55 + return 1; 56 + } 57 + 58 + static void print_diags(const tobi_diag_list *dl, int plain) { 59 + (void)plain; 60 + for (size_t i = 0; i < dl->len; i++) { 61 + fprintf(stderr, "0x%zx: %s: %s\n", dl->items[i].off, 62 + tobi_diag_level_name(dl->items[i].level), dl->items[i].msg); 63 + } 64 + } 65 + 66 + int main(int argc, char **argv) { 67 + int mode_dis = 0; 68 + int mode_json = 0; 69 + int mode_dot = 0; 70 + int mode_raw = 0; 71 + int strict = 0; 72 + int plain = 0; 73 + const char **files = NULL; 74 + size_t file_len = 0; 75 + size_t file_cap = 0; 76 + for (int i = 1; i < argc; i++) { 77 + if (strcmp(argv[i], "--help") == 0) { 78 + usage(stdout); 79 + return 0; 80 + } 81 + if (strcmp(argv[i], "--version") == 0) { 82 + puts("tobi " TOBI_VERSION); 83 + return 0; 84 + } 85 + if (strcmp(argv[i], "--dis") == 0) { 86 + mode_dis = 1; 87 + } else if (strcmp(argv[i], "--json") == 0) { 88 + mode_json = 1; 89 + } else if (strcmp(argv[i], "--dot") == 0) { 90 + mode_dot = 1; 91 + } else if (strcmp(argv[i], "--raw") == 0) { 92 + mode_raw = 1; 93 + } else if (strcmp(argv[i], "--strict") == 0) { 94 + strict = 1; 95 + } else if (strcmp(argv[i], "--plain") == 0) { 96 + plain = 1; 97 + } else if (argv[i][0] == '-') { 98 + fprintf(stderr, "tobi: unknown option %s\n", argv[i]); 99 + usage(stderr); 100 + return 2; 101 + } else { 102 + if (file_len == file_cap) { 103 + file_cap = file_cap ? file_cap * 2u : 4u; 104 + files = tobi_xrealloc(files, file_cap * sizeof(files[0])); 105 + } 106 + files[file_len++] = argv[i]; 107 + } 108 + } 109 + if (file_len == 0) { 110 + usage(stderr); 111 + free(files); 112 + return 2; 113 + } 114 + if ((mode_dis + mode_json + mode_dot + mode_raw) > 1) { 115 + fprintf(stderr, "tobi: choose only one output mode\n"); 116 + free(files); 117 + return 2; 118 + } 119 + uint8_t **data = tobi_xcalloc(file_len, sizeof(data[0])); 120 + size_t *lens = tobi_xcalloc(file_len, sizeof(lens[0])); 121 + for (size_t i = 0; i < file_len; i++) { 122 + if (!read_file(files[i], &data[i], &lens[i])) { 123 + for (size_t j = 0; j < file_len; j++) { 124 + free(data[j]); 125 + } 126 + free(lens); 127 + free(data); 128 + free(files); 129 + return 1; 130 + } 131 + } 132 + if (mode_dis || mode_raw) { 133 + int fail_any = 0; 134 + for (size_t i = 0; i < file_len; i++) { 135 + tobi_parse_result one; 136 + int ok = tobi_parse(data[i], lens[i], strict, &one); 137 + if (mode_raw) { 138 + printf("input=%s\nformat=%s\nis_table=%s\ntable_length=%zu\naml_offset=%zu\naml_length=%zu\n", 139 + files[i], one.meta.signature, one.meta.is_table ? "true" : "false", 140 + one.meta.table_len, one.meta.aml_off, one.meta.aml_len); 141 + } else { 142 + if (file_len > 1) { 143 + printf("== %s ==\n", files[i]); 144 + } 145 + char *s = tobi_da_emit(data[i], lens[i], &one.meta); 146 + fputs(s, stdout); 147 + free(s); 148 + } 149 + print_diags(&one.diag, plain); 150 + if ((strict && tobi_diag_has_error(&one.diag)) || !ok) { 151 + fail_any = 1; 152 + } 153 + tobi_parse_result_free(&one); 154 + } 155 + for (size_t i = 0; i < file_len; i++) { 156 + free(data[i]); 157 + } 158 + free(lens); 159 + free(data); 160 + free(files); 161 + return fail_any ? 1 : 0; 162 + } 163 + tobi_parse_result res; 164 + int ok = 0; 165 + if (file_len == 1) { 166 + ok = tobi_parse(data[0], lens[0], strict, &res); 167 + } else { 168 + tobi_parse_input *inputs = tobi_xcalloc(file_len, sizeof(inputs[0])); 169 + for (size_t i = 0; i < file_len; i++) { 170 + inputs[i].data = data[i]; 171 + inputs[i].len = lens[i]; 172 + inputs[i].name = files[i]; 173 + } 174 + ok = tobi_parse_multi(inputs, file_len, strict, &res); 175 + free(inputs); 176 + } 177 + if (!res.root) { 178 + print_diags(&res.diag, plain); 179 + tobi_parse_result_free(&res); 180 + for (size_t i = 0; i < file_len; i++) { 181 + free(data[i]); 182 + } 183 + free(lens); 184 + free(data); 185 + free(files); 186 + return 1; 187 + } 188 + (void)tobi_cf_recover(res.root, &res.diag); 189 + if (mode_raw) { 190 + printf("input=%s\nformat=%s\nis_table=%s\ntable_length=%zu\naml_offset=%zu\naml_length=%zu\n", 191 + files[0], res.meta.signature, res.meta.is_table ? "true" : "false", 192 + res.meta.table_len, res.meta.aml_off, res.meta.aml_len); 193 + } else if (mode_json) { 194 + char *s = tobi_js_emit(&res); 195 + fputs(s, stdout); 196 + free(s); 197 + } else if (mode_dot) { 198 + char *s = tobi_cf_dot(res.root); 199 + fputs(s, stdout); 200 + free(s); 201 + } else { 202 + char *s = tobi_dc_emit(res.root, &res.diag); 203 + fputs(s, stdout); 204 + free(s); 205 + } 206 + print_diags(&res.diag, plain); 207 + int fail = (strict && tobi_diag_has_error(&res.diag)) || !ok; 208 + tobi_parse_result_free(&res); 209 + for (size_t i = 0; i < file_len; i++) { 210 + free(data[i]); 211 + } 212 + free(lens); 213 + free(data); 214 + free(files); 215 + return fail ? 1 : 0; 216 + }
+58
src/mem.c
··· 1 + #include "mem.h" 2 + 3 + #include <stdint.h> 4 + #include <stdio.h> 5 + #include <stdlib.h> 6 + #include <string.h> 7 + 8 + #ifdef TOBI_SAN_BUILD 9 + const char *__asan_default_options(void) { 10 + return "detect_leaks=0:abort_on_error=1"; 11 + } 12 + #endif 13 + 14 + static void oom(void) { 15 + fputs("tobi: out of memory\n", stderr); 16 + abort(); 17 + } 18 + 19 + void *tobi_xmalloc(size_t n) { 20 + void *p = malloc(n == 0 ? 1 : n); 21 + if (!p) { 22 + oom(); 23 + } 24 + return p; 25 + } 26 + 27 + void *tobi_xcalloc(size_t count, size_t size) { 28 + if (size != 0 && count > SIZE_MAX / size) { 29 + oom(); 30 + } 31 + void *p = calloc(count == 0 ? 1 : count, size == 0 ? 1 : size); 32 + if (!p) { 33 + oom(); 34 + } 35 + return p; 36 + } 37 + 38 + void *tobi_xrealloc(void *ptr, size_t n) { 39 + void *p = realloc(ptr, n == 0 ? 1 : n); 40 + if (!p) { 41 + oom(); 42 + } 43 + return p; 44 + } 45 + 46 + char *tobi_xstrdup(const char *s) { 47 + size_t n = strlen(s); 48 + char *out = tobi_xmalloc(n + 1); 49 + memcpy(out, s, n + 1); 50 + return out; 51 + } 52 + 53 + char *tobi_xstrndup(const char *s, size_t n) { 54 + char *out = tobi_xmalloc(n + 1); 55 + memcpy(out, s, n); 56 + out[n] = '\0'; 57 + return out; 58 + }
+21
src/mem.h
··· 1 + #ifndef TOBI_MEM_H 2 + #define TOBI_MEM_H 3 + 4 + #include <stddef.h> 5 + 6 + /** Allocate n bytes or terminate the process on allocation failure. */ 7 + void *tobi_xmalloc(size_t n); 8 + 9 + /** Allocate zeroed storage for count elements of size bytes or terminate. */ 10 + void *tobi_xcalloc(size_t count, size_t size); 11 + 12 + /** Resize an allocation or terminate the process on allocation failure. */ 13 + void *tobi_xrealloc(void *ptr, size_t n); 14 + 15 + /** Duplicate a NUL-terminated string or terminate on allocation failure. */ 16 + char *tobi_xstrdup(const char *s); 17 + 18 + /** Duplicate exactly n bytes from a string and add a NUL terminator. */ 19 + char *tobi_xstrndup(const char *s, size_t n); 20 + 21 + #endif
+171
src/nm.c
··· 1 + #include "nm.h" 2 + 3 + #include "mem.h" 4 + #include "str.h" 5 + 6 + #include <ctype.h> 7 + #include <stdlib.h> 8 + #include <string.h> 9 + 10 + int tobi_nm_is_lead(unsigned char c) { 11 + return (c >= 'A' && c <= 'Z') || c == '_'; 12 + } 13 + 14 + int tobi_nm_is_tail(unsigned char c) { 15 + return tobi_nm_is_lead(c) || (c >= '0' && c <= '9'); 16 + } 17 + 18 + int tobi_nm_nameseg(tobi_rd *rd, char **out) { 19 + size_t start = rd->pos; 20 + uint8_t b[4]; 21 + if (!tobi_rd_bytes(rd, b, sizeof(b))) { 22 + rd->pos = start; 23 + return 0; 24 + } 25 + if (!tobi_nm_is_lead(b[0])) { 26 + rd->pos = start; 27 + rd->failed = 1; 28 + return 0; 29 + } 30 + for (size_t i = 1; i < 4; i++) { 31 + if (!tobi_nm_is_tail(b[i])) { 32 + rd->pos = start; 33 + rd->failed = 1; 34 + return 0; 35 + } 36 + } 37 + *out = tobi_xstrndup((const char *)b, 4); 38 + return 1; 39 + } 40 + 41 + static void add_seg(tobi_sb *sb, const char *seg, int *first) { 42 + if (!*first && sb->len > 0 && sb->buf[sb->len - 1] != '\\' && sb->buf[sb->len - 1] != '^') { 43 + tobi_sb_ch(sb, '.'); 44 + } 45 + tobi_sb_add(sb, seg); 46 + *first = 0; 47 + } 48 + 49 + int tobi_nm_namestring(tobi_rd *rd, char **out) { 50 + size_t start = rd->pos; 51 + tobi_sb sb; 52 + tobi_sb_init(&sb); 53 + uint8_t b = 0; 54 + int first = 1; 55 + if (!tobi_rd_peek(rd, &b)) { 56 + tobi_sb_free(&sb); 57 + return 0; 58 + } 59 + if (b == '\\') { 60 + (void)tobi_rd_u8(rd, &b); 61 + tobi_sb_ch(&sb, '\\'); 62 + } else { 63 + while (tobi_rd_peek(rd, &b) && b == '^') { 64 + (void)tobi_rd_u8(rd, &b); 65 + tobi_sb_ch(&sb, '^'); 66 + } 67 + } 68 + if (!tobi_rd_peek(rd, &b)) { 69 + tobi_sb_free(&sb); 70 + rd->pos = start; 71 + return 0; 72 + } 73 + if (b == 0x00) { 74 + (void)tobi_rd_u8(rd, &b); 75 + if (sb.len == 0) { 76 + tobi_sb_add(&sb, "<null>"); 77 + } 78 + *out = tobi_sb_take(&sb); 79 + return 1; 80 + } 81 + if (b == 0x2e) { 82 + (void)tobi_rd_u8(rd, &b); 83 + for (int i = 0; i < 2; i++) { 84 + char *seg = NULL; 85 + if (!tobi_nm_nameseg(rd, &seg)) { 86 + tobi_sb_free(&sb); 87 + rd->pos = start; 88 + return 0; 89 + } 90 + add_seg(&sb, seg, &first); 91 + free(seg); 92 + } 93 + *out = tobi_sb_take(&sb); 94 + return 1; 95 + } 96 + if (b == 0x2f) { 97 + (void)tobi_rd_u8(rd, &b); 98 + uint8_t count = 0; 99 + if (!tobi_rd_u8(rd, &count) || count == 0) { 100 + tobi_sb_free(&sb); 101 + rd->pos = start; 102 + rd->failed = 1; 103 + return 0; 104 + } 105 + for (uint8_t i = 0; i < count; i++) { 106 + char *seg = NULL; 107 + if (!tobi_nm_nameseg(rd, &seg)) { 108 + tobi_sb_free(&sb); 109 + rd->pos = start; 110 + return 0; 111 + } 112 + add_seg(&sb, seg, &first); 113 + free(seg); 114 + } 115 + *out = tobi_sb_take(&sb); 116 + return 1; 117 + } 118 + char *seg = NULL; 119 + if (!tobi_nm_nameseg(rd, &seg)) { 120 + tobi_sb_free(&sb); 121 + rd->pos = start; 122 + return 0; 123 + } 124 + add_seg(&sb, seg, &first); 125 + free(seg); 126 + *out = tobi_sb_take(&sb); 127 + return 1; 128 + } 129 + 130 + static char *pop_scope(const char *scope) { 131 + if (!scope || strcmp(scope, "\\") == 0 || scope[0] == '\0') { 132 + return tobi_xstrdup("\\"); 133 + } 134 + const char *last = strrchr(scope, '.'); 135 + if (!last) { 136 + return tobi_xstrdup("\\"); 137 + } 138 + return tobi_xstrndup(scope, (size_t)(last - scope)); 139 + } 140 + 141 + char *tobi_nm_resolve(const char *scope, const char *name) { 142 + if (!name || name[0] == '\0') { 143 + return tobi_xstrdup(scope && scope[0] ? scope : "\\"); 144 + } 145 + if (name[0] == '\\') { 146 + return tobi_xstrdup(name); 147 + } 148 + char *cur = tobi_xstrdup(scope && scope[0] ? scope : "\\"); 149 + const char *rest = name; 150 + while (*rest == '^') { 151 + char *p = pop_scope(cur); 152 + free(cur); 153 + cur = p; 154 + rest++; 155 + } 156 + if (*rest == '\0' || strcmp(rest, "<null>") == 0) { 157 + return cur; 158 + } 159 + tobi_sb sb; 160 + tobi_sb_init(&sb); 161 + if (strcmp(cur, "\\") == 0) { 162 + tobi_sb_add(&sb, "\\"); 163 + tobi_sb_add(&sb, rest); 164 + } else { 165 + tobi_sb_add(&sb, cur); 166 + tobi_sb_ch(&sb, '.'); 167 + tobi_sb_add(&sb, rest); 168 + } 169 + free(cur); 170 + return tobi_sb_take(&sb); 171 + }
+21
src/nm.h
··· 1 + #ifndef TOBI_NM_H 2 + #define TOBI_NM_H 3 + 4 + #include "rd.h" 5 + 6 + /** Return non-zero when c may start an AML NameSeg. */ 7 + int tobi_nm_is_lead(unsigned char c); 8 + 9 + /** Return non-zero when c may appear after the first NameSeg byte. */ 10 + int tobi_nm_is_tail(unsigned char c); 11 + 12 + /** Parse a four-byte AML NameSeg into a newly allocated string. */ 13 + int tobi_nm_nameseg(tobi_rd *rd, char **out); 14 + 15 + /** Parse an AML NameString, including root, parent, dual, and multi prefixes. */ 16 + int tobi_nm_namestring(tobi_rd *rd, char **out); 17 + 18 + /** Join a parsed AML path to a lexical scope path. */ 19 + char *tobi_nm_resolve(const char *scope, const char *name); 20 + 21 + #endif
+250
src/op.c
··· 1 + #include "op.h" 2 + 3 + #include <stddef.h> 4 + 5 + static const tobi_op ops[] = { 6 + {0x00, "ZeroOp", TOBI_OP_SIMPLE, 0}, 7 + {0x01, "OneOp", TOBI_OP_SIMPLE, 0}, 8 + {0x06, "AliasOp", TOBI_OP_NAMED, 0}, 9 + {0x08, "NameOp", TOBI_OP_NAMED, 0}, 10 + {0x0a, "ByteConst", TOBI_OP_SIMPLE, 0}, 11 + {0x0b, "WordConst", TOBI_OP_SIMPLE, 0}, 12 + {0x0c, "DWordConst", TOBI_OP_SIMPLE, 0}, 13 + {0x0d, "StringConst", TOBI_OP_SIMPLE, 0}, 14 + {0x0e, "QWordConst", TOBI_OP_SIMPLE, 0}, 15 + {0x10, "ScopeOp", TOBI_OP_PKG, 1}, 16 + {0x11, "BufferOp", TOBI_OP_PKG, 1}, 17 + {0x12, "PackageOp", TOBI_OP_PKG, 1}, 18 + {0x13, "VarPackageOp", TOBI_OP_PKG, 1}, 19 + {0x14, "MethodOp", TOBI_OP_PKG, 1}, 20 + {0x2e, "DualNamePrefix", TOBI_OP_NAMED, 0}, 21 + {0x2f, "MultiNamePrefix", TOBI_OP_NAMED, 0}, 22 + {0x5b, "ExtOpPrefix", TOBI_OP_EXT, 0}, 23 + {0x60, "Local0", TOBI_OP_SIMPLE, 0}, 24 + {0x61, "Local1", TOBI_OP_SIMPLE, 0}, 25 + {0x62, "Local2", TOBI_OP_SIMPLE, 0}, 26 + {0x63, "Local3", TOBI_OP_SIMPLE, 0}, 27 + {0x64, "Local4", TOBI_OP_SIMPLE, 0}, 28 + {0x65, "Local5", TOBI_OP_SIMPLE, 0}, 29 + {0x66, "Local6", TOBI_OP_SIMPLE, 0}, 30 + {0x67, "Local7", TOBI_OP_SIMPLE, 0}, 31 + {0x68, "Arg0", TOBI_OP_SIMPLE, 0}, 32 + {0x69, "Arg1", TOBI_OP_SIMPLE, 0}, 33 + {0x6a, "Arg2", TOBI_OP_SIMPLE, 0}, 34 + {0x6b, "Arg3", TOBI_OP_SIMPLE, 0}, 35 + {0x6c, "Arg4", TOBI_OP_SIMPLE, 0}, 36 + {0x6d, "Arg5", TOBI_OP_SIMPLE, 0}, 37 + {0x6e, "Arg6", TOBI_OP_SIMPLE, 0}, 38 + {0x70, "StoreOp", TOBI_OP_EXPR, 0}, 39 + {0x71, "RefOfOp", TOBI_OP_EXPR, 0}, 40 + {0x72, "AddOp", TOBI_OP_EXPR, 0}, 41 + {0x73, "ConcatOp", TOBI_OP_EXPR, 0}, 42 + {0x74, "SubtractOp", TOBI_OP_EXPR, 0}, 43 + {0x75, "IncrementOp", TOBI_OP_EXPR, 0}, 44 + {0x76, "DecrementOp", TOBI_OP_EXPR, 0}, 45 + {0x77, "MultiplyOp", TOBI_OP_EXPR, 0}, 46 + {0x78, "DivideOp", TOBI_OP_EXPR, 0}, 47 + {0x79, "ShiftLeftOp", TOBI_OP_EXPR, 0}, 48 + {0x7a, "ShiftRightOp", TOBI_OP_EXPR, 0}, 49 + {0x7b, "AndOp", TOBI_OP_EXPR, 0}, 50 + {0x7c, "NAndOp", TOBI_OP_EXPR, 0}, 51 + {0x7d, "OrOp", TOBI_OP_EXPR, 0}, 52 + {0x7e, "NOrOp", TOBI_OP_EXPR, 0}, 53 + {0x7f, "XorOp", TOBI_OP_EXPR, 0}, 54 + {0x80, "NotOp", TOBI_OP_EXPR, 0}, 55 + {0x81, "FindSetLeftBitOp", TOBI_OP_EXPR, 0}, 56 + {0x82, "FindSetRightBitOp", TOBI_OP_EXPR, 0}, 57 + {0x83, "DerefOfOp", TOBI_OP_EXPR, 0}, 58 + {0x84, "ConcatResOp", TOBI_OP_EXPR, 0}, 59 + {0x85, "ModOp", TOBI_OP_EXPR, 0}, 60 + {0x86, "NotifyOp", TOBI_OP_EXPR, 0}, 61 + {0x87, "SizeOfOp", TOBI_OP_EXPR, 0}, 62 + {0x88, "IndexOp", TOBI_OP_EXPR, 0}, 63 + {0x89, "MatchOp", TOBI_OP_EXPR, 0}, 64 + {0x8a, "CreateDWordFieldOp", TOBI_OP_EXPR, 0}, 65 + {0x8b, "CreateWordFieldOp", TOBI_OP_EXPR, 0}, 66 + {0x8c, "CreateByteFieldOp", TOBI_OP_EXPR, 0}, 67 + {0x8d, "CreateBitFieldOp", TOBI_OP_EXPR, 0}, 68 + {0x8e, "ObjectTypeOp", TOBI_OP_EXPR, 0}, 69 + {0x8f, "CreateQWordFieldOp", TOBI_OP_EXPR, 0}, 70 + {0x90, "LAndOp", TOBI_OP_EXPR, 0}, 71 + {0x91, "LOrOp", TOBI_OP_EXPR, 0}, 72 + {0x92, "LNotOp", TOBI_OP_EXPR, 0}, 73 + {0x93, "LEqualOp", TOBI_OP_EXPR, 0}, 74 + {0x94, "LGreaterOp", TOBI_OP_EXPR, 0}, 75 + {0x95, "LLessOp", TOBI_OP_EXPR, 0}, 76 + {0x96, "ToBufferOp", TOBI_OP_EXPR, 0}, 77 + {0x97, "ToDecimalStringOp", TOBI_OP_EXPR, 0}, 78 + {0x98, "ToHexStringOp", TOBI_OP_EXPR, 0}, 79 + {0x99, "ToIntegerOp", TOBI_OP_EXPR, 0}, 80 + {0x9c, "ToStringOp", TOBI_OP_EXPR, 0}, 81 + {0x9d, "CopyObjectOp", TOBI_OP_EXPR, 0}, 82 + {0x9e, "MidOp", TOBI_OP_EXPR, 0}, 83 + {0x9f, "ContinueOp", TOBI_OP_CONTROL, 0}, 84 + {0xa0, "IfOp", TOBI_OP_CONTROL, 1}, 85 + {0xa1, "ElseOp", TOBI_OP_CONTROL, 1}, 86 + {0xa2, "WhileOp", TOBI_OP_CONTROL, 1}, 87 + {0xa3, "NoopOp", TOBI_OP_CONTROL, 0}, 88 + {0xa4, "ReturnOp", TOBI_OP_CONTROL, 0}, 89 + {0xa5, "BreakOp", TOBI_OP_CONTROL, 0}, 90 + {0xcc, "BreakPointOp", TOBI_OP_CONTROL, 0}, 91 + {0xff, "OnesOp", TOBI_OP_SIMPLE, 0}, 92 + }; 93 + 94 + static const tobi_op ext_ops[] = { 95 + {0x5b01, "MutexOp", TOBI_OP_EXT, 0}, 96 + {0x5b02, "EventOp", TOBI_OP_EXT, 0}, 97 + {0x5b12, "CondRefOfOp", TOBI_OP_EXT, 0}, 98 + {0x5b13, "CreateFieldOp", TOBI_OP_EXT, 0}, 99 + {0x5b20, "LoadOp", TOBI_OP_EXT, 0}, 100 + {0x5b21, "StallOp", TOBI_OP_EXT, 0}, 101 + {0x5b22, "SleepOp", TOBI_OP_EXT, 0}, 102 + {0x5b23, "AcquireOp", TOBI_OP_EXT, 0}, 103 + {0x5b24, "SignalOp", TOBI_OP_EXT, 0}, 104 + {0x5b25, "WaitOp", TOBI_OP_EXT, 0}, 105 + {0x5b26, "ResetOp", TOBI_OP_EXT, 0}, 106 + {0x5b27, "ReleaseOp", TOBI_OP_EXT, 0}, 107 + {0x5b28, "FromBCDOp", TOBI_OP_EXT, 0}, 108 + {0x5b29, "ToBCDOp", TOBI_OP_EXT, 0}, 109 + {0x5b2a, "UnloadOp", TOBI_OP_EXT, 0}, 110 + {0x5b30, "RevisionOp", TOBI_OP_EXT, 0}, 111 + {0x5b31, "DebugOp", TOBI_OP_EXT, 0}, 112 + {0x5b32, "FatalOp", TOBI_OP_EXT, 0}, 113 + {0x5b80, "OpRegionOp", TOBI_OP_EXT, 0}, 114 + {0x5b81, "FieldOp", TOBI_OP_EXT, 1}, 115 + {0x5b82, "DeviceOp", TOBI_OP_EXT, 1}, 116 + {0x5b83, "ProcessorOp", TOBI_OP_EXT, 1}, 117 + {0x5b84, "PowerResOp", TOBI_OP_EXT, 1}, 118 + {0x5b85, "ThermalZoneOp", TOBI_OP_EXT, 1}, 119 + {0x5b86, "IndexFieldOp", TOBI_OP_EXT, 1}, 120 + {0x5b87, "BankFieldOp", TOBI_OP_EXT, 1}, 121 + {0x5b88, "DataRegionOp", TOBI_OP_EXT, 0}, 122 + }; 123 + 124 + static const tobi_op_desc descs[] = { 125 + {0x06, "NameString NameString"}, 126 + {0x08, "NameString DataRef"}, 127 + {0x0a, "ByteData"}, 128 + {0x0b, "WordData"}, 129 + {0x0c, "DWordData"}, 130 + {0x0d, "StringData"}, 131 + {0x0e, "QWordData"}, 132 + {0x10, "PkgLength NameString TermList"}, 133 + {0x11, "PkgLength BufferSize ByteList"}, 134 + {0x12, "PkgLength NumElements PackageElements"}, 135 + {0x13, "PkgLength VarNumElements PackageElements"}, 136 + {0x14, "PkgLength NameString MethodFlags TermList"}, 137 + {0x70, "TermArg SuperName"}, 138 + {0x71, "SuperName"}, 139 + {0x72, "TermArg TermArg Target"}, 140 + {0x73, "Data TermArg Target"}, 141 + {0x74, "TermArg TermArg Target"}, 142 + {0x75, "SuperName"}, 143 + {0x76, "SuperName"}, 144 + {0x77, "TermArg TermArg Target"}, 145 + {0x78, "TermArg TermArg Target RemainderTarget"}, 146 + {0x79, "TermArg TermArg Target"}, 147 + {0x7a, "TermArg TermArg Target"}, 148 + {0x7b, "TermArg TermArg Target"}, 149 + {0x7c, "TermArg TermArg Target"}, 150 + {0x7d, "TermArg TermArg Target"}, 151 + {0x7e, "TermArg TermArg Target"}, 152 + {0x7f, "TermArg TermArg Target"}, 153 + {0x80, "TermArg Target"}, 154 + {0x81, "TermArg Target"}, 155 + {0x82, "TermArg Target"}, 156 + {0x83, "TermArg"}, 157 + {0x84, "Data Buffer Target"}, 158 + {0x85, "TermArg TermArg Target"}, 159 + {0x86, "SuperName TermArg"}, 160 + {0x87, "SuperName"}, 161 + {0x88, "SuperName TermArg Target"}, 162 + {0x89, "SearchPkg MatchOp1 MatchObj1 MatchOp2 MatchObj2 StartIndex"}, 163 + {0x8a, "SourceBuff BitIndex NameString"}, 164 + {0x8b, "SourceBuff BitIndex NameString"}, 165 + {0x8c, "SourceBuff ByteIndex NameString"}, 166 + {0x8d, "SourceBuff BitIndex NameString"}, 167 + {0x8e, "SuperName"}, 168 + {0x8f, "SourceBuff BitIndex NameString"}, 169 + {0x90, "TermArg TermArg"}, 170 + {0x91, "TermArg TermArg"}, 171 + {0x92, "TermArg"}, 172 + {0x93, "TermArg TermArg"}, 173 + {0x94, "TermArg TermArg"}, 174 + {0x95, "TermArg TermArg"}, 175 + {0x96, "TermArg Target"}, 176 + {0x97, "TermArg Target"}, 177 + {0x98, "TermArg Target"}, 178 + {0x99, "TermArg Target"}, 179 + {0x9c, "TermArg LengthArg Target"}, 180 + {0x9d, "SimpleName Target"}, 181 + {0x9e, "TermArg TermArg TermArg Target"}, 182 + {0x9f, ""}, 183 + {0xa0, "PkgLength Predicate TermList"}, 184 + {0xa1, "PkgLength TermList"}, 185 + {0xa2, "PkgLength Predicate TermList"}, 186 + {0xa4, "TermArg"}, 187 + {0xa5, ""}, 188 + {0x5b01, "NameString SyncFlags"}, 189 + {0x5b02, "NameString"}, 190 + {0x5b12, "SuperName Target"}, 191 + {0x5b13, "SourceBuff BitIndex NumBits NameString"}, 192 + {0x5b20, "NameString Target"}, 193 + {0x5b21, "TermArg"}, 194 + {0x5b22, "TermArg"}, 195 + {0x5b23, "MutexObject Timeout"}, 196 + {0x5b24, "EventObject"}, 197 + {0x5b25, "EventObject Timeout"}, 198 + {0x5b26, "EventObject"}, 199 + {0x5b27, "MutexObject"}, 200 + {0x5b28, "TermArg Target"}, 201 + {0x5b29, "TermArg Target"}, 202 + {0x5b2a, "Handle"}, 203 + {0x5b30, ""}, 204 + {0x5b31, ""}, 205 + {0x5b32, "FatalType FatalCode FatalArg"}, 206 + {0x5b80, "NameString RegionSpace RegionOffset RegionLen"}, 207 + {0x5b81, "PkgLength NameString FieldFlags FieldList"}, 208 + {0x5b82, "PkgLength NameString TermList"}, 209 + {0x5b83, "PkgLength NameString ProcId PblkAddr PblkLen TermList"}, 210 + {0x5b84, "PkgLength NameString SystemLevel ResourceOrder TermList"}, 211 + {0x5b85, "PkgLength NameString TermList"}, 212 + {0x5b86, "PkgLength IndexName DataName FieldFlags FieldList"}, 213 + {0x5b87, "PkgLength RegionName BankName BankValue FieldFlags FieldList"}, 214 + {0x5b88, "NameString SignatureString OemIdString OemTableIdString"}, 215 + }; 216 + 217 + const tobi_op *tobi_op_find(uint8_t op) { 218 + for (size_t i = 0; i < sizeof(ops) / sizeof(ops[0]); i++) { 219 + if (ops[i].code == op) { 220 + return &ops[i]; 221 + } 222 + } 223 + return NULL; 224 + } 225 + 226 + const tobi_op *tobi_op_find_ext(uint8_t ext) { 227 + uint16_t code = (uint16_t)(0x5b00u | ext); 228 + return tobi_op_find_code(code); 229 + } 230 + 231 + const tobi_op *tobi_op_find_code(uint16_t code) { 232 + if ((code & 0xff00u) == 0x5b00u) { 233 + for (size_t i = 0; i < sizeof(ext_ops) / sizeof(ext_ops[0]); i++) { 234 + if (ext_ops[i].code == code) { 235 + return &ext_ops[i]; 236 + } 237 + } 238 + return NULL; 239 + } 240 + return tobi_op_find((uint8_t)code); 241 + } 242 + 243 + const char *tobi_op_operands(uint16_t code) { 244 + for (size_t i = 0; i < sizeof(descs) / sizeof(descs[0]); i++) { 245 + if (descs[i].code == code) { 246 + return descs[i].operands; 247 + } 248 + } 249 + return NULL; 250 + }
+39
src/op.h
··· 1 + #ifndef TOBI_OP_H 2 + #define TOBI_OP_H 3 + 4 + #include <stdint.h> 5 + 6 + typedef enum tobi_op_kind { 7 + TOBI_OP_SIMPLE, 8 + TOBI_OP_PKG, 9 + TOBI_OP_NAMED, 10 + TOBI_OP_EXPR, 11 + TOBI_OP_CONTROL, 12 + TOBI_OP_EXT 13 + } tobi_op_kind; 14 + 15 + typedef struct tobi_op { 16 + uint16_t code; 17 + const char *name; 18 + tobi_op_kind kind; 19 + int has_pkg_len; 20 + } tobi_op; 21 + 22 + typedef struct tobi_op_desc { 23 + uint16_t code; 24 + const char *operands; 25 + } tobi_op_desc; 26 + 27 + /** Return metadata for an AML opcode byte or NULL when unsupported. */ 28 + const tobi_op *tobi_op_find(uint8_t op); 29 + 30 + /** Return metadata for an AML extended opcode following ExtOpPrefix. */ 31 + const tobi_op *tobi_op_find_ext(uint8_t ext); 32 + 33 + /** Return metadata for a complete opcode code; extended opcodes use 0x5b00|ext. */ 34 + const tobi_op *tobi_op_find_code(uint16_t code); 35 + 36 + /** Return a compact operand descriptor string for an opcode, or NULL. */ 37 + const char *tobi_op_operands(uint16_t code); 38 + 39 + #endif
+1407
src/p.c
··· 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 + 17 + typedef struct method_ent { 18 + char *path; 19 + char *name; 20 + unsigned args; 21 + } method_ent; 22 + 23 + typedef struct parser { 24 + tobi_diag_list *diag; 25 + int strict; 26 + char *scope; 27 + method_ent *methods; 28 + size_t method_len; 29 + size_t method_cap; 30 + size_t terms; 31 + } parser; 32 + 33 + static void diag(parser *p, tobi_diag_level lvl, size_t off, const char *fmt, ...) { 34 + va_list ap; 35 + va_start(ap, fmt); 36 + va_list cp; 37 + va_copy(cp, ap); 38 + int n = vsnprintf(NULL, 0, fmt, cp); 39 + va_end(cp); 40 + if (n < 0) { 41 + n = 0; 42 + } 43 + char *msg = tobi_xmalloc((size_t)n + 1); 44 + (void)vsnprintf(msg, (size_t)n + 1, fmt, ap); 45 + va_end(ap); 46 + tobi_diag_add(p->diag, lvl, off, "%s", msg); 47 + free(msg); 48 + } 49 + 50 + static uint32_t le32_at(const uint8_t *d) { 51 + return (uint32_t)d[0] | ((uint32_t)d[1] << 8) | ((uint32_t)d[2] << 16) | ((uint32_t)d[3] << 24); 52 + } 53 + 54 + static int is_table_sig(const uint8_t *d) { 55 + return memcmp(d, "DSDT", 4) == 0 || memcmp(d, "SSDT", 4) == 0; 56 + } 57 + 58 + static void method_add(parser *p, const char *path, const char *name, unsigned args) { 59 + for (size_t i = 0; i < p->method_len; i++) { 60 + if (strcmp(p->methods[i].path, path) == 0) { 61 + p->methods[i].args = args; 62 + return; 63 + } 64 + } 65 + if (p->method_len == p->method_cap) { 66 + p->method_cap = p->method_cap ? p->method_cap * 2 : 8; 67 + p->methods = tobi_xrealloc(p->methods, p->method_cap * sizeof(p->methods[0])); 68 + } 69 + p->methods[p->method_len].path = tobi_xstrdup(path); 70 + p->methods[p->method_len].name = tobi_xstrdup(name); 71 + p->methods[p->method_len].args = args; 72 + p->method_len++; 73 + } 74 + 75 + static int method_find(const parser *p, const char *name) { 76 + for (size_t i = 0; i < p->method_len; i++) { 77 + if (strcmp(p->methods[i].path, name) == 0 || strcmp(p->methods[i].name, name) == 0) { 78 + return (int)p->methods[i].args; 79 + } 80 + } 81 + const char *last = strrchr(name, '.'); 82 + if (last) { 83 + last++; 84 + for (size_t i = 0; i < p->method_len; i++) { 85 + if (strcmp(p->methods[i].name, last) == 0) { 86 + return (int)p->methods[i].args; 87 + } 88 + } 89 + } 90 + return -1; 91 + } 92 + 93 + static void parser_free(parser *p) { 94 + free(p->scope); 95 + for (size_t i = 0; i < p->method_len; i++) { 96 + free(p->methods[i].path); 97 + free(p->methods[i].name); 98 + } 99 + free(p->methods); 100 + } 101 + 102 + static uint8_t table_sum8(const uint8_t *data, size_t len) { 103 + uint8_t sum = 0; 104 + for (size_t i = 0; i < len; i++) { 105 + sum = (uint8_t)(sum + data[i]); 106 + } 107 + return sum; 108 + } 109 + 110 + static int detect_input(const uint8_t *data, size_t len, int strict, tobi_diag_list *diag_list, tobi_input_meta *meta) { 111 + memset(meta, 0, sizeof(*meta)); 112 + meta->aml_off = 0; 113 + meta->aml_len = len; 114 + memcpy(meta->signature, "RAW", 4); 115 + meta->signature[4] = '\0'; 116 + if (len >= 4 && is_table_sig(data)) { 117 + memcpy(meta->signature, data, 4); 118 + meta->signature[4] = '\0'; 119 + meta->is_table = 1; 120 + if (len < 36) { 121 + tobi_diag_add(diag_list, TOBI_DIAG_ERROR, 0, "ACPI table shorter than 36-byte header"); 122 + return !strict; 123 + } 124 + uint32_t table_len = le32_at(data + 4); 125 + meta->table_len = table_len; 126 + if (table_len < 36 || table_len > len) { 127 + tobi_diag_add(diag_list, TOBI_DIAG_ERROR, 4, "invalid ACPI table length %u for file size %zu", table_len, len); 128 + return !strict; 129 + } 130 + uint8_t sum = table_sum8(data, table_len); 131 + if (sum != 0) { 132 + tobi_diag_add(diag_list, strict ? TOBI_DIAG_ERROR : TOBI_DIAG_WARN, 9, 133 + "ACPI table checksum mismatch: byte sum is 0x%02x", sum); 134 + if (strict) { 135 + return 0; 136 + } 137 + } 138 + meta->aml_off = 36; 139 + meta->aml_len = (size_t)table_len - 36; 140 + } 141 + return 1; 142 + } 143 + 144 + static int pkg_window(parser *p, tobi_rd *rd, size_t op_start, size_t *body_start, size_t *body_len, size_t *whole_len) { 145 + size_t len = 0; 146 + size_t enc = 0; 147 + size_t pkg_start = rd->pos; 148 + if (!tobi_rd_pkg_len(rd, &len, &enc)) { 149 + diag(p, TOBI_DIAG_ERROR, rd->base + pkg_start, "truncated AML package length"); 150 + return 0; 151 + } 152 + if (len < enc) { 153 + diag(p, TOBI_DIAG_ERROR, rd->base + pkg_start, "invalid AML package length %zu smaller than encoded length %zu", len, enc); 154 + return 0; 155 + } 156 + size_t end = pkg_start + len; 157 + if (end > rd->len) { 158 + diag(p, TOBI_DIAG_ERROR, rd->base + pkg_start, "AML package body extends past input"); 159 + end = rd->len; 160 + } 161 + *body_start = rd->pos; 162 + *body_len = end >= rd->pos ? end - rd->pos : 0; 163 + *whole_len = (rd->base + end) - op_start; 164 + return 1; 165 + } 166 + 167 + static tobi_ir *unknown_node(parser *p, size_t off, uint32_t raw, size_t len, const char *why) { 168 + tobi_ir *n = tobi_ir_new(TOBI_IR_UNKNOWN, off, len); 169 + n->raw_op = raw; 170 + tobi_ir_set_str(n, why); 171 + diag(p, p->strict ? TOBI_DIAG_ERROR : TOBI_DIAG_WARN, off, "unsupported or malformed opcode 0x%x: %s", raw, why); 172 + return n; 173 + } 174 + 175 + static int parse_term_list(parser *p, tobi_rd *rd, tobi_ir *parent, unsigned depth); 176 + static tobi_ir *parse_expr(parser *p, tobi_rd *rd, unsigned depth); 177 + static tobi_ir *parse_term(parser *p, tobi_rd *rd, unsigned depth); 178 + 179 + static int parse_namestring_diag(parser *p, tobi_rd *rd, char **name) { 180 + size_t off = tobi_rd_off(rd); 181 + if (!tobi_nm_namestring(rd, name)) { 182 + diag(p, TOBI_DIAG_ERROR, off, "invalid or truncated AML namestring"); 183 + return 0; 184 + } 185 + return 1; 186 + } 187 + 188 + static tobi_ir *parse_block_from_reader(parser *p, tobi_rd *sub, unsigned depth, size_t off) { 189 + tobi_ir *block = tobi_ir_new(TOBI_IR_BLOCK, off, tobi_rd_left(sub)); 190 + (void)parse_term_list(p, sub, block, depth + 1); 191 + return block; 192 + } 193 + 194 + static tobi_ir *parse_pkg_named(parser *p, tobi_rd *rd, unsigned depth, tobi_ir_kind kind, const char *kind_name, size_t op_start) { 195 + size_t body_start = 0; 196 + size_t body_len = 0; 197 + size_t whole_len = 0; 198 + if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) { 199 + return unknown_node(p, op_start, rd->data[op_start - rd->base], 1, "bad package length"); 200 + } 201 + tobi_rd body; 202 + tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start); 203 + char *name = NULL; 204 + if (!parse_namestring_diag(p, &body, &name)) { 205 + rd->pos = body_start + body_len; 206 + return unknown_node(p, op_start, rd->data[op_start - rd->base], whole_len, kind_name); 207 + } 208 + char *path = tobi_nm_resolve(p->scope, name); 209 + tobi_ir *n = tobi_ir_new(kind, op_start, whole_len); 210 + tobi_ir_set_name(n, name); 211 + tobi_ir_set_path(n, path); 212 + char *old_scope = p->scope; 213 + p->scope = tobi_xstrdup(path); 214 + tobi_ir *block = parse_block_from_reader(p, &body, depth, tobi_rd_off(&body)); 215 + free(p->scope); 216 + p->scope = old_scope; 217 + tobi_ir_add(n, block); 218 + rd->pos = body_start + body_len; 219 + free(name); 220 + free(path); 221 + return n; 222 + } 223 + 224 + static tobi_ir *parse_method(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) { 225 + size_t body_start = 0; 226 + size_t body_len = 0; 227 + size_t whole_len = 0; 228 + if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) { 229 + return unknown_node(p, op_start, 0x14, 1, "bad method package"); 230 + } 231 + tobi_rd body; 232 + tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start); 233 + char *name = NULL; 234 + if (!parse_namestring_diag(p, &body, &name)) { 235 + rd->pos = body_start + body_len; 236 + return unknown_node(p, op_start, 0x14, whole_len, "bad method name"); 237 + } 238 + uint8_t flags = 0; 239 + if (!tobi_rd_u8(&body, &flags)) { 240 + diag(p, TOBI_DIAG_ERROR, tobi_rd_off(&body), "truncated method flags"); 241 + rd->pos = body_start + body_len; 242 + free(name); 243 + return unknown_node(p, op_start, 0x14, whole_len, "bad method flags"); 244 + } 245 + char *path = tobi_nm_resolve(p->scope, name); 246 + tobi_ir *n = tobi_ir_new(TOBI_IR_METHOD, op_start, whole_len); 247 + tobi_ir_set_name(n, name); 248 + tobi_ir_set_path(n, path); 249 + n->method_args = flags & 0x07u; 250 + n->method_serialized = (flags >> 3) & 0x01u; 251 + n->method_sync = (flags >> 4) & 0x0fu; 252 + method_add(p, path, name, n->method_args); 253 + char *old_scope = p->scope; 254 + p->scope = tobi_xstrdup(path); 255 + tobi_ir *block = parse_block_from_reader(p, &body, depth, tobi_rd_off(&body)); 256 + free(p->scope); 257 + p->scope = old_scope; 258 + tobi_ir_add(n, block); 259 + rd->pos = body_start + body_len; 260 + free(name); 261 + free(path); 262 + return n; 263 + } 264 + 265 + static tobi_ir *parse_name(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) { 266 + char *name = NULL; 267 + if (!parse_namestring_diag(p, rd, &name)) { 268 + return unknown_node(p, op_start, 0x08, 1, "bad NameOp name"); 269 + } 270 + char *path = tobi_nm_resolve(p->scope, name); 271 + tobi_ir *n = tobi_ir_new(TOBI_IR_NAME, op_start, 0); 272 + tobi_ir_set_name(n, name); 273 + tobi_ir_set_path(n, path); 274 + tobi_ir *value = parse_expr(p, rd, depth + 1); 275 + if (value) { 276 + tobi_ir_add(n, value); 277 + } 278 + n->len = tobi_rd_off(rd) - op_start; 279 + free(name); 280 + free(path); 281 + return n; 282 + } 283 + 284 + static tobi_ir *parse_alias(parser *p, tobi_rd *rd, size_t op_start) { 285 + char *src = NULL; 286 + char *dst = NULL; 287 + if (!parse_namestring_diag(p, rd, &src) || !parse_namestring_diag(p, rd, &dst)) { 288 + free(src); 289 + free(dst); 290 + return unknown_node(p, op_start, 0x06, 1, "bad AliasOp names"); 291 + } 292 + tobi_ir *n = tobi_ir_new(TOBI_IR_ALIAS, op_start, tobi_rd_off(rd) - op_start); 293 + tobi_ir_set_name(n, dst); 294 + tobi_ir_set_str(n, src); 295 + char *path = tobi_nm_resolve(p->scope, dst); 296 + tobi_ir_set_path(n, path); 297 + free(path); 298 + free(src); 299 + free(dst); 300 + return n; 301 + } 302 + 303 + static void parse_field_list(parser *p, tobi_rd *body, tobi_ir *n) { 304 + while (tobi_rd_left(body) > 0) { 305 + size_t foff = tobi_rd_off(body); 306 + uint8_t b = 0; 307 + if (!tobi_rd_peek(body, &b)) { 308 + break; 309 + } 310 + if (b == 0x00) { 311 + (void)tobi_rd_u8(body, &b); 312 + size_t bits = 0; 313 + size_t enc = 0; 314 + if (!tobi_rd_pkg_len(body, &bits, &enc)) { 315 + diag(p, TOBI_DIAG_ERROR, foff, "truncated reserved field length"); 316 + break; 317 + } 318 + tobi_ir *e = tobi_ir_new(TOBI_IR_FIELD_ELEM, foff, tobi_rd_off(body) - foff); 319 + tobi_ir_set_name(e, "<reserved>"); 320 + e->value = bits; 321 + tobi_ir_add(n, e); 322 + continue; 323 + } 324 + if (b == 0x01) { 325 + if (!tobi_rd_skip(body, 3)) { 326 + diag(p, TOBI_DIAG_ERROR, foff, "truncated AccessField"); 327 + break; 328 + } 329 + tobi_ir *e = tobi_ir_new(TOBI_IR_FIELD_ELEM, foff, tobi_rd_off(body) - foff); 330 + tobi_ir_set_name(e, "<access>"); 331 + tobi_ir_add(n, e); 332 + continue; 333 + } 334 + if (b == 0x02) { 335 + (void)tobi_rd_u8(body, &b); 336 + char *conn = NULL; 337 + if (!tobi_nm_namestring(body, &conn)) { 338 + diag(p, TOBI_DIAG_ERROR, foff, "bad ConnectField name"); 339 + break; 340 + } 341 + tobi_ir *e = tobi_ir_new(TOBI_IR_FIELD_ELEM, foff, tobi_rd_off(body) - foff); 342 + tobi_ir_set_name(e, "<connect>"); 343 + tobi_ir_set_str(e, conn); 344 + tobi_ir_add(n, e); 345 + free(conn); 346 + continue; 347 + } 348 + if (b == 0x03) { 349 + if (!tobi_rd_skip(body, 4)) { 350 + diag(p, TOBI_DIAG_ERROR, foff, "truncated ExtendedAccessField"); 351 + break; 352 + } 353 + tobi_ir *e = tobi_ir_new(TOBI_IR_FIELD_ELEM, foff, tobi_rd_off(body) - foff); 354 + tobi_ir_set_name(e, "<extended_access>"); 355 + tobi_ir_add(n, e); 356 + continue; 357 + } 358 + char *fname = NULL; 359 + if (!tobi_nm_nameseg(body, &fname)) { 360 + diag(p, TOBI_DIAG_ERROR, foff, "bad field element"); 361 + (void)tobi_rd_skip(body, 1); 362 + continue; 363 + } 364 + size_t bits = 0; 365 + size_t enc = 0; 366 + if (!tobi_rd_pkg_len(body, &bits, &enc)) { 367 + diag(p, TOBI_DIAG_ERROR, foff, "truncated field element length"); 368 + free(fname); 369 + break; 370 + } 371 + tobi_ir *e = tobi_ir_new(TOBI_IR_FIELD_ELEM, foff, tobi_rd_off(body) - foff); 372 + tobi_ir_set_name(e, fname); 373 + e->value = bits; 374 + tobi_ir_add(n, e); 375 + free(fname); 376 + } 377 + } 378 + 379 + static tobi_ir *parse_field_like(parser *p, tobi_rd *rd, unsigned depth, size_t op_start, uint32_t raw, const char *label) { 380 + size_t body_start = 0; 381 + size_t body_len = 0; 382 + size_t whole_len = 0; 383 + if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) { 384 + return unknown_node(p, op_start, raw, 2, "bad field package"); 385 + } 386 + tobi_rd body; 387 + tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start); 388 + char *first = NULL; 389 + if (!parse_namestring_diag(p, &body, &first)) { 390 + rd->pos = body_start + body_len; 391 + return unknown_node(p, op_start, raw, whole_len, "bad field name"); 392 + } 393 + tobi_ir *n = tobi_ir_new(TOBI_IR_FIELD, op_start, whole_len); 394 + tobi_ir_set_str(n, label); 395 + if (raw == 0x5b81u) { 396 + tobi_ir_set_name(n, first); 397 + } else if (raw == 0x5b86u) { 398 + char *data = NULL; 399 + if (!parse_namestring_diag(p, &body, &data)) { 400 + free(first); 401 + rd->pos = body_start + body_len; 402 + return unknown_node(p, op_start, raw, whole_len, "bad IndexField data name"); 403 + } 404 + tobi_sb nm; 405 + tobi_sb_init(&nm); 406 + tobi_sb_add(&nm, first); 407 + tobi_sb_add(&nm, ","); 408 + tobi_sb_add(&nm, data); 409 + tobi_ir_set_name(n, nm.buf); 410 + tobi_sb_free(&nm); 411 + free(data); 412 + } else { 413 + char *bank = NULL; 414 + if (!parse_namestring_diag(p, &body, &bank)) { 415 + free(first); 416 + rd->pos = body_start + body_len; 417 + return unknown_node(p, op_start, raw, whole_len, "bad BankField bank name"); 418 + } 419 + tobi_sb nm; 420 + tobi_sb_init(&nm); 421 + tobi_sb_add(&nm, first); 422 + tobi_sb_add(&nm, ","); 423 + tobi_sb_add(&nm, bank); 424 + tobi_ir_set_name(n, nm.buf); 425 + tobi_sb_free(&nm); 426 + free(bank); 427 + tobi_ir *bank_value = parse_expr(p, &body, depth + 1); 428 + if (bank_value) { 429 + tobi_ir_add(n, bank_value); 430 + } 431 + } 432 + uint8_t flags = 0; 433 + if (!tobi_rd_u8(&body, &flags)) { 434 + diag(p, TOBI_DIAG_ERROR, tobi_rd_off(&body), "truncated field flags"); 435 + } 436 + n->value = flags; 437 + parse_field_list(p, &body, n); 438 + rd->pos = body_start + body_len; 439 + free(first); 440 + return n; 441 + } 442 + 443 + static tobi_ir *ref_from_name(parser *p, tobi_rd *rd, size_t owner_start, const char *why) { 444 + size_t off = tobi_rd_off(rd); 445 + char *name = NULL; 446 + if (!parse_namestring_diag(p, rd, &name)) { 447 + return unknown_node(p, owner_start, 0, tobi_rd_off(rd) - owner_start, why); 448 + } 449 + tobi_ir *n = tobi_ir_new(TOBI_IR_REF, off, tobi_rd_off(rd) - off); 450 + tobi_ir_set_name(n, name); 451 + char *path = tobi_nm_resolve(p->scope, name); 452 + tobi_ir_set_path(n, path); 453 + free(path); 454 + free(name); 455 + return n; 456 + } 457 + 458 + static void add_expr_children(parser *p, tobi_rd *rd, unsigned depth, tobi_ir *n, unsigned count) { 459 + for (unsigned i = 0; i < count && tobi_rd_left(rd) > 0; i++) { 460 + tobi_ir *arg = parse_expr(p, rd, depth + 1); 461 + if (arg) { 462 + tobi_ir_add(n, arg); 463 + } 464 + } 465 + } 466 + 467 + static tobi_ir *parse_named_ext_expr(parser *p, tobi_rd *rd, unsigned depth, size_t op_start, 468 + uint32_t raw, const char *name, unsigned expr_count, 469 + unsigned name_count) { 470 + tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, op_start, 0); 471 + n->raw_op = raw; 472 + tobi_ir_set_name(n, name); 473 + add_expr_children(p, rd, depth, n, expr_count); 474 + for (unsigned i = 0; i < name_count && tobi_rd_left(rd) > 0; i++) { 475 + tobi_ir *ref = ref_from_name(p, rd, op_start, "bad extended opcode namestring"); 476 + if (ref) { 477 + tobi_ir_add(n, ref); 478 + } 479 + } 480 + n->len = tobi_rd_off(rd) - op_start; 481 + return n; 482 + } 483 + 484 + static tobi_ir *parse_ext_pkg_scope(parser *p, tobi_rd *rd, unsigned depth, size_t op_start, 485 + uint32_t raw, tobi_ir_kind kind, const char *kind_name, 486 + unsigned prefix_bytes) { 487 + size_t body_start = 0; 488 + size_t body_len = 0; 489 + size_t whole_len = 0; 490 + if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) { 491 + return unknown_node(p, op_start, raw, 2, kind_name); 492 + } 493 + tobi_rd body; 494 + tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start); 495 + char *name = NULL; 496 + if (!parse_namestring_diag(p, &body, &name)) { 497 + rd->pos = body_start + body_len; 498 + return unknown_node(p, op_start, raw, whole_len, kind_name); 499 + } 500 + (void)tobi_rd_skip(&body, prefix_bytes); 501 + char *path = tobi_nm_resolve(p->scope, name); 502 + tobi_ir *n = tobi_ir_new(kind, op_start, whole_len); 503 + tobi_ir_set_name(n, name); 504 + tobi_ir_set_path(n, path); 505 + tobi_ir_set_str(n, kind_name); 506 + char *old_scope = p->scope; 507 + p->scope = tobi_xstrdup(path); 508 + tobi_ir *block = parse_block_from_reader(p, &body, depth, tobi_rd_off(&body)); 509 + free(p->scope); 510 + p->scope = old_scope; 511 + tobi_ir_add(n, block); 512 + rd->pos = body_start + body_len; 513 + free(path); 514 + free(name); 515 + return n; 516 + } 517 + 518 + static tobi_ir *parse_ext(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) { 519 + uint8_t ext = 0; 520 + if (!tobi_rd_u8(rd, &ext)) { 521 + return unknown_node(p, op_start, 0x5b, 1, "truncated extended opcode"); 522 + } 523 + uint32_t raw = 0x5b00u | ext; 524 + switch (ext) { 525 + case 0x82: 526 + return parse_pkg_named(p, rd, depth, TOBI_IR_DEVICE, "DeviceOp", op_start); 527 + case 0x83: { 528 + size_t body_start = 0; 529 + size_t body_len = 0; 530 + size_t whole_len = 0; 531 + if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) { 532 + return unknown_node(p, op_start, raw, 2, "bad ProcessorOp package"); 533 + } 534 + tobi_rd body; 535 + tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start); 536 + char *name = NULL; 537 + if (!parse_namestring_diag(p, &body, &name)) { 538 + rd->pos = body_start + body_len; 539 + return unknown_node(p, op_start, raw, whole_len, "bad processor name"); 540 + } 541 + uint8_t proc_id = 0; 542 + uint32_t pblk = 0; 543 + uint8_t pblk_len = 0; 544 + (void)tobi_rd_u8(&body, &proc_id); 545 + (void)tobi_rd_u32(&body, &pblk); 546 + (void)tobi_rd_u8(&body, &pblk_len); 547 + char *path = tobi_nm_resolve(p->scope, name); 548 + tobi_ir *n = tobi_ir_new(TOBI_IR_PROCESSOR, op_start, whole_len); 549 + tobi_ir_set_name(n, name); 550 + tobi_ir_set_path(n, path); 551 + n->value = proc_id; 552 + char *old_scope = p->scope; 553 + p->scope = tobi_xstrdup(path); 554 + tobi_ir_add(n, parse_block_from_reader(p, &body, depth, tobi_rd_off(&body))); 555 + free(p->scope); 556 + p->scope = old_scope; 557 + rd->pos = body_start + body_len; 558 + free(path); 559 + free(name); 560 + return n; 561 + } 562 + case 0x80: { 563 + char *name = NULL; 564 + if (!parse_namestring_diag(p, rd, &name)) { 565 + return unknown_node(p, op_start, raw, 2, "bad OpRegion name"); 566 + } 567 + uint8_t space = 0; 568 + if (!tobi_rd_u8(rd, &space)) { 569 + free(name); 570 + diag(p, TOBI_DIAG_ERROR, tobi_rd_off(rd), "truncated OpRegion space"); 571 + return unknown_node(p, op_start, raw, 2, "bad OpRegion"); 572 + } 573 + tobi_ir *n = tobi_ir_new(TOBI_IR_OPREGION, op_start, 0); 574 + tobi_ir_set_name(n, name); 575 + char *path = tobi_nm_resolve(p->scope, name); 576 + tobi_ir_set_path(n, path); 577 + n->value = space; 578 + tobi_ir *off = parse_expr(p, rd, depth + 1); 579 + tobi_ir *len = parse_expr(p, rd, depth + 1); 580 + if (off) { 581 + tobi_ir_add(n, off); 582 + } 583 + if (len) { 584 + tobi_ir_add(n, len); 585 + } 586 + n->len = tobi_rd_off(rd) - op_start; 587 + free(path); 588 + free(name); 589 + return n; 590 + } 591 + case 0x81: 592 + return parse_field_like(p, rd, depth, op_start, raw, "Field"); 593 + case 0x86: 594 + return parse_field_like(p, rd, depth, op_start, raw, "IndexField"); 595 + case 0x87: 596 + return parse_field_like(p, rd, depth, op_start, raw, "BankField"); 597 + case 0x84: 598 + return parse_ext_pkg_scope(p, rd, depth, op_start, raw, TOBI_IR_SCOPE, "PowerResOp", 2); 599 + case 0x85: 600 + return parse_ext_pkg_scope(p, rd, depth, op_start, raw, TOBI_IR_SCOPE, "ThermalZoneOp", 0); 601 + case 0x12: 602 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "cond_ref_of", 1, 1); 603 + case 0x13: 604 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "create_field", 3, 1); 605 + case 0x20: 606 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "load", 1, 0); 607 + case 0x21: 608 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "stall", 1, 0); 609 + case 0x22: 610 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "sleep", 1, 0); 611 + case 0x23: 612 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "acquire", 2, 0); 613 + case 0x24: 614 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "signal", 1, 0); 615 + case 0x25: 616 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "wait", 2, 0); 617 + case 0x26: 618 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "reset", 1, 0); 619 + case 0x27: 620 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "release", 1, 0); 621 + case 0x28: 622 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "from_bcd", 2, 0); 623 + case 0x29: 624 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "to_bcd", 2, 0); 625 + case 0x2a: 626 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "unload", 1, 0); 627 + case 0x30: { 628 + tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, op_start, tobi_rd_off(rd) - op_start); 629 + n->value = 2; 630 + return n; 631 + } 632 + case 0x31: { 633 + tobi_ir *n = tobi_ir_new(TOBI_IR_REF, op_start, tobi_rd_off(rd) - op_start); 634 + n->raw_op = raw; 635 + tobi_ir_set_name(n, "Debug"); 636 + return n; 637 + } 638 + case 0x32: 639 + return parse_named_ext_expr(p, rd, depth, op_start, raw, "fatal", 3, 0); 640 + case 0x88: { 641 + char *name = NULL; 642 + if (!parse_namestring_diag(p, rd, &name)) { 643 + return unknown_node(p, op_start, raw, 2, "bad DataRegion name"); 644 + } 645 + tobi_ir *n = tobi_ir_new(TOBI_IR_OPREGION, op_start, 0); 646 + n->raw_op = raw; 647 + tobi_ir_set_name(n, name); 648 + char *path = tobi_nm_resolve(p->scope, name); 649 + tobi_ir_set_path(n, path); 650 + tobi_ir_set_str(n, "DataRegion"); 651 + add_expr_children(p, rd, depth, n, 3); 652 + n->len = tobi_rd_off(rd) - op_start; 653 + free(path); 654 + free(name); 655 + return n; 656 + } 657 + case 0x01: 658 + case 0x02: { 659 + char *name = NULL; 660 + if (!parse_namestring_diag(p, rd, &name)) { 661 + return unknown_node(p, op_start, raw, 2, ext == 0x01 ? "bad MutexOp" : "bad EventOp"); 662 + } 663 + tobi_ir *n = tobi_ir_new(ext == 0x01 ? TOBI_IR_MUTEX : TOBI_IR_EVENT, op_start, 0); 664 + tobi_ir_set_name(n, name); 665 + char *path = tobi_nm_resolve(p->scope, name); 666 + tobi_ir_set_path(n, path); 667 + if (ext == 0x01) { 668 + uint8_t sync = 0; 669 + (void)tobi_rd_u8(rd, &sync); 670 + n->value = sync; 671 + } 672 + n->len = tobi_rd_off(rd) - op_start; 673 + free(path); 674 + free(name); 675 + return n; 676 + } 677 + default: 678 + return unknown_node(p, op_start, raw, 2, "unsupported extended opcode"); 679 + } 680 + } 681 + 682 + static tobi_ir *parse_if(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) { 683 + size_t body_start = 0; 684 + size_t body_len = 0; 685 + size_t whole_len = 0; 686 + if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) { 687 + return unknown_node(p, op_start, 0xa0, 1, "bad IfOp package"); 688 + } 689 + tobi_rd body; 690 + tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start); 691 + tobi_ir *n = tobi_ir_new(TOBI_IR_IF, op_start, whole_len); 692 + tobi_ir *pred = parse_expr(p, &body, depth + 1); 693 + if (pred) { 694 + tobi_ir_add(n, pred); 695 + } 696 + tobi_ir_add(n, parse_block_from_reader(p, &body, depth, tobi_rd_off(&body))); 697 + rd->pos = body_start + body_len; 698 + uint8_t next = 0; 699 + if (tobi_rd_peek(rd, &next) && next == 0xa1) { 700 + size_t else_start = tobi_rd_off(rd); 701 + (void)tobi_rd_u8(rd, &next); 702 + size_t eb = 0; 703 + size_t el = 0; 704 + size_t ew = 0; 705 + if (pkg_window(p, rd, else_start, &eb, &el, &ew)) { 706 + tobi_rd er; 707 + tobi_rd_init(&er, rd->data + eb, el, rd->base + eb); 708 + tobi_ir_add(n, parse_block_from_reader(p, &er, depth, tobi_rd_off(&er))); 709 + rd->pos = eb + el; 710 + n->len = (rd->base + rd->pos) - op_start; 711 + } else { 712 + tobi_ir_add(n, unknown_node(p, else_start, 0xa1, 1, "bad ElseOp package")); 713 + } 714 + } 715 + return n; 716 + } 717 + 718 + static tobi_ir *parse_while(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) { 719 + size_t body_start = 0; 720 + size_t body_len = 0; 721 + size_t whole_len = 0; 722 + if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) { 723 + return unknown_node(p, op_start, 0xa2, 1, "bad WhileOp package"); 724 + } 725 + tobi_rd body; 726 + tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start); 727 + tobi_ir *n = tobi_ir_new(TOBI_IR_WHILE, op_start, whole_len); 728 + tobi_ir *pred = parse_expr(p, &body, depth + 1); 729 + if (pred) { 730 + tobi_ir_add(n, pred); 731 + } 732 + tobi_ir_add(n, parse_block_from_reader(p, &body, depth, tobi_rd_off(&body))); 733 + rd->pos = body_start + body_len; 734 + return n; 735 + } 736 + 737 + static int expr_opcode_arity(uint8_t op, int *has_target, const char **name) { 738 + *has_target = 0; 739 + switch (op) { 740 + case 0x71: *name = "ref_of"; return 1; 741 + case 0x72: *has_target = 1; *name = "add"; return 2; 742 + case 0x73: *has_target = 1; *name = "concat"; return 2; 743 + case 0x74: *has_target = 1; *name = "sub"; return 2; 744 + case 0x75: *name = "inc"; return 1; 745 + case 0x76: *name = "dec"; return 1; 746 + case 0x77: *has_target = 1; *name = "mul"; return 2; 747 + case 0x78: *has_target = 1; *name = "div"; return 2; 748 + case 0x79: *has_target = 1; *name = "shl"; return 2; 749 + case 0x7a: *has_target = 1; *name = "shr"; return 2; 750 + case 0x7b: *has_target = 1; *name = "and"; return 2; 751 + case 0x7c: *has_target = 1; *name = "nand"; return 2; 752 + case 0x7d: *has_target = 1; *name = "or"; return 2; 753 + case 0x7e: *has_target = 1; *name = "nor"; return 2; 754 + case 0x7f: *has_target = 1; *name = "xor"; return 2; 755 + case 0x80: *has_target = 1; *name = "not"; return 1; 756 + case 0x81: *has_target = 1; *name = "find_set_left_bit"; return 1; 757 + case 0x82: *has_target = 1; *name = "find_set_right_bit"; return 1; 758 + case 0x83: *name = "deref_of"; return 1; 759 + case 0x84: *has_target = 1; *name = "concat_res"; return 2; 760 + case 0x85: *has_target = 1; *name = "mod"; return 2; 761 + case 0x86: *name = "notify"; return 2; 762 + case 0x87: *name = "size_of"; return 1; 763 + case 0x88: *has_target = 1; *name = "index"; return 2; 764 + case 0x89: *name = "match"; return 6; 765 + case 0x8e: *name = "object_type"; return 1; 766 + case 0x90: *name = "land"; return 2; 767 + case 0x91: *name = "lor"; return 2; 768 + case 0x92: *name = "lnot"; return 1; 769 + case 0x93: *name = "eq"; return 2; 770 + case 0x94: *name = "gt"; return 2; 771 + case 0x95: *name = "lt"; return 2; 772 + case 0x96: *has_target = 1; *name = "to_buffer"; return 1; 773 + case 0x97: *has_target = 1; *name = "to_decimal_string"; return 1; 774 + case 0x98: *has_target = 1; *name = "to_hex_string"; return 1; 775 + case 0x99: *has_target = 1; *name = "to_integer"; return 1; 776 + case 0x9c: *has_target = 1; *name = "to_string"; return 2; 777 + case 0x9d: *has_target = 1; *name = "copy_object"; return 1; 778 + case 0x9e: *has_target = 1; *name = "mid"; return 3; 779 + default: return -1; 780 + } 781 + } 782 + 783 + static const char *create_field_name(uint8_t op) { 784 + switch (op) { 785 + case 0x8a: return "create_dword_field"; 786 + case 0x8b: return "create_word_field"; 787 + case 0x8c: return "create_byte_field"; 788 + case 0x8d: return "create_bit_field"; 789 + case 0x8f: return "create_qword_field"; 790 + default: return "create_field"; 791 + } 792 + } 793 + 794 + static tobi_ir *parse_string(parser *p, tobi_rd *rd, size_t op_start) { 795 + size_t s = rd->pos; 796 + while (rd->pos < rd->len && rd->data[rd->pos] != 0) { 797 + rd->pos++; 798 + } 799 + size_t n = rd->pos - s; 800 + if (rd->pos >= rd->len) { 801 + diag(p, TOBI_DIAG_ERROR, rd->base + s, "unterminated AML string"); 802 + } else { 803 + rd->pos++; 804 + } 805 + char *tmp = tobi_xstrndup((const char *)rd->data + s, n); 806 + tobi_ir *node = tobi_ir_new(TOBI_IR_STRING, op_start, tobi_rd_off(rd) - op_start); 807 + tobi_ir_set_str(node, tmp); 808 + free(tmp); 809 + return node; 810 + } 811 + 812 + static const char *small_res_name(unsigned name) { 813 + switch (name) { 814 + case 0x04: return "IRQ"; 815 + case 0x05: return "DMA"; 816 + case 0x06: return "StartDependentFn"; 817 + case 0x07: return "EndDependentFn"; 818 + case 0x08: return "IO"; 819 + case 0x09: return "FixedIO"; 820 + case 0x0e: return "VendorSmall"; 821 + case 0x0f: return "EndTag"; 822 + default: return NULL; 823 + } 824 + } 825 + 826 + static const char *large_res_name(unsigned name) { 827 + switch (name) { 828 + case 0x01: return "Memory24"; 829 + case 0x02: return "GenericRegister"; 830 + case 0x04: return "VendorLarge"; 831 + case 0x05: return "Memory32"; 832 + case 0x06: return "FixedMemory32"; 833 + case 0x07: return "DWordAddressSpace"; 834 + case 0x08: return "WordAddressSpace"; 835 + case 0x09: return "ExtendedIRQ"; 836 + case 0x0a: return "QWordAddressSpace"; 837 + case 0x0b: return "ExtendedAddressSpace"; 838 + case 0x0c: return "GPIO"; 839 + case 0x0e: return "SerialBus"; 840 + default: return NULL; 841 + } 842 + } 843 + 844 + static char *decode_resource_template(const uint8_t *data, size_t len) { 845 + if (!data || len == 0) { 846 + return NULL; 847 + } 848 + tobi_sb sb; 849 + tobi_sb_init(&sb); 850 + size_t pos = 0; 851 + int any = 0; 852 + int ended = 0; 853 + while (pos < len) { 854 + uint8_t tag = data[pos++]; 855 + if ((tag & 0x80u) != 0) { 856 + if (pos + 2 > len) { 857 + break; 858 + } 859 + unsigned name = tag & 0x7fu; 860 + size_t item_len = (size_t)data[pos] | ((size_t)data[pos + 1] << 8); 861 + pos += 2; 862 + if (item_len > len - pos) { 863 + break; 864 + } 865 + const char *label = large_res_name(name); 866 + if (!label) { 867 + label = "LargeItem"; 868 + } 869 + if (any) { 870 + tobi_sb_add(&sb, "; "); 871 + } 872 + tobi_sb_printf(&sb, "%s(len=%zu)", label, item_len); 873 + any = 1; 874 + pos += item_len; 875 + } else { 876 + unsigned name = (tag >> 3) & 0x0fu; 877 + size_t item_len = tag & 0x07u; 878 + if (item_len > len - pos) { 879 + break; 880 + } 881 + const char *label = small_res_name(name); 882 + if (!label) { 883 + label = "SmallItem"; 884 + } 885 + if (any) { 886 + tobi_sb_add(&sb, "; "); 887 + } 888 + tobi_sb_printf(&sb, "%s(len=%zu)", label, item_len); 889 + any = 1; 890 + pos += item_len; 891 + if (name == 0x0f) { 892 + ended = 1; 893 + break; 894 + } 895 + } 896 + } 897 + if (!any || !ended || pos != len) { 898 + tobi_sb_free(&sb); 899 + return NULL; 900 + } 901 + return tobi_sb_take(&sb); 902 + } 903 + 904 + static tobi_ir *parse_buffer(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) { 905 + size_t body_start = 0; 906 + size_t body_len = 0; 907 + size_t whole_len = 0; 908 + if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) { 909 + return unknown_node(p, op_start, 0x11, 1, "bad Buffer package"); 910 + } 911 + tobi_rd body; 912 + tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start); 913 + tobi_ir *n = tobi_ir_new(TOBI_IR_BUFFER, op_start, whole_len); 914 + tobi_ir *sz = parse_expr(p, &body, depth + 1); 915 + if (sz) { 916 + tobi_ir_add(n, sz); 917 + } 918 + tobi_sb hex; 919 + tobi_sb_init(&hex); 920 + const uint8_t *bytes = body.data + body.pos; 921 + size_t byte_len = tobi_rd_left(&body); 922 + tobi_hex(&hex, bytes, byte_len); 923 + char *res = decode_resource_template(bytes, byte_len); 924 + if (res) { 925 + tobi_sb_add(&hex, " resources="); 926 + tobi_sb_add(&hex, res); 927 + free(res); 928 + } 929 + tobi_ir_set_str(n, hex.buf); 930 + tobi_sb_free(&hex); 931 + rd->pos = body_start + body_len; 932 + return n; 933 + } 934 + 935 + static tobi_ir *parse_package(parser *p, tobi_rd *rd, unsigned depth, size_t op_start, int variable) { 936 + size_t body_start = 0; 937 + size_t body_len = 0; 938 + size_t whole_len = 0; 939 + if (!pkg_window(p, rd, op_start, &body_start, &body_len, &whole_len)) { 940 + return unknown_node(p, op_start, variable ? 0x13 : 0x12, 1, "bad Package"); 941 + } 942 + tobi_rd body; 943 + tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start); 944 + tobi_ir *n = tobi_ir_new(TOBI_IR_PACKAGE, op_start, whole_len); 945 + if (variable) { 946 + tobi_ir *cnt = parse_expr(p, &body, depth + 1); 947 + if (cnt) { 948 + tobi_ir_add(n, cnt); 949 + } 950 + } else { 951 + uint8_t count = 0; 952 + if (tobi_rd_u8(&body, &count)) { 953 + n->value = count; 954 + } else { 955 + diag(p, TOBI_DIAG_ERROR, tobi_rd_off(&body), "truncated package element count"); 956 + } 957 + } 958 + while (tobi_rd_left(&body) > 0) { 959 + size_t before = body.pos; 960 + tobi_ir *e = parse_expr(p, &body, depth + 1); 961 + if (e) { 962 + tobi_ir_add(n, e); 963 + } 964 + if (body.pos == before) { 965 + (void)tobi_rd_skip(&body, 1); 966 + } 967 + } 968 + rd->pos = body_start + body_len; 969 + return n; 970 + } 971 + 972 + static tobi_ir *parse_name_or_call(parser *p, tobi_rd *rd, unsigned depth) { 973 + size_t off = tobi_rd_off(rd); 974 + char *name = NULL; 975 + if (!parse_namestring_diag(p, rd, &name)) { 976 + return unknown_node(p, off, 0, 1, "bad namestring expression"); 977 + } 978 + char *path = tobi_nm_resolve(p->scope, name); 979 + int argc = method_find(p, path); 980 + if (argc < 0) { 981 + argc = method_find(p, name); 982 + } 983 + tobi_ir *n = tobi_ir_new(argc >= 0 ? TOBI_IR_CALL : TOBI_IR_REF, off, 0); 984 + tobi_ir_set_name(n, name); 985 + tobi_ir_set_path(n, path); 986 + if (argc >= 0) { 987 + n->value = (uint64_t)argc; 988 + for (int i = 0; i < argc && tobi_rd_left(rd) > 0; i++) { 989 + tobi_ir *arg = parse_expr(p, rd, depth + 1); 990 + if (arg) { 991 + tobi_ir_add(n, arg); 992 + } 993 + } 994 + } 995 + n->len = tobi_rd_off(rd) - off; 996 + free(path); 997 + free(name); 998 + return n; 999 + } 1000 + 1001 + static tobi_ir *parse_expr(parser *p, tobi_rd *rd, unsigned depth) { 1002 + if (depth > MAX_DEPTH) { 1003 + diag(p, TOBI_DIAG_ERROR, tobi_rd_off(rd), "AML nesting too deep"); 1004 + return NULL; 1005 + } 1006 + size_t off = tobi_rd_off(rd); 1007 + uint8_t op = 0; 1008 + if (!tobi_rd_u8(rd, &op)) { 1009 + diag(p, TOBI_DIAG_ERROR, off, "truncated AML expression"); 1010 + return NULL; 1011 + } 1012 + if (op == 0x00 || op == 0x01 || op == 0xff) { 1013 + tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, off, 1); 1014 + n->value = op == 0x00 ? 0 : (op == 0x01 ? 1 : UINT64_MAX); 1015 + return n; 1016 + } 1017 + if (op == 0x0a) { 1018 + uint8_t v = 0; 1019 + if (!tobi_rd_u8(rd, &v)) { 1020 + diag(p, TOBI_DIAG_ERROR, off, "truncated ByteConst"); 1021 + } 1022 + tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, off, tobi_rd_off(rd) - off); 1023 + n->value = v; 1024 + return n; 1025 + } 1026 + if (op == 0x0b) { 1027 + uint16_t v = 0; 1028 + if (!tobi_rd_u16(rd, &v)) { 1029 + diag(p, TOBI_DIAG_ERROR, off, "truncated WordConst"); 1030 + } 1031 + tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, off, tobi_rd_off(rd) - off); 1032 + n->value = v; 1033 + return n; 1034 + } 1035 + if (op == 0x0c) { 1036 + uint32_t v = 0; 1037 + if (!tobi_rd_u32(rd, &v)) { 1038 + diag(p, TOBI_DIAG_ERROR, off, "truncated DWordConst"); 1039 + } 1040 + tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, off, tobi_rd_off(rd) - off); 1041 + n->value = v; 1042 + return n; 1043 + } 1044 + if (op == 0x0e) { 1045 + uint64_t v = 0; 1046 + if (!tobi_rd_u64(rd, &v)) { 1047 + diag(p, TOBI_DIAG_ERROR, off, "truncated QWordConst"); 1048 + } 1049 + tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, off, tobi_rd_off(rd) - off); 1050 + n->value = v; 1051 + return n; 1052 + } 1053 + if (op == 0x0d) { 1054 + return parse_string(p, rd, off); 1055 + } 1056 + if (op == 0x5b) { 1057 + return parse_ext(p, rd, depth, off); 1058 + } 1059 + if (op == 0x11) { 1060 + return parse_buffer(p, rd, depth, off); 1061 + } 1062 + if (op == 0x12 || op == 0x13) { 1063 + return parse_package(p, rd, depth, off, op == 0x13); 1064 + } 1065 + if (op >= 0x60 && op <= 0x67) { 1066 + tobi_ir *n = tobi_ir_new(TOBI_IR_REF, off, 1); 1067 + char name[8]; 1068 + (void)snprintf(name, sizeof(name), "Local%u", (unsigned)(op - 0x60)); 1069 + tobi_ir_set_name(n, name); 1070 + return n; 1071 + } 1072 + if (op >= 0x68 && op <= 0x6e) { 1073 + tobi_ir *n = tobi_ir_new(TOBI_IR_REF, off, 1); 1074 + char name[8]; 1075 + (void)snprintf(name, sizeof(name), "Arg%u", (unsigned)(op - 0x68)); 1076 + tobi_ir_set_name(n, name); 1077 + return n; 1078 + } 1079 + if (op == 0x70) { 1080 + tobi_ir *n = tobi_ir_new(TOBI_IR_STORE, off, 0); 1081 + tobi_ir *src = parse_expr(p, rd, depth + 1); 1082 + tobi_ir *dst = parse_expr(p, rd, depth + 1); 1083 + if (src) { 1084 + tobi_ir_add(n, src); 1085 + } 1086 + if (dst) { 1087 + tobi_ir_add(n, dst); 1088 + } 1089 + n->len = tobi_rd_off(rd) - off; 1090 + return n; 1091 + } 1092 + if ((op >= 0x8a && op <= 0x8d) || op == 0x8f) { 1093 + tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, off, 0); 1094 + n->raw_op = op; 1095 + tobi_ir_set_name(n, create_field_name(op)); 1096 + add_expr_children(p, rd, depth, n, 2); 1097 + tobi_ir *dst = ref_from_name(p, rd, off, "bad create-field namestring"); 1098 + if (dst) { 1099 + tobi_ir_add(n, dst); 1100 + } 1101 + n->len = tobi_rd_off(rd) - off; 1102 + return n; 1103 + } 1104 + int has_target = 0; 1105 + const char *ename = NULL; 1106 + int arity = expr_opcode_arity(op, &has_target, &ename); 1107 + if (arity >= 0) { 1108 + tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, off, 0); 1109 + tobi_ir_set_name(n, ename); 1110 + n->raw_op = op; 1111 + for (int i = 0; i < arity; i++) { 1112 + tobi_ir *arg = parse_expr(p, rd, depth + 1); 1113 + if (arg) { 1114 + tobi_ir_add(n, arg); 1115 + } 1116 + } 1117 + if (has_target) { 1118 + tobi_ir *target = parse_expr(p, rd, depth + 1); 1119 + if (target) { 1120 + tobi_ir_add(n, target); 1121 + } 1122 + } 1123 + n->len = tobi_rd_off(rd) - off; 1124 + return n; 1125 + } 1126 + rd->pos--; 1127 + uint8_t b = 0; 1128 + (void)tobi_rd_peek(rd, &b); 1129 + if (b == '\\' || b == '^' || b == 0x2e || b == 0x2f || tobi_nm_is_lead(b)) { 1130 + return parse_name_or_call(p, rd, depth); 1131 + } 1132 + (void)tobi_rd_u8(rd, &op); 1133 + return unknown_node(p, off, op, 1, "unknown expression opcode"); 1134 + } 1135 + 1136 + static tobi_ir *parse_return(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) { 1137 + tobi_ir *n = tobi_ir_new(TOBI_IR_RETURN, op_start, 0); 1138 + if (tobi_rd_left(rd) > 0) { 1139 + tobi_ir *v = parse_expr(p, rd, depth + 1); 1140 + if (v) { 1141 + tobi_ir_add(n, v); 1142 + } 1143 + } 1144 + n->len = tobi_rd_off(rd) - op_start; 1145 + return n; 1146 + } 1147 + 1148 + static tobi_ir *parse_term(parser *p, tobi_rd *rd, unsigned depth) { 1149 + if (depth > MAX_DEPTH) { 1150 + diag(p, TOBI_DIAG_ERROR, tobi_rd_off(rd), "AML nesting too deep"); 1151 + return NULL; 1152 + } 1153 + if (++p->terms > MAX_TERMS) { 1154 + diag(p, TOBI_DIAG_ERROR, tobi_rd_off(rd), "too many AML terms"); 1155 + rd->pos = rd->len; 1156 + return NULL; 1157 + } 1158 + size_t off = tobi_rd_off(rd); 1159 + uint8_t op = 0; 1160 + if (!tobi_rd_u8(rd, &op)) { 1161 + return NULL; 1162 + } 1163 + switch (op) { 1164 + case 0x06: return parse_alias(p, rd, off); 1165 + case 0x08: return parse_name(p, rd, depth, off); 1166 + case 0x10: return parse_pkg_named(p, rd, depth, TOBI_IR_SCOPE, "ScopeOp", off); 1167 + case 0x14: return parse_method(p, rd, depth, off); 1168 + case 0x5b: return parse_ext(p, rd, depth, off); 1169 + case 0x70: 1170 + rd->pos--; 1171 + return parse_expr(p, rd, depth); 1172 + case 0xa0: return parse_if(p, rd, depth, off); 1173 + case 0xa2: return parse_while(p, rd, depth, off); 1174 + case 0xa3: { 1175 + tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, off, 1); 1176 + tobi_ir_set_name(n, "noop"); 1177 + return n; 1178 + } 1179 + case 0xa4: return parse_return(p, rd, depth, off); 1180 + case 0xa5: return tobi_ir_new(TOBI_IR_BREAK, off, 1); 1181 + case 0x9f: return tobi_ir_new(TOBI_IR_CONTINUE, off, 1); 1182 + case 0xcc: { 1183 + tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, off, 1); 1184 + n->raw_op = 0xcc; 1185 + tobi_ir_set_name(n, "breakpoint"); 1186 + return n; 1187 + } 1188 + default: 1189 + rd->pos--; 1190 + return parse_expr(p, rd, depth); 1191 + } 1192 + } 1193 + 1194 + static int parse_term_list(parser *p, tobi_rd *rd, tobi_ir *parent, unsigned depth) { 1195 + while (tobi_rd_left(rd) > 0) { 1196 + size_t before = rd->pos; 1197 + tobi_ir *n = parse_term(p, rd, depth); 1198 + if (n) { 1199 + tobi_ir_add(parent, n); 1200 + } 1201 + if (rd->pos == before) { 1202 + uint8_t b = 0; 1203 + if (tobi_rd_u8(rd, &b)) { 1204 + tobi_ir_add(parent, unknown_node(p, rd->base + before, b, 1, "parser made no progress")); 1205 + } else { 1206 + break; 1207 + } 1208 + } 1209 + } 1210 + return 1; 1211 + } 1212 + 1213 + static int scan_pkg_window(tobi_rd *rd, size_t *body_start, size_t *body_len) { 1214 + size_t pkg_start = rd->pos; 1215 + size_t pkg_len = 0; 1216 + size_t enc = 0; 1217 + if (!tobi_rd_pkg_len(rd, &pkg_len, &enc) || pkg_len < enc) { 1218 + return 0; 1219 + } 1220 + size_t end = pkg_start + pkg_len; 1221 + if (end > rd->len || end < rd->pos) { 1222 + return 0; 1223 + } 1224 + *body_start = rd->pos; 1225 + *body_len = end - rd->pos; 1226 + return 1; 1227 + } 1228 + 1229 + static void scan_methods(parser *p, tobi_rd *rd, unsigned depth) { 1230 + if (depth > MAX_DEPTH) { 1231 + return; 1232 + } 1233 + while (tobi_rd_left(rd) > 0) { 1234 + size_t start = rd->pos; 1235 + uint8_t op = 0; 1236 + if (!tobi_rd_u8(rd, &op)) { 1237 + return; 1238 + } 1239 + if (op == 0x14 || op == 0x10) { 1240 + size_t body_start = 0; 1241 + size_t body_len = 0; 1242 + if (!scan_pkg_window(rd, &body_start, &body_len)) { 1243 + rd->pos = rd->len; 1244 + return; 1245 + } 1246 + tobi_rd body; 1247 + tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start); 1248 + char *name = NULL; 1249 + if (!tobi_nm_namestring(&body, &name)) { 1250 + rd->pos = body_start + body_len; 1251 + continue; 1252 + } 1253 + char *path = tobi_nm_resolve(p->scope, name); 1254 + if (op == 0x14) { 1255 + uint8_t flags = 0; 1256 + if (tobi_rd_u8(&body, &flags)) { 1257 + method_add(p, path, name, flags & 0x07u); 1258 + } 1259 + } else { 1260 + char *old_scope = p->scope; 1261 + p->scope = tobi_xstrdup(path); 1262 + scan_methods(p, &body, depth + 1); 1263 + free(p->scope); 1264 + p->scope = old_scope; 1265 + } 1266 + free(path); 1267 + free(name); 1268 + rd->pos = body_start + body_len; 1269 + continue; 1270 + } 1271 + if (op == 0x5b) { 1272 + uint8_t ext = 0; 1273 + if (!tobi_rd_u8(rd, &ext)) { 1274 + return; 1275 + } 1276 + if (ext == 0x82 || ext == 0x83 || ext == 0x84 || ext == 0x85) { 1277 + size_t body_start = 0; 1278 + size_t body_len = 0; 1279 + if (!scan_pkg_window(rd, &body_start, &body_len)) { 1280 + rd->pos = rd->len; 1281 + return; 1282 + } 1283 + tobi_rd body; 1284 + tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start); 1285 + char *name = NULL; 1286 + if (tobi_nm_namestring(&body, &name)) { 1287 + char *path = tobi_nm_resolve(p->scope, name); 1288 + if (ext == 0x83) { 1289 + (void)tobi_rd_skip(&body, 6); 1290 + } else if (ext == 0x84) { 1291 + (void)tobi_rd_skip(&body, 2); 1292 + } 1293 + char *old_scope = p->scope; 1294 + p->scope = tobi_xstrdup(path); 1295 + scan_methods(p, &body, depth + 1); 1296 + free(p->scope); 1297 + p->scope = old_scope; 1298 + free(path); 1299 + free(name); 1300 + } 1301 + rd->pos = body_start + body_len; 1302 + continue; 1303 + } 1304 + if (ext == 0x81 || ext == 0x86 || ext == 0x87) { 1305 + size_t body_start = 0; 1306 + size_t body_len = 0; 1307 + if (scan_pkg_window(rd, &body_start, &body_len)) { 1308 + rd->pos = body_start + body_len; 1309 + } 1310 + continue; 1311 + } 1312 + } 1313 + if (op == 0x11 || op == 0x12 || op == 0x13 || op == 0xa0 || op == 0xa1 || op == 0xa2) { 1314 + size_t body_start = 0; 1315 + size_t body_len = 0; 1316 + if (scan_pkg_window(rd, &body_start, &body_len)) { 1317 + if (op == 0xa0 || op == 0xa1 || op == 0xa2) { 1318 + tobi_rd body; 1319 + tobi_rd_init(&body, rd->data + body_start, body_len, rd->base + body_start); 1320 + scan_methods(p, &body, depth + 1); 1321 + } 1322 + rd->pos = body_start + body_len; 1323 + continue; 1324 + } 1325 + } 1326 + if (rd->pos == start) { 1327 + rd->pos++; 1328 + } 1329 + } 1330 + } 1331 + 1332 + int tobi_parse(const uint8_t *data, size_t len, int strict, tobi_parse_result *out) { 1333 + memset(out, 0, sizeof(*out)); 1334 + tobi_diag_init(&out->diag); 1335 + if (!detect_input(data, len, strict, &out->diag, &out->meta)) { 1336 + return 0; 1337 + } 1338 + out->root = tobi_ir_new(TOBI_IR_ROOT, out->meta.aml_off, out->meta.aml_len); 1339 + parser p; 1340 + memset(&p, 0, sizeof(p)); 1341 + p.diag = &out->diag; 1342 + p.strict = strict; 1343 + p.scope = tobi_xstrdup("\\"); 1344 + tobi_rd rd; 1345 + if (out->meta.aml_off <= len && out->meta.aml_len <= len - out->meta.aml_off) { 1346 + tobi_rd scan; 1347 + tobi_rd_init(&scan, data + out->meta.aml_off, out->meta.aml_len, out->meta.aml_off); 1348 + scan_methods(&p, &scan, 0); 1349 + tobi_rd_init(&rd, data + out->meta.aml_off, out->meta.aml_len, out->meta.aml_off); 1350 + (void)parse_term_list(&p, &rd, out->root, 0); 1351 + } 1352 + parser_free(&p); 1353 + return !(strict && tobi_diag_has_error(&out->diag)); 1354 + } 1355 + 1356 + int tobi_parse_multi(const tobi_parse_input *inputs, size_t count, int strict, tobi_parse_result *out) { 1357 + memset(out, 0, sizeof(*out)); 1358 + tobi_diag_init(&out->diag); 1359 + memcpy(out->meta.signature, "MULT", 4); 1360 + out->meta.signature[4] = '\0'; 1361 + out->root = tobi_ir_new(TOBI_IR_ROOT, 0, 0); 1362 + if (!inputs || count == 0) { 1363 + tobi_diag_add(&out->diag, TOBI_DIAG_ERROR, 0, "no AML inputs"); 1364 + return 0; 1365 + } 1366 + tobi_input_meta *metas = tobi_xcalloc(count, sizeof(metas[0])); 1367 + int ok = 1; 1368 + parser p; 1369 + memset(&p, 0, sizeof(p)); 1370 + p.diag = &out->diag; 1371 + p.strict = strict; 1372 + p.scope = tobi_xstrdup("\\"); 1373 + for (size_t i = 0; i < count; i++) { 1374 + if (!detect_input(inputs[i].data, inputs[i].len, strict, &out->diag, &metas[i])) { 1375 + ok = 0; 1376 + continue; 1377 + } 1378 + out->meta.aml_len += metas[i].aml_len; 1379 + if (metas[i].aml_off <= inputs[i].len && metas[i].aml_len <= inputs[i].len - metas[i].aml_off) { 1380 + tobi_rd scan; 1381 + tobi_rd_init(&scan, inputs[i].data + metas[i].aml_off, metas[i].aml_len, metas[i].aml_off); 1382 + scan_methods(&p, &scan, 0); 1383 + } 1384 + } 1385 + if (!strict || ok) { 1386 + for (size_t i = 0; i < count; i++) { 1387 + if (metas[i].aml_off > inputs[i].len || metas[i].aml_len > inputs[i].len - metas[i].aml_off) { 1388 + continue; 1389 + } 1390 + tobi_ir *file = tobi_ir_new(TOBI_IR_BLOCK, metas[i].aml_off, metas[i].aml_len); 1391 + tobi_ir_set_name(file, inputs[i].name ? inputs[i].name : "<input>"); 1392 + tobi_rd rd; 1393 + tobi_rd_init(&rd, inputs[i].data + metas[i].aml_off, metas[i].aml_len, metas[i].aml_off); 1394 + (void)parse_term_list(&p, &rd, file, 0); 1395 + tobi_ir_add(out->root, file); 1396 + } 1397 + } 1398 + parser_free(&p); 1399 + free(metas); 1400 + return ok && !(strict && tobi_diag_has_error(&out->diag)); 1401 + } 1402 + 1403 + void tobi_parse_result_free(tobi_parse_result *res) { 1404 + tobi_ir_free(res->root); 1405 + res->root = NULL; 1406 + tobi_diag_free(&res->diag); 1407 + }
+39
src/p.h
··· 1 + #ifndef TOBI_P_H 2 + #define TOBI_P_H 3 + 4 + #include "dg.h" 5 + #include "ir.h" 6 + 7 + #include <stddef.h> 8 + #include <stdint.h> 9 + 10 + typedef struct tobi_input_meta { 11 + int is_table; 12 + char signature[5]; 13 + size_t table_len; 14 + size_t aml_off; 15 + size_t aml_len; 16 + } tobi_input_meta; 17 + 18 + typedef struct tobi_parse_result { 19 + tobi_ir *root; 20 + tobi_diag_list diag; 21 + tobi_input_meta meta; 22 + } tobi_parse_result; 23 + 24 + typedef struct tobi_parse_input { 25 + const uint8_t *data; 26 + size_t len; 27 + const char *name; 28 + } tobi_parse_input; 29 + 30 + /** Parse a raw AML blob or DSDT/SSDT table into IR and diagnostics. */ 31 + int tobi_parse(const uint8_t *data, size_t len, int strict, tobi_parse_result *out); 32 + 33 + /** Parse several AML blobs/tables into one IR with a shared method namespace pre-pass. */ 34 + int tobi_parse_multi(const tobi_parse_input *inputs, size_t count, int strict, tobi_parse_result *out); 35 + 36 + /** Free all allocations owned by a parse result. */ 37 + void tobi_parse_result_free(tobi_parse_result *res); 38 + 39 + #endif
+125
src/rd.c
··· 1 + #include "rd.h" 2 + 3 + #include <string.h> 4 + 5 + void tobi_rd_init(tobi_rd *rd, const uint8_t *data, size_t len, size_t base) { 6 + rd->data = data; 7 + rd->len = len; 8 + rd->pos = 0; 9 + rd->base = base; 10 + rd->failed = 0; 11 + } 12 + 13 + size_t tobi_rd_off(const tobi_rd *rd) { 14 + return rd->base + rd->pos; 15 + } 16 + 17 + size_t tobi_rd_left(const tobi_rd *rd) { 18 + return rd->pos <= rd->len ? rd->len - rd->pos : 0; 19 + } 20 + 21 + int tobi_rd_u8(tobi_rd *rd, uint8_t *out) { 22 + if (rd->pos >= rd->len) { 23 + rd->failed = 1; 24 + return 0; 25 + } 26 + *out = rd->data[rd->pos++]; 27 + return 1; 28 + } 29 + 30 + int tobi_rd_peek(const tobi_rd *rd, uint8_t *out) { 31 + if (rd->pos >= rd->len) { 32 + return 0; 33 + } 34 + *out = rd->data[rd->pos]; 35 + return 1; 36 + } 37 + 38 + int tobi_rd_u16(tobi_rd *rd, uint16_t *out) { 39 + uint8_t b[2]; 40 + if (!tobi_rd_bytes(rd, b, sizeof(b))) { 41 + return 0; 42 + } 43 + *out = (uint16_t)b[0] | ((uint16_t)b[1] << 8); 44 + return 1; 45 + } 46 + 47 + int tobi_rd_u32(tobi_rd *rd, uint32_t *out) { 48 + uint8_t b[4]; 49 + if (!tobi_rd_bytes(rd, b, sizeof(b))) { 50 + return 0; 51 + } 52 + *out = (uint32_t)b[0] | ((uint32_t)b[1] << 8) | ((uint32_t)b[2] << 16) | ((uint32_t)b[3] << 24); 53 + return 1; 54 + } 55 + 56 + int tobi_rd_u64(tobi_rd *rd, uint64_t *out) { 57 + uint8_t b[8]; 58 + if (!tobi_rd_bytes(rd, b, sizeof(b))) { 59 + return 0; 60 + } 61 + uint64_t v = 0; 62 + for (size_t i = 0; i < 8; i++) { 63 + v |= (uint64_t)b[i] << (i * 8); 64 + } 65 + *out = v; 66 + return 1; 67 + } 68 + 69 + int tobi_rd_bytes(tobi_rd *rd, uint8_t *out, size_t n) { 70 + if (n > tobi_rd_left(rd)) { 71 + rd->failed = 1; 72 + return 0; 73 + } 74 + if (n != 0) { 75 + memcpy(out, rd->data + rd->pos, n); 76 + } 77 + rd->pos += n; 78 + return 1; 79 + } 80 + 81 + int tobi_rd_skip(tobi_rd *rd, size_t n) { 82 + if (n > tobi_rd_left(rd)) { 83 + rd->failed = 1; 84 + return 0; 85 + } 86 + rd->pos += n; 87 + return 1; 88 + } 89 + 90 + int tobi_rd_sub(tobi_rd *rd, size_t n, tobi_rd *sub) { 91 + if (n > tobi_rd_left(rd)) { 92 + rd->failed = 1; 93 + return 0; 94 + } 95 + tobi_rd_init(sub, rd->data + rd->pos, n, rd->base + rd->pos); 96 + rd->pos += n; 97 + return 1; 98 + } 99 + 100 + int tobi_rd_pkg_len(tobi_rd *rd, size_t *pkg_len, size_t *encoded_len) { 101 + size_t start = rd->pos; 102 + uint8_t lead = 0; 103 + if (!tobi_rd_u8(rd, &lead)) { 104 + return 0; 105 + } 106 + unsigned follow = lead >> 6; 107 + size_t len = 0; 108 + if (follow == 0) { 109 + len = (size_t)(lead & 0x3f); 110 + } else { 111 + len = (size_t)(lead & 0x0f); 112 + for (unsigned i = 0; i < follow; i++) { 113 + uint8_t b = 0; 114 + if (!tobi_rd_u8(rd, &b)) { 115 + rd->pos = start; 116 + rd->failed = 1; 117 + return 0; 118 + } 119 + len |= (size_t)b << (4 + 8 * i); 120 + } 121 + } 122 + *pkg_len = len; 123 + *encoded_len = rd->pos - start; 124 + return 1; 125 + }
+51
src/rd.h
··· 1 + #ifndef TOBI_RD_H 2 + #define TOBI_RD_H 3 + 4 + #include <stddef.h> 5 + #include <stdint.h> 6 + 7 + typedef struct tobi_rd { 8 + const uint8_t *data; 9 + size_t len; 10 + size_t pos; 11 + size_t base; 12 + int failed; 13 + } tobi_rd; 14 + 15 + /** Initialise a bounded byte reader over data at a source base offset. */ 16 + void tobi_rd_init(tobi_rd *rd, const uint8_t *data, size_t len, size_t base); 17 + 18 + /** Return the absolute source offset of the reader cursor. */ 19 + size_t tobi_rd_off(const tobi_rd *rd); 20 + 21 + /** Return bytes still available in the reader. */ 22 + size_t tobi_rd_left(const tobi_rd *rd); 23 + 24 + /** Read an unsigned byte. Returns 0 on bounds failure. */ 25 + int tobi_rd_u8(tobi_rd *rd, uint8_t *out); 26 + 27 + /** Peek an unsigned byte without advancing. Returns 0 on bounds failure. */ 28 + int tobi_rd_peek(const tobi_rd *rd, uint8_t *out); 29 + 30 + /** Read a little-endian 16-bit integer. */ 31 + int tobi_rd_u16(tobi_rd *rd, uint16_t *out); 32 + 33 + /** Read a little-endian 32-bit integer. */ 34 + int tobi_rd_u32(tobi_rd *rd, uint32_t *out); 35 + 36 + /** Read a little-endian 64-bit integer. */ 37 + int tobi_rd_u64(tobi_rd *rd, uint64_t *out); 38 + 39 + /** Read n bytes into out. */ 40 + int tobi_rd_bytes(tobi_rd *rd, uint8_t *out, size_t n); 41 + 42 + /** Skip n bytes. */ 43 + int tobi_rd_skip(tobi_rd *rd, size_t n); 44 + 45 + /** Create a sub-reader spanning n bytes at the current cursor and advance. */ 46 + int tobi_rd_sub(tobi_rd *rd, size_t n, tobi_rd *sub); 47 + 48 + /** Decode an AML package length and report decoded length plus encoded size. */ 49 + int tobi_rd_pkg_len(tobi_rd *rd, size_t *pkg_len, size_t *encoded_len); 50 + 51 + #endif
+112
src/str.c
··· 1 + #include "str.h" 2 + 3 + #include "mem.h" 4 + 5 + #include <stdarg.h> 6 + #include <stdio.h> 7 + #include <stdlib.h> 8 + #include <string.h> 9 + 10 + static void need(tobi_sb *sb, size_t add) { 11 + size_t req = sb->len + add + 1; 12 + if (req <= sb->cap) { 13 + return; 14 + } 15 + size_t cap = sb->cap ? sb->cap : 64; 16 + while (cap < req) { 17 + cap *= 2; 18 + } 19 + sb->buf = tobi_xrealloc(sb->buf, cap); 20 + sb->cap = cap; 21 + } 22 + 23 + void tobi_sb_init(tobi_sb *sb) { 24 + sb->buf = NULL; 25 + sb->len = 0; 26 + sb->cap = 0; 27 + need(sb, 0); 28 + sb->buf[0] = '\0'; 29 + } 30 + 31 + void tobi_sb_free(tobi_sb *sb) { 32 + free(sb->buf); 33 + sb->buf = NULL; 34 + sb->len = 0; 35 + sb->cap = 0; 36 + } 37 + 38 + void tobi_sb_add(tobi_sb *sb, const char *s) { 39 + tobi_sb_addn(sb, s, strlen(s)); 40 + } 41 + 42 + void tobi_sb_addn(tobi_sb *sb, const char *s, size_t n) { 43 + need(sb, n); 44 + memcpy(sb->buf + sb->len, s, n); 45 + sb->len += n; 46 + sb->buf[sb->len] = '\0'; 47 + } 48 + 49 + void tobi_sb_ch(tobi_sb *sb, char c) { 50 + need(sb, 1); 51 + sb->buf[sb->len++] = c; 52 + sb->buf[sb->len] = '\0'; 53 + } 54 + 55 + void tobi_sb_printf(tobi_sb *sb, const char *fmt, ...) { 56 + va_list ap; 57 + va_start(ap, fmt); 58 + va_list cp; 59 + va_copy(cp, ap); 60 + int n = vsnprintf(NULL, 0, fmt, cp); 61 + va_end(cp); 62 + if (n < 0) { 63 + va_end(ap); 64 + return; 65 + } 66 + need(sb, (size_t)n); 67 + (void)vsnprintf(sb->buf + sb->len, sb->cap - sb->len, fmt, ap); 68 + va_end(ap); 69 + sb->len += (size_t)n; 70 + } 71 + 72 + char *tobi_sb_take(tobi_sb *sb) { 73 + char *out = sb->buf; 74 + sb->buf = NULL; 75 + sb->len = 0; 76 + sb->cap = 0; 77 + return out; 78 + } 79 + 80 + void tobi_json_string(tobi_sb *sb, const char *s) { 81 + tobi_sb_ch(sb, '"'); 82 + for (const unsigned char *p = (const unsigned char *)s; *p; p++) { 83 + unsigned char c = *p; 84 + switch (c) { 85 + case '"': tobi_sb_add(sb, "\\\""); break; 86 + case '\\': tobi_sb_add(sb, "\\\\"); break; 87 + case '\b': tobi_sb_add(sb, "\\b"); break; 88 + case '\f': tobi_sb_add(sb, "\\f"); break; 89 + case '\n': tobi_sb_add(sb, "\\n"); break; 90 + case '\r': tobi_sb_add(sb, "\\r"); break; 91 + case '\t': tobi_sb_add(sb, "\\t"); break; 92 + default: 93 + if (c < 0x20) { 94 + char e[7]; 95 + (void)snprintf(e, sizeof(e), "\\u%04x", (unsigned)c); 96 + tobi_sb_add(sb, e); 97 + } else { 98 + tobi_sb_ch(sb, (char)c); 99 + } 100 + break; 101 + } 102 + } 103 + tobi_sb_ch(sb, '"'); 104 + } 105 + 106 + void tobi_hex(tobi_sb *sb, const uint8_t *data, size_t len) { 107 + static const char hex[] = "0123456789abcdef"; 108 + for (size_t i = 0; i < len; i++) { 109 + char b[2] = {hex[data[i] >> 4], hex[data[i] & 15]}; 110 + tobi_sb_addn(sb, b, 2); 111 + } 112 + }
+40
src/str.h
··· 1 + #ifndef TOBI_STR_H 2 + #define TOBI_STR_H 3 + 4 + #include <stddef.h> 5 + #include <stdint.h> 6 + 7 + typedef struct tobi_sb { 8 + char *buf; 9 + size_t len; 10 + size_t cap; 11 + } tobi_sb; 12 + 13 + /** Initialise an empty string builder. */ 14 + void tobi_sb_init(tobi_sb *sb); 15 + 16 + /** Release storage owned by a string builder. */ 17 + void tobi_sb_free(tobi_sb *sb); 18 + 19 + /** Append a NUL-terminated string. */ 20 + void tobi_sb_add(tobi_sb *sb, const char *s); 21 + 22 + /** Append exactly n bytes. */ 23 + void tobi_sb_addn(tobi_sb *sb, const char *s, size_t n); 24 + 25 + /** Append one character. */ 26 + void tobi_sb_ch(tobi_sb *sb, char c); 27 + 28 + /** Append formatted text. */ 29 + void tobi_sb_printf(tobi_sb *sb, const char *fmt, ...); 30 + 31 + /** Return the builder buffer, transferring ownership to the caller. */ 32 + char *tobi_sb_take(tobi_sb *sb); 33 + 34 + /** Append a JSON string literal with escaping. */ 35 + void tobi_json_string(tobi_sb *sb, const char *s); 36 + 37 + /** Append bytes as a lower-case hexadecimal string. */ 38 + void tobi_hex(tobi_sb *sb, const uint8_t *data, size_t len); 39 + 40 + #endif
+25
test/main.c
··· 1 + #include "t.h" 2 + 3 + int main(int argc, char **argv) { 4 + struct ent { const char *name; int (*fn)(void); } tests[] = { 5 + {"rd", t_rd}, {"nm", t_nm}, {"p", t_p}, {"ir", t_ir}, {"cf", t_cf}, 6 + {"da", t_da}, {"dc", t_dc}, {"js", t_js}, {"bad", t_bad}, {"cli", t_cli}, 7 + }; 8 + if (argc == 2) { 9 + for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { 10 + if (strcmp(argv[1], tests[i].name) == 0) { 11 + return tests[i].fn(); 12 + } 13 + } 14 + fprintf(stderr, "unknown test %s\n", argv[1]); 15 + return 2; 16 + } 17 + for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { 18 + int rc = tests[i].fn(); 19 + if (rc != 0) { 20 + fprintf(stderr, "%s failed\n", tests[i].name); 21 + return rc; 22 + } 23 + } 24 + return 0; 25 + }
+22
test/t.h
··· 1 + #ifndef TOBI_T_H 2 + #define TOBI_T_H 3 + 4 + #include <stdio.h> 5 + #include <stdlib.h> 6 + #include <string.h> 7 + 8 + #define T_CHECK(x) do { if (!(x)) { fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, __LINE__, #x); return 1; } } while (0) 9 + #define T_STR(h, n) T_CHECK(strstr((h), (n)) != NULL) 10 + 11 + int t_rd(void); 12 + int t_nm(void); 13 + int t_p(void); 14 + int t_ir(void); 15 + int t_cf(void); 16 + int t_da(void); 17 + int t_dc(void); 18 + int t_js(void); 19 + int t_bad(void); 20 + int t_cli(void); 21 + 22 + #endif
+48
test/t_bad.c
··· 1 + #include "t.h" 2 + 3 + #include "p.h" 4 + 5 + static int parse_has_error(const uint8_t *b, size_t n) { 6 + tobi_parse_result r; 7 + (void)tobi_parse(b, n, 0, &r); 8 + int has = tobi_diag_has_error(&r.diag); 9 + tobi_parse_result_free(&r); 10 + return has; 11 + } 12 + 13 + static int strict_fails(const uint8_t *b, size_t n) { 14 + tobi_parse_result r; 15 + int ok = tobi_parse(b, n, 1, &r); 16 + int has = tobi_diag_has_error(&r.diag); 17 + tobi_parse_result_free(&r); 18 + return !ok && has; 19 + } 20 + 21 + int t_bad(void) { 22 + uint8_t trunc_pkg[] = {0x14,0x20,'M','T'}; 23 + T_CHECK(parse_has_error(trunc_pkg, sizeof(trunc_pkg))); 24 + uint8_t invalid_pkg[] = {0x14,0x80,0x00}; 25 + T_CHECK(parse_has_error(invalid_pkg, sizeof(invalid_pkg))); 26 + uint8_t unterm_str[] = {0x0d,'a','b'}; 27 + T_CHECK(parse_has_error(unterm_str, sizeof(unterm_str))); 28 + uint8_t trunc_name[] = {0x08,'A'}; 29 + T_CHECK(parse_has_error(trunc_name, sizeof(trunc_name))); 30 + uint8_t bad_table[40] = {'D','S','D','T',1,0,0,0}; 31 + T_CHECK(parse_has_error(bad_table, sizeof(bad_table))); 32 + uint8_t bad_sum[40] = {'S','S','D','T',40,0,0,0}; 33 + T_CHECK(strict_fails(bad_sum, sizeof(bad_sum))); 34 + uint8_t rnd[128]; 35 + for (size_t i = 0; i < sizeof(rnd); i++) { 36 + rnd[i] = (uint8_t)(i * 37u + 11u); 37 + } 38 + tobi_parse_result r; 39 + T_CHECK(tobi_parse(rnd, sizeof(rnd), 0, &r)); 40 + tobi_parse_result_free(&r); 41 + uint8_t deep[160]; 42 + for (size_t i = 0; i < sizeof(deep); i += 2) { 43 + deep[i] = 0x10; 44 + deep[i + 1] = 0x7f; 45 + } 46 + T_CHECK(parse_has_error(deep, sizeof(deep))); 47 + return 0; 48 + }
+37
test/t_cf.c
··· 1 + #include "t.h" 2 + 3 + #include "cf.h" 4 + #include "p.h" 5 + 6 + int t_cf(void) { 7 + uint8_t ifelse[] = {0x14,0x15,'I','F','E','0',0x01,0xa0,0x06,0x68,0x70,0x0a,0x01,0x60,0xa1,0x05,0x70,0x0a,0x02,0x60,0xa4,0x60}; 8 + tobi_parse_result r; 9 + T_CHECK(tobi_parse(ifelse, sizeof(ifelse), 0, &r)); 10 + T_CHECK(tobi_cf_recover(r.root, &r.diag)); 11 + tobi_ir *ifn = r.root->child[0]->child[0]->child[0]; 12 + T_CHECK(ifn->kind == TOBI_IR_IF && ifn->child_len == 3); 13 + char *dot = tobi_cf_dot(r.root); 14 + T_STR(dot, "digraph tobi_cfg"); 15 + T_STR(dot, "then"); 16 + T_STR(dot, "else"); 17 + free(dot); 18 + tobi_parse_result_free(&r); 19 + 20 + uint8_t wh[] = {0x14,0x20,'W','L','O','0',0x01,0x70,0x0a,0x03,0x60,0xa2,0x13,0x60,0xa0,0x06,0x68,0x70,0x0a,0x01,0x61,0xa1,0x05,0x70,0x0a,0x02,0x61,0x74,0x60,0x01,0x60,0xa4,0x61}; 21 + T_CHECK(tobi_parse(wh, sizeof(wh), 0, &r)); 22 + T_CHECK(tobi_cf_recover(r.root, &r.diag)); 23 + tobi_ir *while_n = r.root->child[0]->child[0]->child[1]; 24 + T_CHECK(while_n->kind == TOBI_IR_WHILE); 25 + T_CHECK(while_n->child[1]->child[0]->kind == TOBI_IR_IF); 26 + tobi_parse_result_free(&r); 27 + 28 + uint8_t amb[] = {0xa0,0x03,0x01,0xa1}; 29 + T_CHECK(tobi_parse(amb, sizeof(amb), 0, &r)); 30 + T_CHECK(r.root->child_len == 1); 31 + T_CHECK(r.root->child[0]->kind == TOBI_IR_IF); 32 + T_CHECK(tobi_ir_find_child(r.root->child[0]->child[1], TOBI_IR_UNKNOWN) != NULL); 33 + T_CHECK(tobi_cf_recover(r.root, &r.diag)); 34 + T_CHECK(tobi_ir_find_child(r.root->child[0]->child[1], TOBI_IR_DIAG) != NULL); 35 + tobi_parse_result_free(&r); 36 + return 0; 37 + }
+115
test/t_cli.c
··· 1 + #include "t.h" 2 + 3 + #include <sys/wait.h> 4 + 5 + #ifndef TOBI_BIN 6 + #define TOBI_BIN "./tobi" 7 + #endif 8 + #ifndef TOBI_SRC_DIR 9 + #define TOBI_SRC_DIR "." 10 + #endif 11 + 12 + static int write_blob(const char *path, const unsigned char *data, size_t len) { 13 + FILE *fp = fopen(path, "wb"); 14 + if (!fp) { 15 + return 0; 16 + } 17 + int ok = fwrite(data, 1, len, fp) == len; 18 + return fclose(fp) == 0 && ok; 19 + } 20 + 21 + static char *run_capture(const char *args, int *status) { 22 + char cmd[1024]; 23 + snprintf(cmd, sizeof(cmd), "%s %s 2>/dev/null", TOBI_BIN, args); 24 + FILE *fp = popen(cmd, "r"); 25 + if (!fp) { 26 + *status = 99; 27 + return NULL; 28 + } 29 + size_t cap = 4096; 30 + size_t len = 0; 31 + char *buf = malloc(cap); 32 + if (!buf) { 33 + (void)pclose(fp); 34 + *status = 99; 35 + return NULL; 36 + } 37 + int c = 0; 38 + while ((c = fgetc(fp)) != EOF) { 39 + if (len + 1 == cap) { 40 + cap *= 2; 41 + char *nb = realloc(buf, cap); 42 + if (!nb) { 43 + free(buf); 44 + (void)pclose(fp); 45 + *status = 99; 46 + return NULL; 47 + } 48 + buf = nb; 49 + } 50 + buf[len++] = (char)c; 51 + } 52 + buf[len] = '\0'; 53 + int rc = pclose(fp); 54 + *status = WIFEXITED(rc) ? WEXITSTATUS(rc) : 99; 55 + return buf; 56 + } 57 + 58 + int t_cli(void) { 59 + int st = 0; 60 + char path[512]; 61 + snprintf(path, sizeof(path), "%s/sam/m0.aml", TOBI_SRC_DIR); 62 + char args[700]; 63 + snprintf(args, sizeof(args), "%s", path); 64 + char *s = run_capture(args, &st); 65 + T_CHECK(st == 0); 66 + T_STR(s, "method MTH0"); 67 + T_STR(s, "return Local0"); 68 + free(s); 69 + snprintf(args, sizeof(args), "--dis %s", path); 70 + s = run_capture(args, &st); 71 + T_CHECK(st == 0); 72 + T_STR(s, "0x0000"); 73 + T_STR(s, "MethodOp"); 74 + free(s); 75 + snprintf(args, sizeof(args), "--json %s", path); 76 + s = run_capture(args, &st); 77 + T_CHECK(st == 0); 78 + T_STR(s, "\"ir\""); 79 + T_STR(s, "\"kind\":\"method\""); 80 + free(s); 81 + snprintf(args, sizeof(args), "--dot %s", path); 82 + s = run_capture(args, &st); 83 + T_CHECK(st == 0); 84 + T_STR(s, "digraph tobi_cfg"); 85 + T_STR(s, "method"); 86 + free(s); 87 + { 88 + unsigned char use_file[] = {0x14,0x0d,'U','S','E','0',0x00,0xa4,'C','A','L','0',0x0a,0x05}; 89 + unsigned char cal_file[] = {0x14,0x08,'C','A','L','0',0x01,0xa4,0x68}; 90 + const char *use_path = "/tmp/tobi_cli_use.aml"; 91 + const char *cal_path = "/tmp/tobi_cli_cal.aml"; 92 + T_CHECK(write_blob(use_path, use_file, sizeof(use_file))); 93 + T_CHECK(write_blob(cal_path, cal_file, sizeof(cal_file))); 94 + snprintf(args, sizeof(args), "%s %s", use_path, cal_path); 95 + s = run_capture(args, &st); 96 + T_CHECK(st == 0); 97 + T_STR(s, "method USE0"); 98 + T_STR(s, "CAL0(0x5)"); 99 + free(s); 100 + } 101 + s = run_capture("--help", &st); 102 + T_CHECK(st == 0); 103 + T_STR(s, "usage: tobi"); 104 + free(s); 105 + s = run_capture("--version", &st); 106 + T_CHECK(st == 0); 107 + T_STR(s, "tobi"); 108 + free(s); 109 + snprintf(path, sizeof(path), "%s/sam/bad0.aml", TOBI_SRC_DIR); 110 + snprintf(args, sizeof(args), "--strict %s", path); 111 + s = run_capture(args, &st); 112 + T_CHECK(st != 0); 113 + free(s); 114 + return 0; 115 + }
+24
test/t_da.c
··· 1 + #include "t.h" 2 + 3 + #include "da.h" 4 + #include "p.h" 5 + 6 + int t_da(void) { 7 + uint8_t m0[] = {0x14,0x0c,'M','T','H','0',0x01,0x70,0x0a,0x2a,0x60,0xa4,0x60}; 8 + tobi_parse_result r; 9 + T_CHECK(tobi_parse(m0, sizeof(m0), 0, &r)); 10 + char *s = tobi_da_emit(m0, sizeof(m0), &r.meta); 11 + T_STR(s, "0x0000: MethodOp"); 12 + T_STR(s, "operands=PkgLength NameString MethodFlags TermList"); 13 + T_STR(s, "pkglen=12"); 14 + T_STR(s, "StoreOp"); 15 + free(s); 16 + tobi_parse_result_free(&r); 17 + uint8_t bad[] = {0xfe,0x14,0xff}; 18 + T_CHECK(tobi_parse(bad, sizeof(bad), 0, &r)); 19 + s = tobi_da_emit(bad, sizeof(bad), &r.meta); 20 + T_STR(s, "UnknownOp"); 21 + free(s); 22 + tobi_parse_result_free(&r); 23 + return 0; 24 + }
+28
test/t_dc.c
··· 1 + #include "t.h" 2 + 3 + #include "cf.h" 4 + #include "dc.h" 5 + #include "p.h" 6 + 7 + int t_dc(void) { 8 + uint8_t m2[] = {0x14,0x20,'W','L','O','0',0x01,0x70,0x0a,0x03,0x60,0xa2,0x13,0x60,0xa0,0x06,0x68,0x70,0x0a,0x01,0x61,0xa1,0x05,0x70,0x0a,0x02,0x61,0x74,0x60,0x01,0x60,0xa4,0x61}; 9 + tobi_parse_result r; 10 + T_CHECK(tobi_parse(m2, sizeof(m2), 0, &r)); 11 + (void)tobi_cf_recover(r.root, &r.diag); 12 + char *s = tobi_dc_emit(r.root, &r.diag); 13 + T_STR(s, "method WLO0(args=1"); 14 + T_STR(s, "Local0 = 0x3"); 15 + T_STR(s, "while (Local0)"); 16 + T_STR(s, "if (Arg0)"); 17 + T_STR(s, " while"); 18 + T_STR(s, "return Local1"); 19 + free(s); 20 + tobi_parse_result_free(&r); 21 + uint8_t bad[] = {0xfe}; 22 + T_CHECK(tobi_parse(bad, sizeof(bad), 0, &r)); 23 + s = tobi_dc_emit(r.root, &r.diag); 24 + T_STR(s, "unknown opcode 0xfe at 0x0"); 25 + free(s); 26 + tobi_parse_result_free(&r); 27 + return 0; 28 + }
+22
test/t_ir.c
··· 1 + #include "t.h" 2 + 3 + #include "ir.h" 4 + 5 + int t_ir(void) { 6 + tobi_ir *root = tobi_ir_new(TOBI_IR_ROOT, 0, 10); 7 + tobi_ir *m = tobi_ir_new(TOBI_IR_METHOD, 2, 8); 8 + tobi_ir_set_name(m, "MTH0"); 9 + tobi_ir_add(root, m); 10 + tobi_ir_add(m, tobi_ir_new(TOBI_IR_BLOCK, 7, 3)); 11 + T_CHECK(root->kind == TOBI_IR_ROOT); 12 + T_CHECK(root->child_len == 1); 13 + T_CHECK(root->child[0]->off == 2 && root->child[0]->len == 8); 14 + T_CHECK(tobi_ir_find_child(root, TOBI_IR_METHOD) == m); 15 + T_CHECK(tobi_ir_count_kind(root, TOBI_IR_METHOD) == 1); 16 + tobi_ir *u = tobi_ir_new(TOBI_IR_UNKNOWN, 4, 1); 17 + u->raw_op = 0xfe; 18 + tobi_ir_add(m->child[0], u); 19 + T_CHECK(m->child[0]->child[0]->raw_op == 0xfe); 20 + tobi_ir_free(root); 21 + return 0; 22 + }
+19
test/t_js.c
··· 1 + #include "t.h" 2 + 3 + #include "js.h" 4 + #include "p.h" 5 + 6 + int t_js(void) { 7 + uint8_t data[] = {0x08,'S','T','R','0',0x0d,'a','"','b','\\','c',0x00,0xfe}; 8 + tobi_parse_result r; 9 + T_CHECK(tobi_parse(data, sizeof(data), 0, &r)); 10 + char *s = tobi_js_emit(&r); 11 + T_STR(s, "\"kind\":\"root\""); 12 + T_STR(s, "a\\\"b\\\\c"); 13 + T_STR(s, "\"raw_opcode\":254"); 14 + T_CHECK(strstr(s, ",}") == NULL); 15 + T_CHECK(strstr(s, ",]") == NULL); 16 + free(s); 17 + tobi_parse_result_free(&r); 18 + return 0; 19 + }
+44
test/t_nm.c
··· 1 + #include "t.h" 2 + 3 + #include "nm.h" 4 + 5 + static char *parse(uint8_t *b, size_t n) { 6 + tobi_rd r; 7 + tobi_rd_init(&r, b, n, 0); 8 + char *s = NULL; 9 + if (!tobi_nm_namestring(&r, &s)) { 10 + return NULL; 11 + } 12 + return s; 13 + } 14 + 15 + int t_nm(void) { 16 + uint8_t simple[] = {'A','B','C','0'}; 17 + uint8_t root[] = {'\\','_','S','B','_'}; 18 + uint8_t parent[] = {'^','^','P','C','I','0'}; 19 + uint8_t dual[] = {0x2e,'A','A','A','0','B','B','B','0'}; 20 + uint8_t multi[] = {0x2f,3,'A','A','A','0','B','B','B','0','C','C','C','0'}; 21 + char *s = parse(simple, sizeof(simple)); 22 + T_CHECK(s && strcmp(s, "ABC0") == 0); 23 + free(s); 24 + s = parse(root, sizeof(root)); 25 + T_CHECK(s && strcmp(s, "\\_SB_") == 0); 26 + free(s); 27 + s = parse(parent, sizeof(parent)); 28 + T_CHECK(s && strcmp(s, "^^PCI0") == 0); 29 + free(s); 30 + s = parse(dual, sizeof(dual)); 31 + T_CHECK(s && strcmp(s, "AAA0.BBB0") == 0); 32 + free(s); 33 + s = parse(multi, sizeof(multi)); 34 + T_CHECK(s && strcmp(s, "AAA0.BBB0.CCC0") == 0); 35 + free(s); 36 + uint8_t bad[] = {'1','B','C','D'}; 37 + T_CHECK(parse(bad, sizeof(bad)) == NULL); 38 + uint8_t trunc[] = {0x2f,2,'A','A','A','0','B'}; 39 + T_CHECK(parse(trunc, sizeof(trunc)) == NULL); 40 + s = tobi_nm_resolve("\\_SB_.PCI0", "^DEV0"); 41 + T_CHECK(strcmp(s, "\\_SB_.DEV0") == 0); 42 + free(s); 43 + return 0; 44 + }
+172
test/t_p.c
··· 1 + #include "t.h" 2 + 3 + #include "p.h" 4 + 5 + static int parse_ok(const uint8_t *b, size_t n, tobi_parse_result *r) { 6 + return tobi_parse(b, n, 0, r) && r->root != NULL; 7 + } 8 + 9 + static void fix_checksum(uint8_t *b, size_t n) { 10 + unsigned sum = 0; 11 + b[9] = 0; 12 + for (size_t i = 0; i < n; i++) { 13 + sum = (sum + b[i]) & 0xffu; 14 + } 15 + b[9] = (uint8_t)((256u - sum) & 0xffu); 16 + } 17 + 18 + int t_p(void) { 19 + uint8_t m0[] = {0x14,0x0c,'M','T','H','0',0x01,0x70,0x0a,0x2a,0x60,0xa4,0x60}; 20 + tobi_parse_result r; 21 + T_CHECK(parse_ok(m0, sizeof(m0), &r)); 22 + T_CHECK(r.root->child_len == 1); 23 + tobi_ir *m = r.root->child[0]; 24 + T_CHECK(m->kind == TOBI_IR_METHOD && m->method_args == 1); 25 + T_CHECK(m->child_len == 1 && m->child[0]->child_len == 2); 26 + T_CHECK(m->child[0]->child[0]->kind == TOBI_IR_STORE); 27 + T_CHECK(m->child[0]->child[1]->kind == TOBI_IR_RETURN); 28 + tobi_parse_result_free(&r); 29 + 30 + uint8_t table[36 + sizeof(m0)]; 31 + memset(table, 0, sizeof(table)); 32 + memcpy(table, "DSDT", 4); 33 + uint32_t table_len = (uint32_t)sizeof(table); 34 + table[4] = (uint8_t)(table_len & 0xffu); 35 + table[5] = (uint8_t)((table_len >> 8) & 0xffu); 36 + table[6] = (uint8_t)((table_len >> 16) & 0xffu); 37 + table[7] = (uint8_t)((table_len >> 24) & 0xffu); 38 + memcpy(table + 36, m0, sizeof(m0)); 39 + fix_checksum(table, sizeof(table)); 40 + T_CHECK(parse_ok(table, sizeof(table), &r)); 41 + T_CHECK(r.meta.is_table && r.meta.aml_off == 36 && r.meta.aml_len == sizeof(m0)); 42 + T_CHECK(r.root->child_len == 1 && r.root->child[0]->kind == TOBI_IR_METHOD); 43 + T_CHECK(r.root->child[0]->off == 36); 44 + T_CHECK(r.diag.len == 0); 45 + tobi_parse_result_free(&r); 46 + 47 + uint8_t nested[] = {0x10,0x14,'\\','_','S','B','_',0x5b,0x82,0x0c,'D','E','V','0',0x14,0x06,'M','0','0','0',0x00}; 48 + T_CHECK(parse_ok(nested, sizeof(nested), &r)); 49 + T_CHECK(r.root->child[0]->kind == TOBI_IR_SCOPE); 50 + T_CHECK(r.root->child[0]->child[0]->child[0]->kind == TOBI_IR_DEVICE); 51 + tobi_parse_result_free(&r); 52 + 53 + uint8_t objs[] = {0x08,'B','U','F','0',0x11,0x05,0x0a,0x02,0xaa,0xbb,0x08,'P','K','G','0',0x12,0x06,0x02,0x0a,0x01,0x0a,0x02}; 54 + T_CHECK(parse_ok(objs, sizeof(objs), &r)); 55 + T_CHECK(r.root->child_len == 2); 56 + T_CHECK(r.root->child[0]->child[0]->kind == TOBI_IR_BUFFER); 57 + T_CHECK(r.root->child[1]->child[0]->kind == TOBI_IR_PACKAGE); 58 + tobi_parse_result_free(&r); 59 + 60 + uint8_t crs[] = {0x08,'C','R','S','0',0x11,0x05,0x0a,0x02,0x79,0x00}; 61 + T_CHECK(parse_ok(crs, sizeof(crs), &r)); 62 + T_CHECK(r.root->child_len == 1); 63 + T_CHECK(r.root->child[0]->child[0]->kind == TOBI_IR_BUFFER); 64 + T_STR(r.root->child[0]->child[0]->str, "EndTag"); 65 + tobi_parse_result_free(&r); 66 + 67 + uint8_t fld[] = {0x5b,0x80,'R','E','G','0',0x01,0x0a,0x10,0x0a,0x04,0x5b,0x81,0x0b,'R','E','G','0',0x00,'F','L','D','0',0x08}; 68 + T_CHECK(parse_ok(fld, sizeof(fld), &r)); 69 + T_CHECK(r.root->child_len == 2); 70 + T_CHECK(r.root->child[1]->kind == TOBI_IR_FIELD); 71 + T_CHECK(r.root->child[1]->child_len == 1); 72 + tobi_parse_result_free(&r); 73 + 74 + uint8_t ifld[] = {0x5b,0x86,0x0f,'I','D','X','0','D','A','T','0',0x00,'I','F','L','D',0x08}; 75 + T_CHECK(parse_ok(ifld, sizeof(ifld), &r)); 76 + T_CHECK(r.root->child_len == 1); 77 + T_CHECK(r.root->child[0]->kind == TOBI_IR_FIELD); 78 + T_CHECK(strcmp(r.root->child[0]->str, "IndexField") == 0); 79 + T_CHECK(r.root->child[0]->child_len == 1); 80 + T_CHECK(strcmp(r.root->child[0]->child[0]->name, "IFLD") == 0); 81 + tobi_parse_result_free(&r); 82 + 83 + uint8_t bfld[] = {0x5b,0x87,0x11,'R','E','G','0','B','N','K','0',0x0a,0x01,0x00,'B','F','L','D',0x08}; 84 + T_CHECK(parse_ok(bfld, sizeof(bfld), &r)); 85 + T_CHECK(r.root->child_len == 1); 86 + T_CHECK(r.root->child[0]->kind == TOBI_IR_FIELD); 87 + T_CHECK(strcmp(r.root->child[0]->str, "BankField") == 0); 88 + T_CHECK(r.root->child[0]->child_len == 2); 89 + T_CHECK(r.root->child[0]->child[0]->kind == TOBI_IR_INTEGER); 90 + T_CHECK(r.root->child[0]->child[1]->kind == TOBI_IR_FIELD_ELEM); 91 + tobi_parse_result_free(&r); 92 + 93 + uint8_t call[] = { 94 + 0x14,0x08,'C','A','L','0',0x01,0xa4,0x68, 95 + 0x14,0x0d,'U','S','E','0',0x00,0xa4,'C','A','L','0',0x0a,0x05 96 + }; 97 + T_CHECK(parse_ok(call, sizeof(call), &r)); 98 + T_CHECK(r.root->child_len == 2); 99 + tobi_ir *use = r.root->child[1]; 100 + T_CHECK(use->child_len == 1 && use->child[0]->child_len == 1); 101 + T_CHECK(use->child[0]->child[0]->kind == TOBI_IR_RETURN); 102 + T_CHECK(use->child[0]->child[0]->child_len == 1); 103 + T_CHECK(use->child[0]->child[0]->child[0]->kind == TOBI_IR_CALL); 104 + T_CHECK(use->child[0]->child[0]->child[0]->child_len == 1); 105 + tobi_parse_result_free(&r); 106 + 107 + uint8_t use_file[] = {0x14,0x0d,'U','S','E','0',0x00,0xa4,'C','A','L','0',0x0a,0x05}; 108 + uint8_t cal_file[] = {0x14,0x08,'C','A','L','0',0x01,0xa4,0x68}; 109 + tobi_parse_input inputs[] = { 110 + {use_file, sizeof(use_file), "use.aml"}, 111 + {cal_file, sizeof(cal_file), "cal.aml"} 112 + }; 113 + T_CHECK(tobi_parse_multi(inputs, 2, 0, &r)); 114 + T_CHECK(r.root->child_len == 2); 115 + T_CHECK(strcmp(r.root->child[0]->name, "use.aml") == 0); 116 + use = r.root->child[0]->child[0]; 117 + T_CHECK(use->child[0]->child[0]->kind == TOBI_IR_RETURN); 118 + T_CHECK(use->child[0]->child[0]->child[0]->kind == TOBI_IR_CALL); 119 + T_CHECK(use->child[0]->child[0]->child[0]->child_len == 1); 120 + tobi_parse_result_free(&r); 121 + 122 + uint8_t fwd[] = { 123 + 0x14,0x0d,'U','S','E','0',0x00,0xa4,'C','A','L','0',0x0a,0x05, 124 + 0x14,0x08,'C','A','L','0',0x01,0xa4,0x68 125 + }; 126 + T_CHECK(parse_ok(fwd, sizeof(fwd), &r)); 127 + T_CHECK(r.root->child_len == 2); 128 + use = r.root->child[0]; 129 + T_CHECK(use->child[0]->child[0]->kind == TOBI_IR_RETURN); 130 + T_CHECK(use->child[0]->child[0]->child[0]->kind == TOBI_IR_CALL); 131 + T_CHECK(use->child[0]->child[0]->child[0]->child_len == 1); 132 + tobi_parse_result_free(&r); 133 + 134 + uint8_t ops[] = { 135 + 0x14,0x24,'O','P','S','0',0x00, 136 + 0x5b,0x22,0x0a,0x01, 137 + 0x8c,'B','U','F','0',0x0a,0x00,'B','Y','T','0', 138 + 0x70,0x5b,0x30,0x60, 139 + 0x5b,0x32,0x0a,0x01,0x0c,0x34,0x12,0x00,0x00,0x0a,0x02 140 + }; 141 + T_CHECK(parse_ok(ops, sizeof(ops), &r)); 142 + T_CHECK(!tobi_diag_has_error(&r.diag)); 143 + T_CHECK(r.diag.len == 0); 144 + tobi_ir *blk = r.root->child[0]->child[0]; 145 + T_CHECK(blk->child_len == 4); 146 + T_CHECK(blk->child[0]->kind == TOBI_IR_EXPR); 147 + T_CHECK(strcmp(blk->child[0]->name, "sleep") == 0); 148 + T_CHECK(blk->child[1]->kind == TOBI_IR_EXPR); 149 + T_CHECK(strcmp(blk->child[1]->name, "create_byte_field") == 0); 150 + T_CHECK(blk->child[2]->kind == TOBI_IR_STORE); 151 + T_CHECK(blk->child[2]->child[0]->kind == TOBI_IR_INTEGER); 152 + T_CHECK(blk->child[3]->kind == TOBI_IR_EXPR); 153 + T_CHECK(strcmp(blk->child[3]->name, "fatal") == 0); 154 + tobi_parse_result_free(&r); 155 + 156 + uint8_t ext_obj[] = { 157 + 0x5b,0x88,'D','R','G','0',0x0d,'S','I','G',0x00,0x0d,'O','E','M',0x00,0x0d,'D','A','T',0x00, 158 + 0x5b,0x23,'M','T','X','0',0x0b,0xff,0xff, 159 + 0x5b,0x31 160 + }; 161 + T_CHECK(parse_ok(ext_obj, sizeof(ext_obj), &r)); 162 + T_CHECK(r.diag.len == 0); 163 + T_CHECK(r.root->child_len == 3); 164 + T_CHECK(r.root->child[0]->kind == TOBI_IR_OPREGION); 165 + T_CHECK(strcmp(r.root->child[0]->str, "DataRegion") == 0); 166 + T_CHECK(r.root->child[1]->kind == TOBI_IR_EXPR); 167 + T_CHECK(strcmp(r.root->child[1]->name, "acquire") == 0); 168 + T_CHECK(r.root->child[2]->kind == TOBI_IR_REF); 169 + T_CHECK(strcmp(r.root->child[2]->name, "Debug") == 0); 170 + tobi_parse_result_free(&r); 171 + return 0; 172 + }
+35
test/t_rd.c
··· 1 + #include "t.h" 2 + 3 + #include "rd.h" 4 + 5 + int t_rd(void) { 6 + uint8_t one[] = {0x3f}; 7 + uint8_t two[] = {0x41, 0x12}; 8 + uint8_t three[] = {0x82, 0x34, 0x12}; 9 + uint8_t four[] = {0xc3, 0x56, 0x34, 0x12}; 10 + tobi_rd r; 11 + size_t len = 0; 12 + size_t enc = 0; 13 + tobi_rd_init(&r, one, sizeof(one), 0); 14 + T_CHECK(tobi_rd_pkg_len(&r, &len, &enc) && len == 0x3f && enc == 1); 15 + tobi_rd_init(&r, two, sizeof(two), 0); 16 + T_CHECK(tobi_rd_pkg_len(&r, &len, &enc) && len == 0x121 && enc == 2); 17 + tobi_rd_init(&r, three, sizeof(three), 0); 18 + T_CHECK(tobi_rd_pkg_len(&r, &len, &enc) && len == 0x12342 && enc == 3); 19 + tobi_rd_init(&r, four, sizeof(four), 0); 20 + T_CHECK(tobi_rd_pkg_len(&r, &len, &enc) && len == 0x1234563 && enc == 4); 21 + uint8_t trunc[] = {0x80}; 22 + tobi_rd_init(&r, trunc, sizeof(trunc), 0); 23 + T_CHECK(!tobi_rd_pkg_len(&r, &len, &enc)); 24 + T_CHECK(r.pos == 0); 25 + uint8_t b[] = {1, 2, 3}; 26 + uint32_t v = 0; 27 + tobi_rd_init(&r, b, sizeof(b), 0); 28 + T_CHECK(!tobi_rd_u32(&r, &v)); 29 + T_CHECK(r.pos == 0); 30 + uint8_t x = 0; 31 + T_CHECK(tobi_rd_u8(&r, &x) && x == 1); 32 + T_CHECK(!tobi_rd_skip(&r, 99)); 33 + T_CHECK(r.pos == 1); 34 + return 0; 35 + }