#include #include #include #include "parser.h" #include "opcode.h" #include "object.h" #include "value.h" #include "../table.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; cur = next; } 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; aml_parser_push_scope(parser, ns->root); enum parse_status status = PARSE_OK; while (1) { struct aml_value value; status = parse_opcode(parser, &value); if (status != PARSE_OK) { break; } 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); } } if (status == PARSE_EOF && !parser->cur_scope->next) { status = PARSE_OK; } if (status != PARSE_OK) { fprintf(stderr, "parse error at 0x%04lx: %s\n", aml_parser_cursorpos(parser) + sizeof(struct acpi_table), parse_status_string(status)); } return status; } 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); char *token_buf = rpath; strcpy(rpath, path); while (*token_buf == '^') { cur = cur->parent; token_buf++; } char *sp; char *tok = strtok_r(token_buf, ".", &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 ""; } }