From f8c1a52259b40fb9423212452dd511433c180a9d Mon Sep 17 00:00:00 2001 From: Max Wash Date: Wed, 19 Jul 2023 19:00:27 +0100 Subject: [PATCH] tools: add tool to decode AML files and build an ACPI namespace --- tools/Makefile | 2 +- tools/amldecode/Makefile | 8 + tools/amldecode/acpi-dsdt.aml | Bin 0 -> 3640 bytes tools/amldecode/acpi-dsdt.asl | 598 +++++++++++++++++++++++ tools/amldecode/aml/constants.h | 6 + tools/amldecode/aml/object.c | 136 ++++++ tools/amldecode/aml/object.h | 52 ++ tools/amldecode/aml/opcode.c | 818 ++++++++++++++++++++++++++++++++ tools/amldecode/aml/opcode.h | 190 ++++++++ tools/amldecode/aml/parser.c | 271 +++++++++++ tools/amldecode/aml/parser.h | 48 ++ tools/amldecode/aml/value.c | 17 + tools/amldecode/aml/value.h | 43 ++ tools/amldecode/main.c | 154 ++++++ tools/amldecode/table.c | 72 +++ tools/amldecode/table.h | 32 ++ 16 files changed, 2446 insertions(+), 1 deletion(-) create mode 100644 tools/amldecode/Makefile create mode 100644 tools/amldecode/acpi-dsdt.aml create mode 100644 tools/amldecode/acpi-dsdt.asl create mode 100644 tools/amldecode/aml/constants.h create mode 100644 tools/amldecode/aml/object.c create mode 100644 tools/amldecode/aml/object.h create mode 100644 tools/amldecode/aml/opcode.c create mode 100644 tools/amldecode/aml/opcode.h create mode 100644 tools/amldecode/aml/parser.c create mode 100644 tools/amldecode/aml/parser.h create mode 100644 tools/amldecode/aml/value.c create mode 100644 tools/amldecode/aml/value.h create mode 100644 tools/amldecode/main.c create mode 100644 tools/amldecode/table.c create mode 100644 tools/amldecode/table.h diff --git a/tools/Makefile b/tools/Makefile index 7598ba4..b1ce472 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -1,4 +1,4 @@ -TOOL_LIST := e64patch +TOOL_LIST := e64patch amldecode all: @for prog in $(TOOL_LIST); do \ diff --git a/tools/amldecode/Makefile b/tools/amldecode/Makefile new file mode 100644 index 0000000..0d1e36a --- /dev/null +++ b/tools/amldecode/Makefile @@ -0,0 +1,8 @@ +TOOL_NAME := amldecode + +include ../tool-config.mk + +CFLAGS := -g +LDFLAGS := -g + +include ../tool-template.mk diff --git a/tools/amldecode/acpi-dsdt.aml b/tools/amldecode/acpi-dsdt.aml new file mode 100644 index 0000000000000000000000000000000000000000..ef6350f6e912faf518cb53b24ede519e2c5e3337 GIT binary patch literal 3640 zcmbW3&2HO95XXlflBgABOEPWC->#fPo3^PXr9e+@MA24Mf2pEe7*JrcKn^_>LJAZy zf&j8EEfAoWqIU=I1N05rNARKZ3J4z`%9SJ(+mj%o89%CHwb(;=nl4Cx9ZJJ zD|p)U?AnDetf1z#!3qwWo@NEDcBioObH9)=Goumg?(cXI^)>9*pkUK=p`&nPVW6l( zA#2*B5!#93#)`5qa4Nbd^Ds1)l!@cZSY2jf>{wA|9IAuN#88=uk}}baj5TCt?7#rZ zj6;nm6GLStO3I99F=b}#z?7MBs2OF(=25Dg3C5X_nQ>?$%8bpUR5_E3GbuCU&?M(X zsdA+G0q(4 zM5%J-8E2kx<~b)ym9xM&3yibCIZ>*dGmLYFan5i~lq#pgI3336a88se=Pcu#Wt_8| z6Q#;I$2jL0=N#umsdCOU&UwZ;&pA=5oJGc2WSm9LiBjcUV4Mq#bAfZBR5=$J=OW`= zoGXlTg>#}*Iae9yD&t(`oG4Y!HO9Hd zIM+BQ%81hn>TN&oro>ObWkJX95&lNhXbo*>4{goD>za*67QiLm9@-O~#q$>6-(PVX zMfU*&j7Io1ls_Hh9Xjvn_s?kb_xo}8>l46Z-F({G>cq#ohQ~S{=-{LSzf*&AF&#Nu z6U#JAc0A0*`;l=G4TERl3FP)ZeMD6PRS4a|!|xllK$ZvSR0qcj{IUnmtN5Wbk>rQ^$d#iX(%y1}#!i%r>+h^dP5~Xn9`13(%8<;+Z?H zH;FflD+uQkN(HO4*9xKmLI^>_?C0nSy_sJU#fJ9IcxQmw&@PQjEILY0;Gs#gQ$TmHlh|!_CexeX zd@LVC@A&xmQR}E&_1b4#^@KSEr^weOsevrSEdny1t>srhz2cOW-!_w>OR2JK+l zk+ci7R{Oh63!5@E6>6$hcd#$0h>Q_!d_&?A*w3d$a$~q!JOO7hdTt^Uo@HN&J`MFl zPo%^1(8&j0(}&k|DZ1f&6Fs|DyS*ug&;f^dLsFwn*`v_#%>FYJ{Sz&uOK(#>p#O6U za+U(W?Q3s-l+gpa9{v*cLQ3ln4#=;2&9Bg@X8*9^@=e2KX}EkB!{z%KE{_e@uN%fM z(`|+;Hw{;$;mTbMSMF!HGB)g8H*DT+xO&rYRT{3|#c=h0hVjkut3e@YYWOwB{$uLX Kk%0X# +#include +#include +#include "object.h" + +struct acpi_namespace *acpi_namespace_create(void) +{ + struct acpi_namespace *ns = malloc(sizeof *ns); + if (!ns) { + return NULL; + } + + memset(ns, 0x00, sizeof *ns); + + ns->root = acpi_object_create("\\", ACPI_OBJECT_NAMESPACE); + return ns; +} + +struct acpi_object *acpi_namespace_get_object(struct acpi_namespace *ns, const char *path) +{ + return NULL; +} + +struct acpi_object *acpi_object_create(const char name[ACPI_OBJECT_NAME_MAX], enum acpi_object_type type) +{ + struct acpi_object *object = malloc(sizeof *object); + if (!object) { + return NULL; + } + + memset(object, 0x00, sizeof *object); + + object->ref = 1; + object->type = type; + + if (name) { + strncpy(object->name, name, sizeof object->name - 1); + object->name[ACPI_OBJECT_NAME_MAX - 1] = '\0'; + } + + return object; +} + +struct acpi_object *acpi_object_ref(struct acpi_object *object) +{ + object->ref++; + return object; +} + +void acpi_object_deref(struct acpi_object *object) +{ + if (object->ref > 1) { + object->ref--; + return; + } + + object->ref = 0; + free(object); +} + +void acpi_object_add_child(struct acpi_object *parent, struct acpi_object *child) +{ + if (child->parent) { + return; + } + + child->parent = parent; + if (!parent->first_child) { + parent->first_child = acpi_object_ref(child); + return; + } + + struct acpi_object *cur = parent->first_child; + while (cur->next_sibling) { + cur = cur->next_sibling; + } + + cur->next_sibling = acpi_object_ref(child); +} + +struct acpi_object *acpi_object_get_child(struct acpi_object *object, const char *name) +{ + struct acpi_object *cur = object->first_child; + while (cur) { + if (!strcmp(cur->name, name)) { + return acpi_object_ref(cur); + } + + cur = cur->next_sibling; + } + + return NULL; +} + +void acpi_object_print(struct acpi_object *object, int depth) +{ + for (int i = 0; i < depth; i++) { + fputs(" ", stdout); + } + + if (object->name[0] == 0) { + printf("\n"); + } else { + printf("%s [%s]\n", object->name, acpi_object_type_string(object->type)); + } + + struct acpi_object *cur = object->first_child; + while (cur) { + acpi_object_print(cur, depth + 1); + cur = cur->next_sibling; + } +} + +#define OBJECT_TYPE_STRING(type) \ + case type: \ + return #type; + +const char *acpi_object_type_string(enum acpi_object_type type) +{ + switch (type) { + OBJECT_TYPE_STRING(ACPI_OBJECT_NONE) + OBJECT_TYPE_STRING(ACPI_OBJECT_VALUE) + OBJECT_TYPE_STRING(ACPI_OBJECT_NAMESPACE) + OBJECT_TYPE_STRING(ACPI_OBJECT_CPU) + OBJECT_TYPE_STRING(ACPI_OBJECT_DEVICE) + OBJECT_TYPE_STRING(ACPI_OBJECT_METHOD) + OBJECT_TYPE_STRING(ACPI_OBJECT_POWER_RESOURCE) + OBJECT_TYPE_STRING(ACPI_OBJECT_OPERATION_REGION) + OBJECT_TYPE_STRING(ACPI_OBJECT_THERMAL_ZONE) + OBJECT_TYPE_STRING(ACPI_OBJECT_FIELD) + OBJECT_TYPE_STRING(ACPI_OBJECT_PACKAGE) + OBJECT_TYPE_STRING(ACPI_OBJECT_BUFFER) + default: + return ""; + } +} diff --git a/tools/amldecode/aml/object.h b/tools/amldecode/aml/object.h new file mode 100644 index 0000000..62c312a --- /dev/null +++ b/tools/amldecode/aml/object.h @@ -0,0 +1,52 @@ +#ifndef AML_OBJECT_H_ +#define AML_OBJECT_H_ + +#include +#include "constants.h" + +enum acpi_object_type { + ACPI_OBJECT_NONE = 0, + ACPI_OBJECT_VALUE, + ACPI_OBJECT_NAMESPACE, + ACPI_OBJECT_CPU, + ACPI_OBJECT_DEVICE, + ACPI_OBJECT_METHOD, + ACPI_OBJECT_POWER_RESOURCE, + ACPI_OBJECT_OPERATION_REGION, + ACPI_OBJECT_THERMAL_ZONE, + ACPI_OBJECT_FIELD, + ACPI_OBJECT_PACKAGE, + ACPI_OBJECT_BUFFER, +}; + +struct acpi_object { + int ref; + enum acpi_object_type type; + union { + char name[ACPI_OBJECT_NAME_MAX]; + char *publish_path; + }; + + size_t scope_end; + + struct acpi_object *parent; + struct acpi_object *first_child, *next_sibling; +}; + +struct acpi_namespace { + struct acpi_object *root; +}; + +extern struct acpi_namespace *acpi_namespace_create(void); +extern struct acpi_object *acpi_namespace_get_object(struct acpi_namespace *ns, const char *path); + +extern struct acpi_object *acpi_object_create(const char name[ACPI_OBJECT_NAME_MAX], enum acpi_object_type type); +extern struct acpi_object *acpi_object_ref(struct acpi_object *object); +extern void acpi_object_deref(struct acpi_object *object); +extern void acpi_object_add_child(struct acpi_object *parent, struct acpi_object *child); +extern struct acpi_object *acpi_object_get_child(struct acpi_object *object, const char *name); +extern void acpi_object_print(struct acpi_object *object, int depth); + +extern const char *acpi_object_type_string(enum acpi_object_type type); + +#endif diff --git a/tools/amldecode/aml/opcode.c b/tools/amldecode/aml/opcode.c new file mode 100644 index 0000000..54c6e1b --- /dev/null +++ b/tools/amldecode/aml/opcode.c @@ -0,0 +1,818 @@ +#include +#include +#include +#include +#include +#include "opcode.h" +#include "value.h" +#include "object.h" +#include "parser.h" + +#define BYTECODE(opcode, parser, name) \ + { opcode, 0, parser, name } + +#define EXT_BYTECODE(opcode, parser, name) \ + { AML_EXT_OP, opcode, parser, name } + +static int32_t read_pkg_length(struct aml_parser *parser) +{ + int32_t p0 = aml_parser_advance(parser); + if (p0 == PARSE_EOF) { + return PARSE_EOF; + } + + int32_t len = 0; + int32_t tmp = 0; + + int nbytes = (p0 & 0xC0) >> 6; + switch (nbytes) { + case 3: + tmp = aml_parser_advance(parser); + if (tmp == PARSE_EOF) { + return PARSE_EOF; + } + + len |= tmp << 20; + case 2: + tmp = aml_parser_advance(parser); + if (tmp == PARSE_EOF) { + return PARSE_EOF; + } + + len |= tmp << 12; + case 1: + len |= p0 & 0x0F; + tmp = aml_parser_advance(parser); + if (tmp == PARSE_EOF) { + return PARSE_EOF; + } + + len |= tmp << 4; + break; + case 0: + len = p0 & 0x3F; + break; + default: + return -1; + } + + return len; +} + +static enum parse_status read_single_segment_name(struct aml_parser *parser, char name[ACPI_OBJECT_NAME_MAX]) +{ + name[0] = 0; + + for (int i = 0; i < 4; i++) { + int c = aml_parser_advance(parser); + if (c < 0) { + return c; + } + + if (c == 0) { + break; + } + + if (c == AML_ROOT_CHAR) { + c = '\\'; + } + + if (!isalnum(c) && c != '_' && c != '\\') { + return PARSE_BADSTRING; + } + + name[i] = c; + name[i + 1] = '\0'; + } + + return PARSE_OK; +} + +static enum parse_status read_name(struct aml_parser *parser, char **out) +{ + int prefix = aml_parser_peek(parser); + if (prefix < 0) { + return PARSE_EOF; + } + + int nsegments = 1; + int root = 0; + + if (prefix == AML_ROOT_CHAR) { + root = 1; + aml_parser_advance(parser); + prefix = aml_parser_peek(parser); + } + + if (prefix == AML_DUAL_NAME_PREFIX) { + aml_parser_advance(parser); + nsegments = 2; + } else if (prefix == AML_MULTI_NAME_PREFIX) { + aml_parser_advance(parser); + nsegments = aml_parser_advance(parser); + } + + /* each segment is 4 characters long, with a dot separator between each segment */ + size_t name_len = nsegments * 4 + (nsegments - 1) + 1; + char *name = malloc(name_len + 1); + if (!name) { + return PARSE_NOMEM; + } + + size_t offset = 0; + if (root) { + name[offset++] = '\\'; + name[offset] = '\0'; + } + + for (int seg = 0; seg < nsegments; seg++) { + for (int i = 0; i < 4; i++) { + int c = aml_parser_advance(parser); + if (c < 0) { + return c; + } + + if (c == 0) { + break; + } + + if (c == AML_ROOT_CHAR) { + c = '\\'; + } + + if (!isalnum(c) && c != '_' && c != '\\') { + return PARSE_BADSTRING; + } + + name[offset++] = c; + name[offset] = '\0'; + } + + if (seg < nsegments - 1) { + name[offset++] = '.'; + name[offset] = '\0'; + } + } + + *out = name; + return PARSE_OK; +} + +static enum parse_status parse_zero(struct aml_parser *parser, struct aml_value *out) +{ + int opcode = aml_parser_advance(parser); + out->type = AML_VALUE_UINT8; + out->value.uint8 = 0; + + return PARSE_OK; +} + +static enum parse_status parse_one(struct aml_parser *parser, struct aml_value *out) +{ + int opcode = aml_parser_advance(parser); + out->type = AML_VALUE_UINT8; + out->value.uint8 = 1; + + return PARSE_OK; +} + +static enum parse_status parse_byte_prefix(struct aml_parser *parser, struct aml_value *out) +{ + int opcode = aml_parser_advance(parser); + int a = aml_parser_advance(parser); + if (a == PARSE_EOF) { + return PARSE_EOF; + } + + out->type = AML_VALUE_UINT8; + out->value.uint8 = a; + + return PARSE_OK; +} + +static enum parse_status parse_word_prefix(struct aml_parser *parser, struct aml_value *out) +{ + int opcode = aml_parser_advance(parser); + + int a = aml_parser_advance(parser); + int b = aml_parser_advance(parser); + + if (a == PARSE_EOF || b == PARSE_EOF) { + return PARSE_EOF; + } + + out->type = AML_VALUE_UINT16; + out->value.uint16 = a | (b << 8); + + return PARSE_OK; +} + +static enum parse_status parse_dword_prefix(struct aml_parser *parser, struct aml_value *out) +{ + int opcode = aml_parser_advance(parser); + + int a = aml_parser_advance(parser); + int b = aml_parser_advance(parser); + int c = aml_parser_advance(parser); + int d = aml_parser_advance(parser); + + if (a == PARSE_EOF || b == PARSE_EOF || c == PARSE_EOF || d == PARSE_EOF) { + return PARSE_EOF; + } + + out->type = AML_VALUE_UINT32; + out->value.uint16 = a | (b << 8) | (c << 16) | (d << 24); + + return PARSE_OK; +} + +static enum parse_status parse_region(struct aml_parser *parser, struct aml_value *out) +{ + return PARSE_OK; +} + +static enum parse_status parse_qword_prefix(struct aml_parser *parser, struct aml_value *out) +{ + int opcode = aml_parser_advance(parser); + + int64_t a = aml_parser_advance(parser); + int64_t b = aml_parser_advance(parser); + int64_t c = aml_parser_advance(parser); + int64_t d = aml_parser_advance(parser); + int64_t e = aml_parser_advance(parser); + int64_t f = aml_parser_advance(parser); + int64_t g = aml_parser_advance(parser); + int64_t h = aml_parser_advance(parser); + + if (a == PARSE_EOF || b == PARSE_EOF || c == PARSE_EOF || d == PARSE_EOF) { + return PARSE_EOF; + } + + if (e == PARSE_EOF || f == PARSE_EOF || g == PARSE_EOF || h == PARSE_EOF) { + return PARSE_EOF; + } + + out->type = AML_VALUE_UINT64; + out->value.uint16 = a | (b << 8) | (c << 16) | (d << 24) | (e << 32) | (f << 40) | (g << 48) | (h << 56); + + return PARSE_OK; +} + +static enum parse_status parse_string_prefix(struct aml_parser *parser, struct aml_value *out) +{ + int opcode = aml_parser_advance(parser); + + aml_parser_save_cursor(parser); + unsigned int len = 0; + while (1) { + int c = aml_parser_advance(parser); + if (c == 0) { + break; + } + + if (c == PARSE_EOF) { + return PARSE_EOF; + } + + len++; + } + + aml_parser_load_cursor(parser); + char *buf = malloc(len + 1); + if (!buf) { + return PARSE_NOMEM; + } + + for (unsigned int i = 0; i < len; i++) { + int c = aml_parser_advance(parser); + + if (c == 0) { + break; + } + + if (c == PARSE_EOF) { + return PARSE_EOF; + } + + buf[i] = c; + } + + buf[len - 1] = '\0'; + + out->type = AML_VALUE_STRING; + out->value.str = buf; + + return PARSE_OK; +} + +static enum parse_status parse_scope(struct aml_parser *parser, struct aml_value *out) +{ + int opcode = aml_parser_advance(parser); + unsigned int scope_start = aml_parser_cursorpos(parser); + int32_t scope_size = read_pkg_length(parser); + if (scope_size < 0) { + return scope_size; + } + + unsigned int scope_end = scope_start + scope_size; + + char *scope_name; + enum parse_status status = read_name(parser, &scope_name); + if (status != PARSE_OK) { + return status; + } + + struct acpi_object *scope = aml_parser_resolve_path(parser, scope_name); + + if (!scope) { + scope = acpi_object_create(NULL, ACPI_OBJECT_NAMESPACE); + scope->publish_path = scope_name; + } + + scope->scope_end = scope_end; + + out->type = AML_VALUE_OBJECT; + out->value.object = scope; + + return PARSE_OK; +} + +static enum parse_status parse_op_region(struct aml_parser *parser, struct aml_value *out) +{ + aml_parser_advance(parser); + aml_parser_advance(parser); + + char *name; + enum parse_status status = read_name(parser, &name); + + if (status != PARSE_OK) { + return status; + } + + int region_space = aml_parser_advance(parser); + if (region_space < 0) { + free(name); + return region_space; + } + + struct aml_value region_offset; + status = parse_opcode(parser, ®ion_offset); + if (status != PARSE_OK) { + free(name); + return status; + } + + if (!aml_value_is_integer(®ion_offset)) { + free(name); + return PARSE_BADTYPE; + } + + struct aml_value region_len; + status = parse_opcode(parser, ®ion_len); + if (status != PARSE_OK) { + free(name); + return status; + } + + if (!aml_value_is_integer(®ion_len)) { + free(name); + return PARSE_BADTYPE; + } + + struct acpi_object *op_region = acpi_object_create(name, ACPI_OBJECT_OPERATION_REGION); + op_region->publish_path = name; + + out->type = AML_VALUE_OBJECT; + out->value.object = op_region; + + return PARSE_OK; +} + +static enum parse_status parse_field(struct aml_parser *parser, struct aml_value *out) +{ + long start = aml_parser_cursorpos(parser); + + aml_parser_advance(parser); + aml_parser_advance(parser); + + long len = read_pkg_length(parser); + if (len < 0) { + return PARSE_EOF; + } + + char *name; + enum parse_status status = read_name(parser, &name); + if (status != PARSE_OK) { + return status; + } + + int flags = aml_parser_advance(parser); + if (flags < 0) { + free(name); + return PARSE_EOF; + } + + struct acpi_object *region = aml_parser_resolve_path(parser, name); + free(name); + + if (!region) { + return PARSE_BADREF; + } + + long end = start + len; + while (aml_parser_cursorpos(parser) < end) { + char unit_name[ACPI_OBJECT_NAME_MAX]; + status = read_single_segment_name(parser, unit_name); + if (status != PARSE_OK) { + return status; + } + + int bits = read_pkg_length(parser); + if (bits < 0) { + return PARSE_EOF; + } + + struct acpi_object *field = acpi_object_create(unit_name, ACPI_OBJECT_FIELD); + //field_unit->field_unit.size = bits / 8; + + acpi_object_add_child(region, field); + } + + out->type = AML_VALUE_NONE; + return PARSE_OK; +} + +static enum parse_status parse_device(struct aml_parser *parser, struct aml_value *out) +{ + aml_parser_advance(parser); + aml_parser_advance(parser); + + long start = aml_parser_cursorpos(parser); + + long len = read_pkg_length(parser); + if (len < 0) { + return PARSE_EOF; + } + + char *name; + enum parse_status status = read_name(parser, &name); + if (status != PARSE_OK) { + return status; + } + + struct acpi_object *device = acpi_object_create(NULL, ACPI_OBJECT_DEVICE); + device->publish_path = name; + device->scope_end = start + len; + + out->type = AML_VALUE_OBJECT; + out->value.object = device; + + return PARSE_OK; +} + +static enum parse_status parse_name(struct aml_parser *parser, struct aml_value *out) +{ + aml_parser_advance(parser); + + char *name; + enum parse_status status = read_name(parser, &name); + if (status != PARSE_OK) { + return status; + } + + struct aml_value named_value; + status = parse_opcode(parser, &named_value); + + if (status != PARSE_OK) { + return status; + } + + struct acpi_object *object = NULL; + if (named_value.type == AML_VALUE_OBJECT) { + object = named_value.value.object; + } else { + object = acpi_object_create(NULL, ACPI_OBJECT_VALUE); + // TODO set value. + } + + object->publish_path = name; + + out->type = AML_VALUE_OBJECT; + out->value.object = object; + + return PARSE_OK; +} + +static enum parse_status parse_package(struct aml_parser *parser, struct aml_value *out) +{ + aml_parser_advance(parser); + + long len = read_pkg_length(parser); + if (len < 0) { + return PARSE_EOF; + } + + int nitems = aml_parser_advance(parser); + if (nitems < 0) { + return PARSE_EOF; + } + + struct acpi_object *pkg = acpi_object_create(NULL, ACPI_OBJECT_PACKAGE); + for (int i = 0; i < nitems; i++) { + struct aml_value pkg_item; + enum parse_status status = parse_opcode(parser, &pkg_item); + if (status != PARSE_OK) { + acpi_object_deref(pkg); + return status; + } + + // TODO add value to package + } + + out->type = AML_VALUE_OBJECT; + out->value.object = pkg; + + return PARSE_OK; +} + +static enum parse_status parse_name_chars(struct aml_parser *parser, struct aml_value *out) +{ + char *name; + enum parse_status status = read_name(parser, &name); + if (status != PARSE_OK) { + return status; + } + + out->type = AML_VALUE_NAME; + out->value.name = name; + return PARSE_OK; +} + +static enum parse_status parse_buffer(struct aml_parser *parser, struct aml_value *out) +{ + aml_parser_advance(parser); + long pkg_len = read_pkg_length(parser); + if (pkg_len < 0) { + return PARSE_EOF; + } + + struct aml_value buffer_size; + enum parse_status status = parse_opcode(parser, &buffer_size); + if (status != PARSE_OK) { + return status; + } + + if (!aml_value_is_integer(&buffer_size)) { + return PARSE_BADTYPE; + } + + uint64_t nbytes = aml_value_get_integer(&buffer_size); + + for (uint64_t i = 0; i < nbytes; i++) { + int b = aml_parser_advance(parser); + if (b < 0) { + return PARSE_EOF; + } + } + + struct acpi_object *buffer = acpi_object_create(NULL, ACPI_OBJECT_BUFFER); + + out->type = AML_VALUE_OBJECT; + out->value.object = buffer; + return PARSE_OK; +} + +static enum parse_status parse_method(struct aml_parser *parser, struct aml_value *out) +{ + aml_parser_advance(parser); + + long start = aml_parser_cursorpos(parser); + + long pkg_len = read_pkg_length(parser); + if (pkg_len < 0) { + return PARSE_EOF; + } + + char *name; + enum parse_status status = read_name(parser, &name); + if (status != PARSE_OK) { + return status; + } + + int flags = aml_parser_advance(parser); + if (flags < 0) { + return PARSE_EOF; + } + + long end = start + pkg_len; + while (aml_parser_cursorpos(parser) < end) { + int b = aml_parser_advance(parser); + if (b < 0) { + return PARSE_EOF; + } + } + + struct acpi_object *method = acpi_object_create(NULL, ACPI_OBJECT_METHOD); + method->publish_path = name; + + out->type = AML_VALUE_OBJECT; + out->value.object = method; + + return PARSE_OK; +} + +struct aml_byte_encoding byte_encoding[] = { + BYTECODE(AML_ZERO_OP, parse_zero, "ZeroOp"), + BYTECODE(AML_ONE_OP, parse_one, "OneOp"), + BYTECODE(AML_ALIAS_OP, NULL, "AliasOp"), + BYTECODE(AML_NAME_OP, parse_name, "NameOp"), + BYTECODE(AML_BYTE_PREFIX, parse_byte_prefix, "BytePrefix"), + BYTECODE(AML_WORD_PREFIX, parse_word_prefix, "WordPrefix"), + BYTECODE(AML_DWORD_PREFIX, parse_dword_prefix, "DWordPrefix"), + BYTECODE(AML_STRING_PREFIX, parse_string_prefix, "StringPrefix"), + BYTECODE(AML_QWORD_PREFIX, parse_qword_prefix, "QWordPrefix"), + BYTECODE(AML_SCOPE_OP, parse_scope, "ScopeOp"), + BYTECODE(AML_BUFFER_OP, parse_buffer, "BufferOp"), + BYTECODE(AML_PACKAGE_OP, parse_package, "PackageOp"), + BYTECODE(AML_VAR_PACKAGE_OP, NULL, "VarPackageOp"), + BYTECODE(AML_METHOD_OP, parse_method, "MethodOp"), + BYTECODE(AML_DUAL_NAME_PREFIX, NULL, "DualNamePrefix"), + BYTECODE(AML_MULTI_NAME_PREFIX, NULL, "MultiNamePrefix"), + BYTECODE('0', parse_name_chars, "DigitChar"), + BYTECODE('1', parse_name_chars, "DigitChar"), + BYTECODE('2', parse_name_chars, "DigitChar"), + BYTECODE('3', parse_name_chars, "DigitChar"), + BYTECODE('4', parse_name_chars, "DigitChar"), + BYTECODE('5', parse_name_chars, "DigitChar"), + BYTECODE('6', parse_name_chars, "DigitChar"), + BYTECODE('7', parse_name_chars, "DigitChar"), + BYTECODE('8', parse_name_chars, "DigitChar"), + BYTECODE('9', parse_name_chars, "DigitChar"), + BYTECODE('A', parse_name_chars, "NameChar"), + BYTECODE('B', parse_name_chars, "NameChar"), + BYTECODE('C', parse_name_chars, "NameChar"), + BYTECODE('D', parse_name_chars, "NameChar"), + BYTECODE('E', parse_name_chars, "NameChar"), + BYTECODE('F', parse_name_chars, "NameChar"), + BYTECODE('G', parse_name_chars, "NameChar"), + BYTECODE('H', parse_name_chars, "NameChar"), + BYTECODE('I', parse_name_chars, "NameChar"), + BYTECODE('J', parse_name_chars, "NameChar"), + BYTECODE('K', parse_name_chars, "NameChar"), + BYTECODE('L', parse_name_chars, "NameChar"), + BYTECODE('M', parse_name_chars, "NameChar"), + BYTECODE('N', parse_name_chars, "NameChar"), + BYTECODE('O', parse_name_chars, "NameChar"), + BYTECODE('P', parse_name_chars, "NameChar"), + BYTECODE('Q', parse_name_chars, "NameChar"), + BYTECODE('R', parse_name_chars, "NameChar"), + BYTECODE('S', parse_name_chars, "NameChar"), + BYTECODE('T', parse_name_chars, "NameChar"), + BYTECODE('U', parse_name_chars, "NameChar"), + BYTECODE('V', parse_name_chars, "NameChar"), + BYTECODE('W', parse_name_chars, "NameChar"), + BYTECODE('X', parse_name_chars, "NameChar"), + BYTECODE('Y', parse_name_chars, "NameChar"), + BYTECODE('Z', parse_name_chars, "NameChar"), + EXT_BYTECODE(AML_EXT_MUTEX_OP, NULL, "MutexOp"), + EXT_BYTECODE(AML_EXT_EVENT_OP, NULL, "EventOp"), + EXT_BYTECODE(AML_EXT_COND_REF_OF_OP, NULL, "CondRefOfOp"), + EXT_BYTECODE(AML_EXT_CREATE_FIELD_OP, NULL, "CreateFieldOp"), + EXT_BYTECODE(AML_EXT_LOAD_TABLE_OP, NULL, "LoadTableOp"), + EXT_BYTECODE(AML_EXT_LOAD_OP, NULL, "LoadOp"), + EXT_BYTECODE(AML_EXT_STALL_OP, NULL, "StallOp"), + EXT_BYTECODE(AML_EXT_SLEEP_OP, NULL, "SleepOp"), + EXT_BYTECODE(AML_EXT_ACQUIRE_OP, NULL, "AcquireOp"), + EXT_BYTECODE(AML_EXT_SIGNAL_OP, NULL, "SignalOp"), + EXT_BYTECODE(AML_EXT_WAIT_OP, NULL, "WaitOp"), + EXT_BYTECODE(AML_EXT_RESET_OP, NULL, "ResetOp"), + EXT_BYTECODE(AML_EXT_RELEASE_OP, NULL, "ReleaseOp"), + EXT_BYTECODE(AML_EXT_FROM_BCD_OP, NULL, "FromBCDOp"), + EXT_BYTECODE(AML_EXT_TO_BCD_OP, NULL, "ToBCDOp"), + EXT_BYTECODE(AML_EXT_UNLOAD_OP, NULL, "UnloadOp"), + EXT_BYTECODE(AML_EXT_REVISION_OP, NULL, "RevisionOp"), + EXT_BYTECODE(AML_EXT_DEBUG_OP, NULL, "DebugOp"), + EXT_BYTECODE(AML_EXT_FATAL_OP, NULL, "FatalOp"), + EXT_BYTECODE(AML_EXT_TIMER_OP, NULL, "TimerOp"), + EXT_BYTECODE(AML_EXT_REGION_OP, parse_op_region, "OpRegionOp"), + EXT_BYTECODE(AML_EXT_FIELD_OP, parse_field, "FieldOp"), + EXT_BYTECODE(AML_EXT_DEVICE_OP, parse_device, "DeviceOp"), + EXT_BYTECODE(AML_EXT_PROCESSOR_OP, NULL, "ProcessorOp"), + EXT_BYTECODE(AML_EXT_POWER_RES_OP, NULL, "PowerResOp"), + EXT_BYTECODE(AML_EXT_THERMAL_ZONE_OP, NULL, "ThermalZoneOp"), + EXT_BYTECODE(AML_EXT_INDEX_FIELD_OP, NULL, "IndexFieldOp"), + EXT_BYTECODE(AML_EXT_BANK_FIELD_OP, NULL, "BankFieldOp"), + EXT_BYTECODE(AML_EXT_DATA_REGION_OP, NULL, "DataRegionOp"), + BYTECODE(AML_ROOT_CHAR, NULL, "RootChar"), + BYTECODE(AML_PARENT_PREFIX_CHAR, NULL, "ParentPrefixChar"), + BYTECODE('_', NULL, "NameChar"), + BYTECODE(AML_LOCAL0, NULL, "Local0Op"), + BYTECODE(AML_LOCAL1, NULL, "Local1Op"), + BYTECODE(AML_LOCAL2, NULL, "Local2Op"), + BYTECODE(AML_LOCAL3, NULL, "Local3Op"), + BYTECODE(AML_LOCAL4, NULL, "Local4Op"), + BYTECODE(AML_LOCAL5, NULL, "Local5Op"), + BYTECODE(AML_LOCAL6, NULL, "Local6Op"), + BYTECODE(AML_LOCAL7, NULL, "Local7Op"), + BYTECODE(AML_ARG0, NULL, "Arg0Op"), + BYTECODE(AML_ARG1, NULL, "Arg1Op"), + BYTECODE(AML_ARG2, NULL, "Arg2Op"), + BYTECODE(AML_ARG3, NULL, "Arg3Op"), + BYTECODE(AML_ARG4, NULL, "Arg4Op"), + BYTECODE(AML_ARG5, NULL, "Arg5Op"), + BYTECODE(AML_ARG6, NULL, "Arg6Op"), + BYTECODE(AML_STORE_OP, NULL, "StoreOp"), + BYTECODE(AML_REF_OF_OP, NULL, "RefOfOp"), + BYTECODE(AML_ADD_OP, NULL, "AddOp"), + BYTECODE(AML_CONCAT_OP, NULL, "ConcatOp"), + BYTECODE(AML_SUBTRACT_OP, NULL, "SubtractOp"), + BYTECODE(AML_INCREMENT_OP, NULL, "IncrementOp"), + BYTECODE(AML_DECREMENT_OP, NULL, "DecrementOp"), + BYTECODE(AML_MULTIPLY_OP, NULL, "MultiplyOp"), + BYTECODE(AML_DIVIDE_OP, NULL, "DivideOp"), + BYTECODE(AML_SHIFT_LEFT_OP, NULL, "ShiftLeftOp"), + BYTECODE(AML_SHIFT_RIGHT_OP, NULL, "ShiftRightOp"), + BYTECODE(AML_AND_OP, NULL, "AndOp"), + BYTECODE(AML_NAND_OP, NULL, "NAndOp"), + BYTECODE(AML_OR_OP, NULL, "OrOp"), + BYTECODE(AML_NOR_OP, NULL, "NorOp"), + BYTECODE(AML_XOR_OP, NULL, "XOrOp"), + BYTECODE(AML_NOT_OP, NULL, "NotOp"), + BYTECODE(AML_FIND_SET_LEFT_BIT_OP, NULL, "FindSetLeftBitOp"), + BYTECODE(AML_FIND_SET_RIGHT_BIT_OP, NULL, "FindSetRightBitOp"), + BYTECODE(AML_DEREF_OF_OP, NULL, "DerefOfOp"), + BYTECODE(AML_CONCAT_RES_OP, NULL, "ConcatResOp"), + BYTECODE(AML_MOD_OP, NULL, "ModOp"), + BYTECODE(AML_NOTIFY_OP, NULL, "NotifyOp"), + BYTECODE(AML_SIZE_OF_OP, NULL, "SizeOfOp"), + BYTECODE(AML_INDEX_OP, NULL, "IndexOp"), + BYTECODE(AML_MATCH_OP, NULL, "MatchOp"), + BYTECODE(AML_CREATE_DWORD_FIELD_OP, NULL, "CreateDWordFieldOp"), + BYTECODE(AML_CREATE_WORD_FIELD_OP, NULL, "CreateWordFieldOp"), + BYTECODE(AML_CREATE_BYTE_FIELD_OP, NULL, "CreateByteFieldOp"), + BYTECODE(AML_CREATE_BIT_FIELD_OP, NULL, "CreateBitFieldOp"), + BYTECODE(AML_OBJECT_TYPE_OP, NULL, "ObjectTypeOp"), + BYTECODE(AML_CREATE_QWORD_FIELD_OP, NULL, "CreateQWordFieldOp"), + BYTECODE(AML_LAND_OP, NULL, "LAndOp"), + BYTECODE(AML_LOR_OP, NULL, "LOrOp"), + BYTECODE(AML_LNOT_OP, NULL, "LNotOp"), + BYTECODE(AML_LEQUAL_OP, NULL, "LEqualOp"), + BYTECODE(AML_LGREATER_OP, NULL, "LGreaterOp"), + BYTECODE(AML_LLESS_OP, NULL, "LLessOp"), + BYTECODE(AML_TO_BUFFER_OP, NULL, "ToBufferOp"), + BYTECODE(AML_TO_DEC_STRING_OP, NULL, "ToDecimalStringOp"), + BYTECODE(AML_TO_HEX_STRING_OP, NULL, "ToHexStringOp"), + BYTECODE(AML_TO_INTEGER_OP, NULL, "ToIntegerOp"), + BYTECODE(AML_TO_STRING_OP, NULL, "ToStringOp"), + BYTECODE(AML_COPY_OBJECT_OP, NULL, "CopyObjectOp"), + BYTECODE(AML_MID_OP, NULL, "MidOp"), + BYTECODE(AML_CONTINUE_OP, NULL, "ContinueOp"), + BYTECODE(AML_IF_OP, NULL, "IfOp"), + BYTECODE(AML_ELSE_OP, NULL, "ElseOp"), + BYTECODE(AML_WHILE_OP, NULL, "WhileOp"), + BYTECODE(AML_NOOP_OP, NULL, "NoopOp"), + BYTECODE(AML_RETURN_OP, NULL, "ReturnOp"), + BYTECODE(AML_BREAK_OP, NULL, "BreakOp"), + BYTECODE(AML_BREAK_POINT_OP, NULL, "BreakPointOp"), + BYTECODE(AML_ONES_OP, NULL, "OnesOp"), +}; + +static unsigned int nr_byte_encoding = sizeof byte_encoding / sizeof(struct aml_byte_encoding); + +enum parse_status parse_opcode(struct aml_parser *parser, struct aml_value *out) +{ + unsigned int offset = aml_parser_cursorpos(parser); + int op0 = aml_parser_peek(parser); + int op1 = 0; + + if (op0 == PARSE_EOF) { + return PARSE_EOF; + } + + if (op0 == AML_EXT_OP) { + op1 = aml_parser_peek_next(parser); + if (op1 == PARSE_EOF) { + return PARSE_EOF; + } + } + + struct aml_byte_encoding *opcode = NULL; + for (unsigned int i = 0; i < nr_byte_encoding; i++) { + if (byte_encoding[i].opcode == op0 && byte_encoding[i].sub_opcode == op1) { + opcode = &byte_encoding[i]; + break; + } + } + + if (!opcode) { + fprintf(stderr, "unrecognised opcode at 0x%04x: %02x:%02x\n", offset, op0, op1); + return PARSE_UNKNOWNOP; + } + + if (!opcode->parser) { + fprintf(stderr, "unimplemented opcode at 0x%04x: %02x:%02x (%s)\n", offset, op0, op1, opcode->name); + return PARSE_UNKNOWNOP; + } + + return opcode->parser(parser, out); +} diff --git a/tools/amldecode/aml/opcode.h b/tools/amldecode/aml/opcode.h new file mode 100644 index 0000000..bbabfe4 --- /dev/null +++ b/tools/amldecode/aml/opcode.h @@ -0,0 +1,190 @@ +#ifndef AML_OPCODE_H_ +#define AML_OPCODE_H_ + +#include +#include "parser.h" + +struct aml_parser; +struct aml_value; + +// +// Primary OpCode +// +#define AML_ZERO_OP 0x00 +#define AML_ONE_OP 0x01 +#define AML_ALIAS_OP 0x06 +#define AML_NAME_OP 0x08 +#define AML_BYTE_PREFIX 0x0a +#define AML_WORD_PREFIX 0x0b +#define AML_DWORD_PREFIX 0x0c +#define AML_STRING_PREFIX 0x0d +#define AML_QWORD_PREFIX 0x0e +#define AML_SCOPE_OP 0x10 +#define AML_BUFFER_OP 0x11 +#define AML_PACKAGE_OP 0x12 +#define AML_VAR_PACKAGE_OP 0x13 +#define AML_METHOD_OP 0x14 +#define AML_EXTERNAL_OP 0x15 +#define AML_DUAL_NAME_PREFIX 0x2e +#define AML_MULTI_NAME_PREFIX 0x2f +#define AML_NAME_CHAR_A 0x41 +#define AML_NAME_CHAR_B 0x42 +#define AML_NAME_CHAR_C 0x43 +#define AML_NAME_CHAR_D 0x44 +#define AML_NAME_CHAR_E 0x45 +#define AML_NAME_CHAR_F 0x46 +#define AML_NAME_CHAR_G 0x47 +#define AML_NAME_CHAR_H 0x48 +#define AML_NAME_CHAR_I 0x49 +#define AML_NAME_CHAR_J 0x4a +#define AML_NAME_CHAR_K 0x4b +#define AML_NAME_CHAR_L 0x4c +#define AML_NAME_CHAR_M 0x4d +#define AML_NAME_CHAR_N 0x4e +#define AML_NAME_CHAR_O 0x4f +#define AML_NAME_CHAR_P 0x50 +#define AML_NAME_CHAR_Q 0x51 +#define AML_NAME_CHAR_R 0x52 +#define AML_NAME_CHAR_S 0x53 +#define AML_NAME_CHAR_T 0x54 +#define AML_NAME_CHAR_U 0x55 +#define AML_NAME_CHAR_V 0x56 +#define AML_NAME_CHAR_W 0x57 +#define AML_NAME_CHAR_X 0x58 +#define AML_NAME_CHAR_Y 0x59 +#define AML_NAME_CHAR_Z 0x5a +#define AML_ROOT_CHAR 0x5c +#define AML_PARENT_PREFIX_CHAR 0x5e +#define AML_NAME_CHAR__ 0x5f +#define AML_LOCAL0 0x60 +#define AML_LOCAL1 0x61 +#define AML_LOCAL2 0x62 +#define AML_LOCAL3 0x63 +#define AML_LOCAL4 0x64 +#define AML_LOCAL5 0x65 +#define AML_LOCAL6 0x66 +#define AML_LOCAL7 0x67 +#define AML_ARG0 0x68 +#define AML_ARG1 0x69 +#define AML_ARG2 0x6a +#define AML_ARG3 0x6b +#define AML_ARG4 0x6c +#define AML_ARG5 0x6d +#define AML_ARG6 0x6e +#define AML_STORE_OP 0x70 +#define AML_REF_OF_OP 0x71 +#define AML_ADD_OP 0x72 +#define AML_CONCAT_OP 0x73 +#define AML_SUBTRACT_OP 0x74 +#define AML_INCREMENT_OP 0x75 +#define AML_DECREMENT_OP 0x76 +#define AML_MULTIPLY_OP 0x77 +#define AML_DIVIDE_OP 0x78 +#define AML_SHIFT_LEFT_OP 0x79 +#define AML_SHIFT_RIGHT_OP 0x7a +#define AML_AND_OP 0x7b +#define AML_NAND_OP 0x7c +#define AML_OR_OP 0x7d +#define AML_NOR_OP 0x7e +#define AML_XOR_OP 0x7f +#define AML_NOT_OP 0x80 +#define AML_FIND_SET_LEFT_BIT_OP 0x81 +#define AML_FIND_SET_RIGHT_BIT_OP 0x82 +#define AML_DEREF_OF_OP 0x83 +#define AML_CONCAT_RES_OP 0x84 +#define AML_MOD_OP 0x85 +#define AML_NOTIFY_OP 0x86 +#define AML_SIZE_OF_OP 0x87 +#define AML_INDEX_OP 0x88 +#define AML_MATCH_OP 0x89 +#define AML_CREATE_DWORD_FIELD_OP 0x8a +#define AML_CREATE_WORD_FIELD_OP 0x8b +#define AML_CREATE_BYTE_FIELD_OP 0x8c +#define AML_CREATE_BIT_FIELD_OP 0x8d +#define AML_OBJECT_TYPE_OP 0x8e +#define AML_CREATE_QWORD_FIELD_OP 0x8f +#define AML_LAND_OP 0x90 +#define AML_LOR_OP 0x91 +#define AML_LNOT_OP 0x92 +#define AML_LEQUAL_OP 0x93 +#define AML_LGREATER_OP 0x94 +#define AML_LLESS_OP 0x95 +#define AML_TO_BUFFER_OP 0x96 +#define AML_TO_DEC_STRING_OP 0x97 +#define AML_TO_HEX_STRING_OP 0x98 +#define AML_TO_INTEGER_OP 0x99 +#define AML_TO_STRING_OP 0x9c +#define AML_COPY_OBJECT_OP 0x9d +#define AML_MID_OP 0x9e +#define AML_CONTINUE_OP 0x9f +#define AML_IF_OP 0xa0 +#define AML_ELSE_OP 0xa1 +#define AML_WHILE_OP 0xa2 +#define AML_NOOP_OP 0xa3 +#define AML_RETURN_OP 0xa4 +#define AML_BREAK_OP 0xa5 +#define AML_BREAK_POINT_OP 0xcc +#define AML_ONES_OP 0xff + +// +// Extended OpCode +// +#define AML_EXT_OP 0x5b + +#define AML_EXT_MUTEX_OP 0x01 +#define AML_EXT_EVENT_OP 0x02 +#define AML_EXT_COND_REF_OF_OP 0x12 +#define AML_EXT_CREATE_FIELD_OP 0x13 +#define AML_EXT_LOAD_TABLE_OP 0x1f +#define AML_EXT_LOAD_OP 0x20 +#define AML_EXT_STALL_OP 0x21 +#define AML_EXT_SLEEP_OP 0x22 +#define AML_EXT_ACQUIRE_OP 0x23 +#define AML_EXT_SIGNAL_OP 0x24 +#define AML_EXT_WAIT_OP 0x25 +#define AML_EXT_RESET_OP 0x26 +#define AML_EXT_RELEASE_OP 0x27 +#define AML_EXT_FROM_BCD_OP 0x28 +#define AML_EXT_TO_BCD_OP 0x29 +#define AML_EXT_UNLOAD_OP 0x2a +#define AML_EXT_REVISION_OP 0x30 +#define AML_EXT_DEBUG_OP 0x31 +#define AML_EXT_FATAL_OP 0x32 +#define AML_EXT_TIMER_OP 0x33 +#define AML_EXT_REGION_OP 0x80 +#define AML_EXT_FIELD_OP 0x81 +#define AML_EXT_DEVICE_OP 0x82 +#define AML_EXT_PROCESSOR_OP 0x83 +#define AML_EXT_POWER_RES_OP 0x84 +#define AML_EXT_THERMAL_ZONE_OP 0x85 +#define AML_EXT_INDEX_FIELD_OP 0x86 +#define AML_EXT_BANK_FIELD_OP 0x87 +#define AML_EXT_DATA_REGION_OP 0x88 + +// +// FieldElement OpCode +// +#define AML_FIELD_RESERVED_OP 0x00 +#define AML_FIELD_ACCESS_OP 0x01 +#define AML_FIELD_CONNECTION_OP 0x02 +#define AML_FIELD_EXT_ACCESS_OP 0x03 + +// +// AML Name segment definitions +// +#define AML_NAME_SEG_SIZE 4 + +typedef enum parse_status(*aml_bytecode_parser)(struct aml_parser *, struct aml_value *); + +struct aml_byte_encoding { + uint8_t opcode; + uint8_t sub_opcode; + + aml_bytecode_parser parser; + + const char *name; +}; + +extern enum parse_status parse_opcode(struct aml_parser *parser, struct aml_value *out); + +#endif diff --git a/tools/amldecode/aml/parser.c b/tools/amldecode/aml/parser.c new file mode 100644 index 0000000..6fc13c9 --- /dev/null +++ b/tools/amldecode/aml/parser.c @@ -0,0 +1,271 @@ +#include +#include +#include +#include "parser.h" +#include "opcode.h" +#include "object.h" +#include "value.h" + +void aml_parser_init(struct aml_parser *parser, void *p, size_t len) +{ + memset(parser, 0x0, sizeof *parser); + + parser->start = parser->ptr = p; + parser->end = parser->start + len; +} + +void aml_parser_set_namespace(struct aml_parser *parser, struct acpi_namespace *ns) +{ + parser->ns = ns; +} + +int aml_parser_peek(struct aml_parser *parser) +{ + if (parser->ptr >= parser->end) { + return PARSE_EOF; + } + + return *parser->ptr; +} + +int aml_parser_peek_next(struct aml_parser *parser) +{ + if (parser->ptr + 1 >= parser->end) { + return PARSE_EOF; + } + + return *(parser->ptr + 1); +} + +int aml_parser_advance(struct aml_parser *parser) +{ + if (parser->ptr >= parser->end) { + return PARSE_EOF; + } + + return *(parser->ptr++); +} + +unsigned int aml_parser_cursorpos(struct aml_parser *parser) +{ + if (parser->ptr < parser->start) { + return 0; + } + + return parser->ptr - parser->start; +} + +void aml_parser_save_cursor(struct aml_parser *parser) +{ + parser->saved_ptr = parser->ptr; +} + +void aml_parser_load_cursor(struct aml_parser *parser) +{ + parser->ptr = parser->saved_ptr; +} + +static bool object_name_is_always_root(const char *name) +{ + return (!strcmp(name, "_SB_") || !strcmp(name, "GPE_") || !strcmp(name, "_PR_") || !strcmp(name, "_TZ_")); +} + +void aml_parser_add_object(struct aml_parser *parser, struct acpi_object *object) +{ + if (object->parent) { + return; + } + + char *publish_path = object->publish_path; + char *rpath = publish_path; + + //printf("adding object '%s'. current scope=%s\n", publish_path, parser->cur_scope ? parser->cur_scope->scope_object->name : ""); + + struct acpi_object *cur = parser->cur_scope ? parser->cur_scope->scope_object : NULL; + if (*rpath == '\\') { + cur = parser->ns->root; + rpath++; + } + + char *sp; + char *tok = strtok_r(rpath, ".", &sp); + if (object_name_is_always_root(tok)) { + cur = parser->ns->root; + } + + if (!cur) { + fprintf(stderr, "cannot add object with relative path: no scope!\n"); + abort(); + } + + while (1) { + if (!tok || *tok == '\0') { + break; + } + + char *next_tok = strtok_r(NULL, ".", &sp); + if (!next_tok) { + break; + } + + struct acpi_object *next = acpi_object_get_child(cur, tok); + if (!next) { + //printf("auto-creating scope '%s' under '%s'\n", tok, cur->name); + next = acpi_object_create(tok, ACPI_OBJECT_NAMESPACE); + acpi_object_add_child(cur, next); + } + + tok = next_tok; + } + + strncpy(object->name, tok, sizeof object->name - 1); + object->name[sizeof object->name - 1] = '\0'; + + acpi_object_add_child(cur, object); + free(publish_path); +} + +void aml_parser_push_scope(struct aml_parser *parser, struct acpi_object *object) +{ + struct aml_parser_scope *scope = malloc(sizeof *scope); + if (!scope) { + perror("aml_parser_push_scope: malloc"); + abort(); + } + + scope->scope_object = object; + scope->scope_end = object->scope_end; + scope->next = parser->cur_scope; + object->scope_end = 0; + parser->cur_scope = scope; + + //printf("## 0x%04x: moving into scope '%s' (ends at 0x%04zx)\n", aml_parser_cursorpos(parser), parser->cur_scope->scope_object->name, scope->scope_end); +} + +void aml_parser_pop_scope(struct aml_parser *parser) +{ + if (!parser->cur_scope) { + return; + } + + if (!strcmp(parser->cur_scope->scope_object->name, "\\") && !parser->cur_scope->next) { + return; + } + + /* + printf("## 0x%04x: moving out of scope '%s' up to scope '%s' (ends at 0x%04zx)\n", + aml_parser_cursorpos(parser), + parser->cur_scope ? parser->cur_scope->scope_object->name : "", + parser->cur_scope->next ? parser->cur_scope->next->scope_object->name : "", + parser->cur_scope->next ? parser->cur_scope->next->scope_end : 0xFFFF); + */ + + struct aml_parser_scope *previous_scope = parser->cur_scope; + parser->cur_scope = parser->cur_scope->next; + free(previous_scope); +} + +static void add_object_to_parser(struct aml_parser *parser, struct acpi_object *object) +{ + if (!object->parent && object != parser->ns->root) { + aml_parser_add_object(parser, object); + } + + if (object->scope_end != 0) { + aml_parser_push_scope(parser, object); + } +} + +static bool should_pop_current_scope(struct aml_parser *parser) +{ + if (!parser->cur_scope) { + return false; + } + + if (!strcmp(parser->cur_scope->scope_object->name, "\\") && !parser->cur_scope->next) { + return false; + } + + return parser->cur_scope->scope_end <= aml_parser_cursorpos(parser); +} + +enum parse_status aml_parser_parse_into_namespace(struct aml_parser *parser, struct acpi_namespace *ns) +{ + parser->ns = ns; + + while (1) { + struct aml_value value; + enum parse_status status = parse_opcode(parser, &value); + if (status != PARSE_OK) { + return status; + } + + if (value.type == AML_VALUE_OBJECT) { + struct acpi_object *object = value.value.object; + add_object_to_parser(parser, object); + } + + while (should_pop_current_scope(parser)) { + aml_parser_pop_scope(parser); + } + } + + return PARSE_OK; +} + +struct acpi_object *aml_parser_resolve_path(struct aml_parser *parser, const char *path) +{ + struct acpi_object *cur = parser->cur_scope ? parser->cur_scope->scope_object : NULL; + if (*path == '\\') { + path++; + cur = parser->ns->root; + } + + size_t path_len = strlen(path); + char *rpath = malloc(path_len + 1); + strcpy(rpath, path); + + char *sp; + char *tok = strtok_r(rpath, ".", &sp); + if (tok && object_name_is_always_root(tok)) { + cur = parser->ns->root; + } + + if (!cur) { + free(rpath); + return NULL; + } + + while (tok) { + struct acpi_object *child = acpi_object_get_child(cur, tok); + if (!child) { + free(rpath); + return NULL; + } + + cur = child; + tok = strtok_r(NULL, ".", &sp); + } + + free(rpath); + return cur; +} + +#define STATUS_STRING(code) \ + case code: \ + return #code; + +const char *parse_status_string(enum parse_status status) +{ + switch (status) { + STATUS_STRING(PARSE_OK) + STATUS_STRING(PARSE_EOF) + STATUS_STRING(PARSE_NOMEM) + STATUS_STRING(PARSE_BADSTRING) + STATUS_STRING(PARSE_UNKNOWNOP) + STATUS_STRING(PARSE_BADTYPE) + STATUS_STRING(PARSE_BADREF) + default: + return ""; + } +} diff --git a/tools/amldecode/aml/parser.h b/tools/amldecode/aml/parser.h new file mode 100644 index 0000000..8b59971 --- /dev/null +++ b/tools/amldecode/aml/parser.h @@ -0,0 +1,48 @@ +#ifndef AML_PARSER_H_ +#define AML_PARSER_H_ + +#include + +struct acpi_namespace; + +enum parse_status { + PARSE_OK = 0, + PARSE_EOF = -1, + PARSE_NOMEM = -2, + PARSE_BADSTRING = -3, + PARSE_UNKNOWNOP = -4, + PARSE_BADTYPE = -5, + PARSE_BADREF = -6, +}; + +struct aml_parser_scope { + struct acpi_object *scope_object; + size_t scope_end; + struct aml_parser_scope *next; +}; + +struct aml_parser { + unsigned char *start, *end; + unsigned char *ptr, *saved_ptr; + struct acpi_namespace *ns; + struct aml_parser_scope *cur_scope; +}; + +extern void aml_parser_init(struct aml_parser *parser, void *p, size_t len); +extern void aml_parser_set_namespace(struct aml_parser *parser, struct acpi_namespace *ns); +extern int aml_parser_peek(struct aml_parser *parser); +extern int aml_parser_peek_next(struct aml_parser *parser); +extern int aml_parser_advance(struct aml_parser *parser); +extern unsigned int aml_parser_cursorpos(struct aml_parser *parser); +extern void aml_parser_save_cursor(struct aml_parser *parser); +extern void aml_parser_load_cursor(struct aml_parser *parser); +extern void aml_parser_add_object(struct aml_parser *parser, struct acpi_object *object); +extern void aml_parser_push_scope(struct aml_parser *parser, struct acpi_object *object); +extern void aml_parser_pop_scope(struct aml_parser *parser); + +extern enum parse_status aml_parser_parse_into_namespace(struct aml_parser *parser, struct acpi_namespace *ns); +extern struct acpi_object *aml_parser_resolve_path(struct aml_parser *parser, const char *path); + +extern const char *parse_status_string(enum parse_status status); + +#endif diff --git a/tools/amldecode/aml/value.c b/tools/amldecode/aml/value.c new file mode 100644 index 0000000..caaa525 --- /dev/null +++ b/tools/amldecode/aml/value.c @@ -0,0 +1,17 @@ +#include "value.h" + +uint64_t aml_value_get_integer(const struct aml_value *value) +{ + switch (value->type) { + case AML_VALUE_UINT8: + return value->value.uint8; + case AML_VALUE_UINT16: + return value->value.uint8; + case AML_VALUE_UINT32: + return value->value.uint8; + case AML_VALUE_UINT64: + return value->value.uint8; + default: + return 0; + } +} diff --git a/tools/amldecode/aml/value.h b/tools/amldecode/aml/value.h new file mode 100644 index 0000000..1d652fc --- /dev/null +++ b/tools/amldecode/aml/value.h @@ -0,0 +1,43 @@ +#ifndef AML_VALUE_H_ +#define AML_VALUE_H_ + +#include +#include +#include "constants.h" + +struct acpi_object; + +enum aml_value_type { + AML_VALUE_NONE, + AML_VALUE_UINT8, + AML_VALUE_UINT16, + AML_VALUE_UINT32, + AML_VALUE_UINT64, + AML_VALUE_OBJECT, + AML_VALUE_NAME, + AML_VALUE_STRING, +}; + +struct aml_value { + enum aml_value_type type; + + union { + uint8_t uint8; + uint16_t uint16; + uint32_t uint32; + uint64_t uint64; + struct acpi_object *object; + const char *name; + const char *str; + } value; +}; + +static inline bool aml_value_is_integer(const struct aml_value *value) +{ + return value->type == AML_VALUE_UINT8 || value->type == AML_VALUE_UINT16 || value->type == AML_VALUE_UINT32 || value->type == AML_VALUE_UINT64; +} + +extern uint64_t aml_value_get_integer(const struct aml_value *value); +extern void aml_value_destroy(struct aml_value *value); + +#endif diff --git a/tools/amldecode/main.c b/tools/amldecode/main.c new file mode 100644 index 0000000..45914a0 --- /dev/null +++ b/tools/amldecode/main.c @@ -0,0 +1,154 @@ +#include +#include +#include +#include "aml/parser.h" +#include "aml/object.h" +#include "table.h" + +/* +static struct acpi_table_header *read_table(FILE *fp) +{ + fseek(fp, 0, SEEK_END); + size_t len = ftell(fp); + fseek(fp, 0, SEEK_SET); + + struct acpi_table_header *table = malloc(len); + if (!table) { + return NULL; + } + + fread(table, 1, len, fp); + + return table; +} + +int main(int argc, const char **argv) +{ + if (argc < 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return -1; + } + + const char *filepath = argv[1]; + FILE *fp = fopen(filepath, "rb"); + if (!fp) { + perror("open"); + return -1; + } + + struct acpi_table_header *table = read_table(fp); + fclose(fp); + + if (!table) { + perror("read_table"); + return -1; + } + + struct aml_object *root; + + parse_aml(table, &root); + + printf("\n\n"); + aml_object_print(root, 0); + free(table); + + return 0; +} +*/ + +/* +static struct acpi_namespace *create_test_namespace(void) +{ + struct acpi_namespace *ns = acpi_namespace_create(); + + struct acpi_object *root = acpi_object_create("\\", ACPI_OBJECT_NAMESPACE); + ns->root = root; + + struct acpi_object *pr = acpi_object_create("_PR", ACPI_OBJECT_NAMESPACE); + struct acpi_object *cpu0 = acpi_object_create("CPU0", ACPI_OBJECT_CPU); + acpi_object_add_child(pr, cpu0); + + struct acpi_object *sb = acpi_object_create("_SB", ACPI_OBJECT_NAMESPACE); + struct acpi_object *lid0 = acpi_object_create("LID0", ACPI_OBJECT_DEVICE); + struct acpi_object *lid0_hid = acpi_object_create("_HID", ACPI_OBJECT_VALUE); + struct acpi_object *lid0_sta = acpi_object_create("_STA", ACPI_OBJECT_METHOD); + acpi_object_add_child(lid0, lid0_hid); + acpi_object_add_child(lid0, lid0_sta); + + struct acpi_object *pci0 = acpi_object_create("PCI0", ACPI_OBJECT_DEVICE); + struct acpi_object *pci0_hid = acpi_object_create("_HID", ACPI_OBJECT_VALUE); + struct acpi_object *pci0_cid = acpi_object_create("_CID", ACPI_OBJECT_VALUE); + struct acpi_object *pci0_rp03 = acpi_object_create("RP03", ACPI_OBJECT_NAMESPACE); + struct acpi_object *pci0_rp03_pxp3 = acpi_object_create("PXP3", ACPI_OBJECT_POWER_RESOURCE); + struct acpi_object *pci0_gfx0 = acpi_object_create("GFX0", ACPI_OBJECT_DEVICE); + struct acpi_object *pci0_gfx0_adr = acpi_object_create("_ADR", ACPI_OBJECT_VALUE); + struct acpi_object *pci0_gfx0_dd01 = acpi_object_create("DD01", ACPI_OBJECT_DEVICE); + struct acpi_object *pci0_gfx0_dd01_bcl = acpi_object_create("_BCL", ACPI_OBJECT_METHOD); + acpi_object_add_child(pci0_gfx0_dd01, pci0_gfx0_dd01_bcl); + acpi_object_add_child(pci0_gfx0, pci0_gfx0_adr); + acpi_object_add_child(pci0_gfx0, pci0_gfx0_dd01); + acpi_object_add_child(pci0_rp03, pci0_rp03_pxp3); + acpi_object_add_child(pci0, pci0_hid); + acpi_object_add_child(pci0, pci0_cid); + acpi_object_add_child(pci0, pci0_rp03); + acpi_object_add_child(pci0, pci0_gfx0); + + acpi_object_add_child(sb, lid0); + acpi_object_add_child(sb, pci0); + + struct acpi_object *tz = acpi_object_create("_TZ", ACPI_OBJECT_NAMESPACE); + struct acpi_object *tz_fn00 = acpi_object_create("FN00", ACPI_OBJECT_POWER_RESOURCE); + struct acpi_object *tz_fan0 = acpi_object_create("FAN0", ACPI_OBJECT_DEVICE); + struct acpi_object *tz_fan0_hid = acpi_object_create("_HID", ACPI_OBJECT_VALUE); + struct acpi_object *tz_tz00 = acpi_object_create("TZ00", ACPI_OBJECT_THERMAL_ZONE); + acpi_object_add_child(tz_fan0, tz_fan0_hid); + acpi_object_add_child(tz, tz_fn00); + acpi_object_add_child(tz, tz_fan0); + acpi_object_add_child(tz, tz_tz00); + + struct acpi_object *gpe = acpi_object_create("_GPE", ACPI_OBJECT_NAMESPACE); + + acpi_object_add_child(root, pr); + acpi_object_add_child(root, sb); + acpi_object_add_child(root, tz); + acpi_object_add_child(root, gpe); + + return ns; +} +*/ + +int main(int argc, const char **argv) +{ + if (argc < 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return -1; + } + + struct acpi_table *table; + enum loader_status loader_status = acpi_table_load(argv[1], &table); + if (loader_status != LOADER_OK) { + fprintf(stderr, "cannot load '%s': %s\n", argv[1], loader_status_string(loader_status)); + return -1; + } + + void *table_payload; + size_t payload_len; + acpi_table_get_payload(table, &table_payload, &payload_len); + + struct acpi_namespace *ns = acpi_namespace_create(); + + struct aml_parser parser; + aml_parser_init(&parser, table_payload, payload_len); + + enum parse_status parser_status = aml_parser_parse_into_namespace(&parser, ns); + if (parser_status != PARSE_OK) { + fprintf(stderr, "error occurred while parsing '%s': %s\n", argv[1], parse_status_string(parser_status)); + } + + if (ns->root) { + acpi_object_print(ns->root, 0); + } + + acpi_table_destroy(table); + return parser_status == PARSE_OK ? 0 : -1; +} diff --git a/tools/amldecode/table.c b/tools/amldecode/table.c new file mode 100644 index 0000000..6f03c2f --- /dev/null +++ b/tools/amldecode/table.c @@ -0,0 +1,72 @@ +#include +#include +#include "table.h" + +enum loader_status acpi_table_load(const char *path, struct acpi_table **out) +{ + FILE *fp = fopen(path, "rb"); + if (!fp) { + return LOADER_IOERR; + } + + fseek(fp, 0, SEEK_END); + size_t len = ftell(fp); + fseek(fp, 0, SEEK_SET); + + unsigned char *buf = malloc(len); + if (!buf) { + fclose(fp); + return LOADER_NOMEM; + } + + size_t r = fread(buf, 1, len, fp); + if (r != len) { + free(buf); + fclose(fp); + return LOADER_IOERR; + } + + fclose(fp); + + size_t sum = 0; + for (size_t i = 0; i < len; i++) { + sum += buf[i]; + } + + if ((sum % 0x100) != 0) { + free(buf); + return LOADER_BADSUM; + } + + struct acpi_table *table = (struct acpi_table *)buf; + *out = table; + + return LOADER_OK; +} + +void acpi_table_destroy(struct acpi_table *table) +{ + free(table); +} + +void acpi_table_get_payload(struct acpi_table *table, void **p, size_t *len) +{ + *p = (unsigned char *)table + sizeof *table; + *len = table->length; +} + +#define STATUS_STRING(code) \ + case code: \ + return #code; + +const char *loader_status_string(enum loader_status status) +{ + switch (status) { + STATUS_STRING(LOADER_OK) + STATUS_STRING(LOADER_IOERR) + STATUS_STRING(LOADER_BADSUM) + STATUS_STRING(LOADER_NOMEM) + default: + return ""; + } +} diff --git a/tools/amldecode/table.h b/tools/amldecode/table.h new file mode 100644 index 0000000..f2301c9 --- /dev/null +++ b/tools/amldecode/table.h @@ -0,0 +1,32 @@ +#ifndef TABLE_H_ +#define TABLE_H_ + +#include +#include + +enum loader_status { + LOADER_OK = 0, + LOADER_IOERR, + LOADER_BADSUM, + LOADER_NOMEM, +}; + +struct acpi_table { + char sig[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + char oem_id[6]; + char oem_table_id[8]; + uint32_t oem_revision; + uint32_t creator_id; + uint32_t creator_revision; +}; + +extern enum loader_status acpi_table_load(const char *path, struct acpi_table **out); +extern void acpi_table_destroy(struct acpi_table *table); +extern void acpi_table_get_payload(struct acpi_table *table, void **p, size_t *len); + +extern const char *loader_status_string(enum loader_status status); + +#endif