tools: add tool to decode AML files and build an ACPI namespace
This commit is contained in:
271
tools/amldecode/aml/parser.c
Normal file
271
tools/amldecode/aml/parser.c
Normal file
@@ -0,0 +1,271 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#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 : "<none>");
|
||||
|
||||
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 : "<none>",
|
||||
parser->cur_scope->next ? parser->cur_scope->next->scope_object->name : "<none>",
|
||||
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 "<unknown>";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user