Files
mango/tools/amldecode/aml/parser.c

291 lines
6.4 KiB
C

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#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 : "<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;
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 : "<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;
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 "<unknown>";
}
}