Files
reactos/drivers/bus/acpi/parser/psparse.c
T
Timo Kreuzer 9ea495ba33 Create a branch for header work.
svn path=/branches/header-work/; revision=45691
2010-02-26 22:57:55 +00:00

1224 lines
30 KiB
C

/******************************************************************************
*
* Module Name: psparse - Parser top level AML parse routines
* $Revision: 1.1 $
*
*****************************************************************************/
/*
* Copyright (C) 2000, 2001 R. Byron Moore
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Parse the AML and build an operation tree as most interpreters,
* like Perl, do. Parsing is done by hand rather than with a YACC
* generated parser to tightly constrain stack and dynamic memory
* usage. At the same time, parsing is kept flexible and the code
* fairly compact by parsing based on a list of AML opcode
* templates in Aml_op_info[]
*/
#include <acpi.h>
#define _COMPONENT ACPI_PARSER
MODULE_NAME ("psparse")
u32 acpi_gbl_depth = 0;
extern u32 acpi_gbl_scope_depth;
/*******************************************************************************
*
* FUNCTION: Acpi_ps_peek_opcode
*
* PARAMETERS: None
*
* RETURN: Status
*
* DESCRIPTION: Get next AML opcode (without incrementing AML pointer)
*
******************************************************************************/
static u32
acpi_ps_get_opcode_size (
u32 opcode)
{
/* Extended (2-byte) opcode if > 255 */
if (opcode > 0x00FF) {
return (2);
}
/* Otherwise, just a single byte opcode */
return (1);
}
/*******************************************************************************
*
* FUNCTION: Acpi_ps_peek_opcode
*
* PARAMETERS: Parser_state - A parser state object
*
* RETURN: Status
*
* DESCRIPTION: Get next AML opcode (without incrementing AML pointer)
*
******************************************************************************/
u16
acpi_ps_peek_opcode (
ACPI_PARSE_STATE *parser_state)
{
u8 *aml;
u16 opcode;
aml = parser_state->aml;
opcode = (u16) GET8 (aml);
aml++;
/*
* Original code special cased LNOTEQUAL, LLESSEQUAL, LGREATEREQUAL.
* These opcodes are no longer recognized. Instead, they are broken into
* two opcodes.
*
*
* if (Opcode == AML_EXTOP
* || (Opcode == AML_LNOT
* && (GET8 (Acpi_aml) == AML_LEQUAL
* || GET8 (Acpi_aml) == AML_LGREATER
* || GET8 (Acpi_aml) == AML_LLESS)))
*
* extended Opcode, !=, <=, or >=
*/
if (opcode == AML_EXTOP) {
/* Extended opcode */
opcode = (u16) ((opcode << 8) | GET8 (aml));
aml++;
}
/* don't convert bare name to a namepath */
return (opcode);
}
/*******************************************************************************
*
* FUNCTION: Acpi_ps_create_state
*
* PARAMETERS: Acpi_aml - Acpi_aml code pointer
* Acpi_aml_size - Length of AML code
*
* RETURN: A new parser state object
*
* DESCRIPTION: Create and initialize a new parser state object
*
******************************************************************************/
ACPI_PARSE_STATE *
acpi_ps_create_state (
u8 *aml,
u32 aml_size)
{
ACPI_PARSE_STATE *parser_state;
parser_state = acpi_cm_callocate (sizeof (ACPI_PARSE_STATE));
if (!parser_state) {
return (NULL);
}
parser_state->aml = aml;
parser_state->aml_end = aml + aml_size;
parser_state->pkg_end = parser_state->aml_end;
parser_state->aml_start = aml;
return (parser_state);
}
/*******************************************************************************
*
* FUNCTION: Acpi_ps_find_object
*
* PARAMETERS: Opcode - Current opcode
* Parser_state - Current state
* Walk_state - Current state
* *Op - Where found/new op is returned
*
* RETURN: Status
*
* DESCRIPTION: Find a named object. Two versions - one to search the parse
* tree (for parser-only applications such as acpidump), another
* to search the ACPI internal namespace (the parse tree may no
* longer exist)
*
******************************************************************************/
#ifdef PARSER_ONLY
ACPI_STATUS
acpi_ps_find_object (
u16 opcode,
ACPI_PARSE_OBJECT *op,
ACPI_WALK_STATE *walk_state,
ACPI_PARSE_OBJECT **out_op)
{
NATIVE_CHAR *path;
/* We are only interested in opcodes that have an associated name */
if (!acpi_ps_is_named_op (opcode)) {
*out_op = op;
return (AE_OK);
}
/* Find the name in the parse tree */
path = acpi_ps_get_next_namestring (walk_state->parser_state);
*out_op = acpi_ps_find (acpi_ps_get_parent_scope (walk_state->parser_state),
path, opcode, 1);
if (!(*out_op)) {
return (AE_NOT_FOUND);
}
return (AE_OK);
}
#endif
/*******************************************************************************
*
* FUNCTION: Acpi_ps_complete_this_op
*
* PARAMETERS: Walk_state - Current State
* Op - Op to complete
*
* RETURN: TRUE if Op and subtree was deleted
*
* DESCRIPTION: Perform any cleanup at the completion of an Op.
*
******************************************************************************/
static u8
acpi_ps_complete_this_op (
ACPI_WALK_STATE *walk_state,
ACPI_PARSE_OBJECT *op)
{
#ifndef PARSER_ONLY
ACPI_PARSE_OBJECT *prev;
ACPI_PARSE_OBJECT *next;
ACPI_OPCODE_INFO *op_info;
ACPI_OPCODE_INFO *parent_info;
u32 opcode_class;
ACPI_PARSE_OBJECT *replacement_op = NULL;
op_info = acpi_ps_get_opcode_info (op->opcode);
opcode_class = ACPI_GET_OP_CLASS (op_info);
/* Delete this op and the subtree below it if asked to */
if (((walk_state->parse_flags & ACPI_PARSE_TREE_MASK) == ACPI_PARSE_DELETE_TREE) &&
(opcode_class != OPTYPE_CONSTANT) &&
(opcode_class != OPTYPE_LITERAL) &&
(opcode_class != OPTYPE_LOCAL_VARIABLE) &&
(opcode_class != OPTYPE_METHOD_ARGUMENT) &&
(opcode_class != OPTYPE_DATA_TERM) &&
(op->opcode != AML_NAMEPATH_OP)) {
/* Make sure that we only delete this subtree */
if (op->parent) {
/*
* Check if we need to replace the operator and its subtree
* with a return value op (placeholder op)
*/
parent_info = acpi_ps_get_opcode_info (op->parent->opcode);
switch (ACPI_GET_OP_CLASS (parent_info)) {
case OPTYPE_CONTROL: /* IF, ELSE, WHILE only */
break;
case OPTYPE_NAMED_OBJECT: /* Scope, method, etc. */
/*
* These opcodes contain Term_arg operands. The current
* op must be replace by a placeholder return op
*/
if ((op->parent->opcode == AML_REGION_OP) ||
(op->parent->opcode == AML_CREATE_FIELD_OP) ||
(op->parent->opcode == AML_BIT_FIELD_OP) ||
(op->parent->opcode == AML_BYTE_FIELD_OP) ||
(op->parent->opcode == AML_WORD_FIELD_OP) ||
(op->parent->opcode == AML_DWORD_FIELD_OP) ||
(op->parent->opcode == AML_QWORD_FIELD_OP)) {
replacement_op = acpi_ps_alloc_op (AML_RETURN_VALUE_OP);
if (!replacement_op) {
return (FALSE);
}
}
break;
default:
replacement_op = acpi_ps_alloc_op (AML_RETURN_VALUE_OP);
if (!replacement_op) {
return (FALSE);
}
}
/* We must unlink this op from the parent tree */
prev = op->parent->value.arg;
if (prev == op) {
/* This op is the first in the list */
if (replacement_op) {
replacement_op->parent = op->parent;
replacement_op->value.arg = NULL;
op->parent->value.arg = replacement_op;
replacement_op->next = op->next;
}
else {
op->parent->value.arg = op->next;
}
}
/* Search the parent list */
else while (prev) {
/* Traverse all siblings in the parent's argument list */
next = prev->next;
if (next == op) {
if (replacement_op) {
replacement_op->parent = op->parent;
replacement_op->value.arg = NULL;
prev->next = replacement_op;
replacement_op->next = op->next;
next = NULL;
}
else {
prev->next = op->next;
next = NULL;
}
}
prev = next;
}
}
/* Now we can actually delete the subtree rooted at op */
acpi_ps_delete_parse_tree (op);
return (TRUE);
}
return (FALSE);
#else
return (FALSE);
#endif
}
/*******************************************************************************
*
* FUNCTION: Acpi_ps_next_parse_state
*
* PARAMETERS: Parser_state - Current parser state object
*
* RETURN:
*
* DESCRIPTION:
*
******************************************************************************/
static ACPI_STATUS
acpi_ps_next_parse_state (
ACPI_WALK_STATE *walk_state,
ACPI_PARSE_OBJECT *op,
ACPI_STATUS callback_status)
{
ACPI_PARSE_STATE *parser_state = walk_state->parser_state;
ACPI_STATUS status = AE_CTRL_PENDING;
u8 *start;
u32 package_length;
switch (callback_status) {
case AE_CTRL_TERMINATE:
/*
* A control method was terminated via a RETURN statement.
* The walk of this method is complete.
*/
parser_state->aml = parser_state->aml_end;
status = AE_CTRL_TERMINATE;
break;
case AE_CTRL_PENDING:
/*
* Predicate of a WHILE was true and the loop just completed an
* execution. Go back to the start of the loop and reevaluate the
* predicate.
*/
/* Walk_state->Control_state->Common.State =
CONTROL_PREDICATE_EXECUTING;*/
/* TBD: How to handle a break within a while. */
/* This code attempts it */
parser_state->aml = walk_state->aml_last_while;
break;
case AE_CTRL_TRUE:
/*
* Predicate of an IF was true, and we are at the matching ELSE.
* Just close out this package
*
* Note: Parser_state->Aml is modified by the package length procedure
* TBD: [Investigate] perhaps it shouldn't, too much trouble
*/
start = parser_state->aml;
package_length = acpi_ps_get_next_package_length (parser_state);
parser_state->aml = start + package_length;
break;
case AE_CTRL_FALSE:
/*
* Either an IF/WHILE Predicate was false or we encountered a BREAK
* opcode. In both cases, we do not execute the rest of the
* package; We simply close out the parent (finishing the walk of
* this branch of the tree) and continue execution at the parent
* level.
*/
parser_state->aml = parser_state->scope->parse_scope.pkg_end;
/* In the case of a BREAK, just force a predicate (if any) to FALSE */
walk_state->control_state->common.value = FALSE;
status = AE_CTRL_END;
break;
case AE_CTRL_TRANSFER:
/*
* A method call (invocation) -- transfer control
*/
status = AE_CTRL_TRANSFER;
walk_state->prev_op = op;
walk_state->method_call_op = op;
walk_state->method_call_node = (op->value.arg)->node;
/* Will return value (if any) be used by the caller? */
walk_state->return_used = acpi_ds_is_result_used (op, walk_state);
break;
default:
status = callback_status;
if ((callback_status & AE_CODE_MASK) == AE_CODE_CONTROL) {
status = AE_OK;
}
break;
}
return (status);
}
/*******************************************************************************
*
* FUNCTION: Acpi_ps_parse_loop
*
* PARAMETERS: Parser_state - Current parser state object
*
* RETURN: Status
*
* DESCRIPTION: Parse AML (pointed to by the current parser state) and return
* a tree of ops.
*
******************************************************************************/
ACPI_STATUS
acpi_ps_parse_loop (
ACPI_WALK_STATE *walk_state)
{
ACPI_STATUS status = AE_OK;
ACPI_PARSE_OBJECT *op = NULL; /* current op */
ACPI_OPCODE_INFO *op_info;
ACPI_PARSE_OBJECT *arg = NULL;
ACPI_PARSE2_OBJECT *deferred_op;
u32 arg_count; /* push for fixed or var args */
u32 arg_types = 0;
ACPI_PTRDIFF aml_offset;
u16 opcode;
ACPI_PARSE_OBJECT pre_op;
ACPI_PARSE_STATE *parser_state;
u8 *aml_op_start;
parser_state = walk_state->parser_state;
#ifndef PARSER_ONLY
if (walk_state->walk_type & WALK_METHOD_RESTART) {
/* We are restarting a preempted control method */
if (acpi_ps_has_completed_scope (parser_state)) {
/*
* We must check if a predicate to an IF or WHILE statement
* was just completed
*/
if ((parser_state->scope->parse_scope.op) &&
((parser_state->scope->parse_scope.op->opcode == AML_IF_OP) ||
(parser_state->scope->parse_scope.op->opcode == AML_WHILE_OP)) &&
(walk_state->control_state) &&
(walk_state->control_state->common.state ==
CONTROL_PREDICATE_EXECUTING)) {
/*
* A predicate was just completed, get the value of the
* predicate and branch based on that value
*/
status = acpi_ds_get_predicate_value (walk_state, NULL, TRUE);
if (ACPI_FAILURE (status) &&
((status & AE_CODE_MASK) != AE_CODE_CONTROL)) {
return (status);
}
status = acpi_ps_next_parse_state (walk_state, op, status);
}
acpi_ps_pop_scope (parser_state, &op, &arg_types, &arg_count);
}
else if (walk_state->prev_op) {
/* We were in the middle of an op */
op = walk_state->prev_op;
arg_types = walk_state->prev_arg_types;
}
}
#endif
/*
* Iterative parsing loop, while there is more aml to process:
*/
while ((parser_state->aml < parser_state->aml_end) || (op)) {
if (!op) {
/* Get the next opcode from the AML stream */
aml_op_start = parser_state->aml;
aml_offset = parser_state->aml - parser_state->aml_start;
opcode = acpi_ps_peek_opcode (parser_state);
/*
* First cut to determine what we have found:
* 1) A valid AML opcode
* 2) A name string
* 3) An unknown/invalid opcode
*/
op_info = acpi_ps_get_opcode_info (opcode);
switch (ACPI_GET_OP_TYPE (op_info)) {
case ACPI_OP_TYPE_OPCODE:
/* Found opcode info, this is a normal opcode */
parser_state->aml += acpi_ps_get_opcode_size (opcode);
arg_types = op_info->parse_args;
break;
case ACPI_OP_TYPE_ASCII:
case ACPI_OP_TYPE_PREFIX:
/*
* Starts with a valid prefix or ASCII char, this is a name
* string. Convert the bare name string to a namepath.
*/
opcode = AML_NAMEPATH_OP;
arg_types = ARGP_NAMESTRING;
break;
case ACPI_OP_TYPE_UNKNOWN:
/* The opcode is unrecognized. Just skip unknown opcodes */
/* Assume one-byte bad opcode */
parser_state->aml++;
continue;
}
/* Create Op structure and append to parent's argument list */
if (acpi_ps_is_named_op (opcode)) {
pre_op.value.arg = NULL;
pre_op.opcode = opcode;
while (GET_CURRENT_ARG_TYPE (arg_types) != ARGP_NAME) {
arg = acpi_ps_get_next_arg (parser_state,
GET_CURRENT_ARG_TYPE (arg_types),
&arg_count);
acpi_ps_append_arg (&pre_op, arg);
INCREMENT_ARG_LIST (arg_types);
}
/* We know that this arg is a name, move to next arg */
INCREMENT_ARG_LIST (arg_types);
if (walk_state->descending_callback != NULL) {
/*
* Find the object. This will either insert the object into
* the namespace or simply look it up
*/
status = walk_state->descending_callback (opcode, NULL, walk_state, &op);
if (op == NULL) {
continue;
}
status = acpi_ps_next_parse_state (walk_state, op, status);
if (status == AE_CTRL_PENDING) {
status = AE_OK;
goto close_this_op;
}
if (ACPI_FAILURE (status)) {
goto close_this_op;
}
}
acpi_ps_append_arg (op, pre_op.value.arg);
acpi_gbl_depth++;
if (op->opcode == AML_REGION_OP) {
deferred_op = acpi_ps_to_extended_op (op);
if (deferred_op) {
/*
* Defer final parsing of an Operation_region body,
* because we don't have enough info in the first pass
* to parse it correctly (i.e., there may be method
* calls within the Term_arg elements of the body.
*
* However, we must continue parsing because
* the opregion is not a standalone package --
* we don't know where the end is at this point.
*
* (Length is unknown until parse of the body complete)
*/
deferred_op->data = aml_op_start;
deferred_op->length = 0;
}
}
}
else {
/* Not a named opcode, just allocate Op and append to parent */
op = acpi_ps_alloc_op (opcode);
if (!op) {
return (AE_NO_MEMORY);
}
if ((op->opcode == AML_CREATE_FIELD_OP) ||
(op->opcode == AML_BIT_FIELD_OP) ||
(op->opcode == AML_BYTE_FIELD_OP) ||
(op->opcode == AML_WORD_FIELD_OP) ||
(op->opcode == AML_DWORD_FIELD_OP)) {
/*
* Backup to beginning of Create_xXXfield declaration
* Body_length is unknown until we parse the body
*/
deferred_op = (ACPI_PARSE2_OBJECT *) op;
deferred_op->data = aml_op_start;
deferred_op->length = 0;
}
acpi_ps_append_arg (acpi_ps_get_parent_scope (parser_state), op);
if ((walk_state->descending_callback != NULL)) {
/*
* Find the object. This will either insert the object into
* the namespace or simply look it up
*/
status = walk_state->descending_callback (opcode, op, walk_state, &op);
status = acpi_ps_next_parse_state (walk_state, op, status);
if (status == AE_CTRL_PENDING) {
status = AE_OK;
goto close_this_op;
}
if (ACPI_FAILURE (status)) {
goto close_this_op;
}
}
}
op->aml_offset = aml_offset;
}
/* Start Arg_count at zero because we don't know if there are any args yet */
arg_count = 0;
if (arg_types) /* Are there any arguments that must be processed? */ {
/* get arguments */
switch (op->opcode) {
case AML_BYTE_OP: /* AML_BYTEDATA_ARG */
case AML_WORD_OP: /* AML_WORDDATA_ARG */
case AML_DWORD_OP: /* AML_DWORDATA_ARG */
case AML_STRING_OP: /* AML_ASCIICHARLIST_ARG */
/* fill in constant or string argument directly */
acpi_ps_get_next_simple_arg (parser_state,
GET_CURRENT_ARG_TYPE (arg_types), op);
break;
case AML_NAMEPATH_OP: /* AML_NAMESTRING_ARG */
acpi_ps_get_next_namepath (parser_state, op, &arg_count, 1);
arg_types = 0;
break;
default:
/* Op is not a constant or string, append each argument */
while (GET_CURRENT_ARG_TYPE (arg_types) && !arg_count) {
aml_offset = parser_state->aml - parser_state->aml_start;
arg = acpi_ps_get_next_arg (parser_state,
GET_CURRENT_ARG_TYPE (arg_types),
&arg_count);
if (arg) {
arg->aml_offset = aml_offset;
acpi_ps_append_arg (op, arg);
}
INCREMENT_ARG_LIST (arg_types);
}
/* For a method, save the length and address of the body */
if (op->opcode == AML_METHOD_OP) {
deferred_op = acpi_ps_to_extended_op (op);
if (deferred_op) {
/*
* Skip parsing of control method or opregion body,
* because we don't have enough info in the first pass
* to parse them correctly.
*/
deferred_op->data = parser_state->aml;
deferred_op->length = parser_state->pkg_end -
parser_state->aml;
/*
* Skip body of method. For Op_regions, we must continue
* parsing because the opregion is not a standalone
* package (We don't know where the end is).
*/
parser_state->aml = parser_state->pkg_end;
arg_count = 0;
}
}
break;
}
}
/*
* Zero Arg_count means that all arguments for this op have been processed
*/
if (!arg_count) {
/* completed Op, prepare for next */
if (acpi_ps_is_named_op (op->opcode)) {
if (acpi_gbl_depth) {
acpi_gbl_depth--;
}
if (op->opcode == AML_REGION_OP) {
deferred_op = acpi_ps_to_extended_op (op);
if (deferred_op) {
/*
* Skip parsing of control method or opregion body,
* because we don't have enough info in the first pass
* to parse them correctly.
*
* Completed parsing an Op_region declaration, we now
* know the length.
*/
deferred_op->length = parser_state->aml -
deferred_op->data;
}
}
}
if ((op->opcode == AML_CREATE_FIELD_OP) ||
(op->opcode == AML_BIT_FIELD_OP) ||
(op->opcode == AML_BYTE_FIELD_OP) ||
(op->opcode == AML_WORD_FIELD_OP) ||
(op->opcode == AML_DWORD_FIELD_OP) ||
(op->opcode == AML_QWORD_FIELD_OP)) {
/*
* Backup to beginning of Create_xXXfield declaration (1 for
* Opcode)
*
* Body_length is unknown until we parse the body
*/
deferred_op = (ACPI_PARSE2_OBJECT *) op;
deferred_op->length = parser_state->aml - deferred_op->data;
}
/* This op complete, notify the dispatcher */
if (walk_state->ascending_callback != NULL) {
status = walk_state->ascending_callback (walk_state, op);
status = acpi_ps_next_parse_state (walk_state, op, status);
if (status == AE_CTRL_PENDING) {
status = AE_OK;
goto close_this_op;
}
}
close_this_op:
/*
* Finished one argument of the containing scope
*/
parser_state->scope->parse_scope.arg_count--;
/* Close this Op (may result in parse subtree deletion) */
if (acpi_ps_complete_this_op (walk_state, op)) {
op = NULL;
}
switch (status) {
case AE_OK:
break;
case AE_CTRL_TRANSFER:
/*
* We are about to transfer to a called method.
*/
walk_state->prev_op = op;
walk_state->prev_arg_types = arg_types;
return (status);
break;
case AE_CTRL_END:
acpi_ps_pop_scope (parser_state, &op, &arg_types, &arg_count);
status = walk_state->ascending_callback (walk_state, op);
status = acpi_ps_next_parse_state (walk_state, op, status);
acpi_ps_complete_this_op (walk_state, op);
op = NULL;
status = AE_OK;
break;
case AE_CTRL_TERMINATE:
status = AE_OK;
/* Clean up */
do {
if (op) {
acpi_ps_complete_this_op (walk_state, op);
}
acpi_ps_pop_scope (parser_state, &op, &arg_types, &arg_count);
} while (op);
return (status);
break;
default: /* All other non-AE_OK status */
if (op == NULL) {
acpi_ps_pop_scope (parser_state, &op, &arg_types, &arg_count);
}
walk_state->prev_op = op;
walk_state->prev_arg_types = arg_types;
/*
* TEMP:
*/
return (status);
break;
}
/* This scope complete? */
if (acpi_ps_has_completed_scope (parser_state)) {
acpi_ps_pop_scope (parser_state, &op, &arg_types, &arg_count);
}
else {
op = NULL;
}
}
/* Arg_count is non-zero */
else {
/* complex argument, push Op and prepare for argument */
acpi_ps_push_scope (parser_state, op, arg_types, arg_count);
op = NULL;
}
} /* while Parser_state->Aml */
/*
* Complete the last Op (if not completed), and clear the scope stack.
* It is easily possible to end an AML "package" with an unbounded number
* of open scopes (such as when several AML blocks are closed with
* sequential closing braces). We want to terminate each one cleanly.
*/
do {
if (op) {
if (walk_state->ascending_callback != NULL) {
status = walk_state->ascending_callback (walk_state, op);
status = acpi_ps_next_parse_state (walk_state, op, status);
if (status == AE_CTRL_PENDING) {
status = AE_OK;
goto close_this_op;
}
if (status == AE_CTRL_TERMINATE) {
status = AE_OK;
/* Clean up */
do {
if (op) {
acpi_ps_complete_this_op (walk_state, op);
}
acpi_ps_pop_scope (parser_state, &op, &arg_types, &arg_count);
} while (op);
return (status);
}
else if (ACPI_FAILURE (status)) {
acpi_ps_complete_this_op (walk_state, op);
return (status);
}
}
acpi_ps_complete_this_op (walk_state, op);
}
acpi_ps_pop_scope (parser_state, &op, &arg_types, &arg_count);
} while (op);
return (status);
}
/*******************************************************************************
*
* FUNCTION: Acpi_ps_parse_aml
*
* PARAMETERS: Start_scope - The starting point of the parse. Becomes the
* root of the parsed op tree.
* Aml - Pointer to the raw AML code to parse
* Aml_size - Length of the AML to parse
*
* RETURN: Status
*
* DESCRIPTION: Parse raw AML and return a tree of ops
*
******************************************************************************/
ACPI_STATUS
acpi_ps_parse_aml (
ACPI_PARSE_OBJECT *start_scope,
u8 *aml,
u32 aml_size,
u32 parse_flags,
ACPI_NAMESPACE_NODE *method_node,
ACPI_OPERAND_OBJECT **params,
ACPI_OPERAND_OBJECT **caller_return_desc,
ACPI_PARSE_DOWNWARDS descending_callback,
ACPI_PARSE_UPWARDS ascending_callback)
{
ACPI_STATUS status;
ACPI_PARSE_STATE *parser_state;
ACPI_WALK_STATE *walk_state;
ACPI_WALK_LIST walk_list;
ACPI_NAMESPACE_NODE *node = NULL;
ACPI_WALK_LIST *prev_walk_list = acpi_gbl_current_walk_list;
ACPI_OPERAND_OBJECT *return_desc;
ACPI_OPERAND_OBJECT *mth_desc = NULL;
/* Create and initialize a new parser state */
parser_state = acpi_ps_create_state (aml, aml_size);
if (!parser_state) {
return (AE_NO_MEMORY);
}
acpi_ps_init_scope (parser_state, start_scope);
if (method_node) {
mth_desc = acpi_ns_get_attached_object (method_node);
}
/* Create and initialize a new walk list */
walk_list.walk_state = NULL;
walk_list.acquired_mutex_list.prev = NULL;
walk_list.acquired_mutex_list.next = NULL;
walk_state = acpi_ds_create_walk_state (TABLE_ID_DSDT, parser_state->start_op, mth_desc, &walk_list);
if (!walk_state) {
status = AE_NO_MEMORY;
goto cleanup;
}
walk_state->method_node = method_node;
walk_state->parser_state = parser_state;
walk_state->parse_flags = parse_flags;
walk_state->descending_callback = descending_callback;
walk_state->ascending_callback = ascending_callback;
/* TBD: [Restructure] TEMP until we pass Walk_state to the interpreter
*/
acpi_gbl_current_walk_list = &walk_list;
if (method_node) {
parser_state->start_node = method_node;
walk_state->walk_type = WALK_METHOD;
/* Push start scope on scope stack and make it current */
status = acpi_ds_scope_stack_push (method_node, ACPI_TYPE_METHOD, walk_state);
if (ACPI_FAILURE (status)) {
return (status);
}
/* Init arguments if this is a control method */
/* TBD: [Restructure] add walkstate as a param */
acpi_ds_method_data_init_args (params, MTH_NUM_ARGS, walk_state);
}
else {
/* Setup the current scope */
node = parser_state->start_op->node;
parser_state->start_node = node;
if (node) {
/* Push start scope on scope stack and make it current */
status = acpi_ds_scope_stack_push (node, node->type,
walk_state);
if (ACPI_FAILURE (status)) {
goto cleanup;
}
}
}
status = AE_OK;
/*
* Execute the walk loop as long as there is a valid Walk State. This
* handles nested control method invocations without recursion.
*/
while (walk_state) {
if (ACPI_SUCCESS (status)) {
status = acpi_ps_parse_loop (walk_state);
}
if (status == AE_CTRL_TRANSFER) {
/*
* A method call was detected.
* Transfer control to the called control method
*/
status = acpi_ds_call_control_method (&walk_list, walk_state, NULL);
/*
* If the transfer to the new method method call worked, a new walk
* state was created -- get it
*/
walk_state = acpi_ds_get_current_walk_state (&walk_list);
continue;
}
else if (status == AE_CTRL_TERMINATE) {
status = AE_OK;
}
/* We are done with this walk, move on to the parent if any */
walk_state = acpi_ds_pop_walk_state (&walk_list);
/* Extract return value before we delete Walk_state */
return_desc = walk_state->return_desc;
/* Reset the current scope to the beginning of scope stack */
acpi_ds_scope_stack_clear (walk_state);
/*
* If we just returned from the execution of a control method,
* there's lots of cleanup to do
*/
if ((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) == ACPI_PARSE_EXECUTE) {
acpi_ds_terminate_control_method (walk_state);
}
/* Delete this walk state and all linked control states */
acpi_ps_cleanup_scope (walk_state->parser_state);
acpi_cm_free (walk_state->parser_state);
acpi_ds_delete_walk_state (walk_state);
/* Check if we have restarted a preempted walk */
walk_state = acpi_ds_get_current_walk_state (&walk_list);
if (walk_state &&
ACPI_SUCCESS (status)) {
/* There is another walk state, restart it */
/*
* If the method returned value is not used by the parent,
* The object is deleted
*/
acpi_ds_restart_control_method (walk_state, return_desc);
walk_state->walk_type |= WALK_METHOD_RESTART;
}
/*
* Just completed a 1st-level method, save the final internal return
* value (if any)
*/
else if (caller_return_desc) {
*caller_return_desc = return_desc; /* NULL if no return value */
}
else if (return_desc) {
/* Caller doesn't want it, must delete it */
acpi_cm_remove_reference (return_desc);
}
}
/* Normal exit */
acpi_aml_release_all_mutexes ((ACPI_OPERAND_OBJECT *) &walk_list.acquired_mutex_list);
acpi_gbl_current_walk_list = prev_walk_list;
return (status);
cleanup:
/* Cleanup */
acpi_ds_delete_walk_state (walk_state);
acpi_ps_cleanup_scope (parser_state);
acpi_cm_free (parser_state);
acpi_aml_release_all_mutexes ((ACPI_OPERAND_OBJECT *)&walk_list.acquired_mutex_list);
acpi_gbl_current_walk_list = prev_walk_list;
return (status);
}