1304 lines · 34.6 KB · C
RawDownload
/****************************************************************************** * * Module Name: psparse - Parser top level AML parse routines * *****************************************************************************/ /* * Copyright (C) 2000 - 2005, R. Byron Moore * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions, and the following disclaimer, *    without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer *    substantially similar to the "NO WARRANTY" disclaimer below *    ("Disclaimer") and any redistribution must be conditioned upon *    including a substantially similar Disclaimer requirement for further *    binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names *    of any contributors may be used to endorse or promote products derived *    from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. */  /* * 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/acpi.h>#include <acpi/acparser.h>#include <acpi/acdispat.h>#include <acpi/amlcode.h>#include <acpi/acnamesp.h>#include <acpi/acinterp.h> #define _COMPONENT          ACPI_PARSER	 ACPI_MODULE_NAME    ("psparse")  static u32                          acpi_gbl_depth = 0; /* Local prototypes */ static voidacpi_ps_complete_this_op (	struct acpi_walk_state          *walk_state,	union acpi_parse_object         *op); static acpi_statusacpi_ps_next_parse_state (	struct acpi_walk_state          *walk_state,	union acpi_parse_object         *op,	acpi_status                     callback_status); static acpi_statusacpi_ps_parse_loop (	struct acpi_walk_state          *walk_state);  /******************************************************************************* * * FUNCTION:    acpi_ps_get_opcode_size * * PARAMETERS:  Opcode          - An AML opcode * * RETURN:      Size of the opcode, in bytes (1 or 2) * * DESCRIPTION: Get the size of the current opcode. * ******************************************************************************/ u32acpi_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:      Next AML opcode * * DESCRIPTION: Get next AML opcode (without incrementing AML pointer) * ******************************************************************************/ u16acpi_ps_peek_opcode (	struct acpi_parse_state         *parser_state){	u8                              *aml;	u16                             opcode;  	aml = parser_state->aml;	opcode = (u16) ACPI_GET8 (aml); 	if (opcode == AML_EXTOP) {		/* Extended opcode */ 		aml++;		opcode = (u16) ((opcode << 8) | ACPI_GET8 (aml));	} 	return (opcode);}  /******************************************************************************* * * FUNCTION:    acpi_ps_complete_this_op * * PARAMETERS:  walk_state      - Current State *              Op              - Op to complete * * RETURN:      None. * * DESCRIPTION: Perform any cleanup at the completion of an Op. * ******************************************************************************/ static voidacpi_ps_complete_this_op (	struct acpi_walk_state          *walk_state,	union acpi_parse_object         *op){	union acpi_parse_object         *prev;	union acpi_parse_object         *next;	const struct acpi_opcode_info   *parent_info;	union acpi_parse_object         *replacement_op = NULL;  	ACPI_FUNCTION_TRACE_PTR ("ps_complete_this_op", op);  	/* Check for null Op, can happen if AML code is corrupt */ 	if (!op) {		return_VOID;	} 	/* Delete this op and the subtree below it if asked to */ 	if (((walk_state->parse_flags & ACPI_PARSE_TREE_MASK) != ACPI_PARSE_DELETE_TREE) ||		 (walk_state->op_info->class == AML_CLASS_ARGUMENT)) {		return_VOID;	} 	/* Make sure that we only delete this subtree */ 	if (op->common.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->common.parent->common.aml_opcode); 		switch (parent_info->class) {		case AML_CLASS_CONTROL:			break; 		case AML_CLASS_CREATE: 			/*			 * These opcodes contain term_arg operands. The current			 * op must be replaced by a placeholder return op			 */			replacement_op = acpi_ps_alloc_op (AML_INT_RETURN_VALUE_OP);			if (!replacement_op) {				goto cleanup;			}			break; 		case AML_CLASS_NAMED_OBJECT: 			/*			 * These opcodes contain term_arg operands. The current			 * op must be replaced by a placeholder return op			 */			if ((op->common.parent->common.aml_opcode == AML_REGION_OP)      ||				(op->common.parent->common.aml_opcode == AML_DATA_REGION_OP) ||				(op->common.parent->common.aml_opcode == AML_BUFFER_OP)      ||				(op->common.parent->common.aml_opcode == AML_PACKAGE_OP)     ||				(op->common.parent->common.aml_opcode == AML_VAR_PACKAGE_OP)) {				replacement_op = acpi_ps_alloc_op (AML_INT_RETURN_VALUE_OP);				if (!replacement_op) {					goto cleanup;				}			} 			if ((op->common.parent->common.aml_opcode == AML_NAME_OP) &&				(walk_state->descending_callback != acpi_ds_exec_begin_op)) {				if ((op->common.aml_opcode == AML_BUFFER_OP) ||					(op->common.aml_opcode == AML_PACKAGE_OP) ||					(op->common.aml_opcode == AML_VAR_PACKAGE_OP)) {					replacement_op = acpi_ps_alloc_op (op->common.aml_opcode);					if (!replacement_op) {						goto cleanup;					} 					replacement_op->named.data = op->named.data;					replacement_op->named.length = op->named.length;				}			}			break; 		default:			replacement_op = acpi_ps_alloc_op (AML_INT_RETURN_VALUE_OP);			if (!replacement_op) {				goto cleanup;			}		} 		/* We must unlink this op from the parent tree */ 		prev = op->common.parent->common.value.arg;		if (prev == op) {			/* This op is the first in the list */ 			if (replacement_op) {				replacement_op->common.parent       = op->common.parent;				replacement_op->common.value.arg    = NULL;				replacement_op->common.node         = op->common.node;				op->common.parent->common.value.arg = replacement_op;				replacement_op->common.next         = op->common.next;			}			else {				op->common.parent->common.value.arg = op->common.next;			}		} 		/* Search the parent list */ 		else while (prev) {			/* Traverse all siblings in the parent's argument list */ 			next = prev->common.next;			if (next == op) {				if (replacement_op) {					replacement_op->common.parent   = op->common.parent;					replacement_op->common.value.arg = NULL;					replacement_op->common.node     = op->common.node;					prev->common.next               = replacement_op;					replacement_op->common.next     = op->common.next;					next = NULL;				}				else {					prev->common.next = op->common.next;					next = NULL;				}			}			prev = next;		}	}  cleanup: 	/* Now we can actually delete the subtree rooted at Op */ 	acpi_ps_delete_parse_tree (op);	return_VOID;}  /******************************************************************************* * * FUNCTION:    acpi_ps_next_parse_state * * PARAMETERS:  walk_state          - Current state *              Op                  - Current parse op *              callback_status     - Status from previous operation * * RETURN:      Status * * DESCRIPTION: Update the parser state based upon the return exception from *              the parser callback. * ******************************************************************************/ static acpi_statusacpi_ps_next_parse_state (	struct acpi_walk_state          *walk_state,	union acpi_parse_object         *op,	acpi_status                     callback_status){	struct acpi_parse_state         *parser_state = &walk_state->parser_state;	acpi_status                     status = AE_CTRL_PENDING;  	ACPI_FUNCTION_TRACE_PTR ("ps_next_parse_state", op);  	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_BREAK: 		parser_state->aml = walk_state->aml_last_while;		walk_state->control_state->common.value = FALSE;		status = AE_CTRL_BREAK;		break; 	case AE_CTRL_CONTINUE:  		parser_state->aml = walk_state->aml_last_while;		status = AE_CTRL_CONTINUE;		break; 	case AE_CTRL_PENDING: 		parser_state->aml = walk_state->aml_last_while;		break; #if 0	case AE_CTRL_SKIP: 		parser_state->aml = parser_state->scope->parse_scope.pkg_end;		status = AE_OK;		break;#endif 	case AE_CTRL_TRUE: 		/*		 * Predicate of an IF was true, and we are at the matching ELSE.		 * Just close out this package		 */		parser_state->aml = acpi_ps_get_next_package_end (parser_state);		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->common.value.arg)->common.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_ACPI_STATUS (status);}  /******************************************************************************* * * FUNCTION:    acpi_ps_parse_loop * * PARAMETERS:  walk_state          - Current state * * RETURN:      Status * * DESCRIPTION: Parse AML (pointed to by the current parser state) and return *              a tree of ops. * ******************************************************************************/ static acpi_statusacpi_ps_parse_loop (	struct acpi_walk_state          *walk_state){	acpi_status                     status = AE_OK;	union acpi_parse_object         *op = NULL;     /* current op */	union acpi_parse_object         *arg = NULL;	union acpi_parse_object         *pre_op = NULL;	struct acpi_parse_state         *parser_state;	u8                              *aml_op_start = NULL;  	ACPI_FUNCTION_TRACE_PTR ("ps_parse_loop", walk_state); 	if (walk_state->descending_callback == NULL) {		return_ACPI_STATUS (AE_BAD_PARAMETER);	} 	parser_state = &walk_state->parser_state;	walk_state->arg_types = 0; #if (!defined (ACPI_NO_METHOD_EXECUTION) && !defined (ACPI_CONSTANT_EVAL_ONLY)) 	if (walk_state->walk_type & ACPI_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->common.aml_opcode == AML_IF_OP) ||				(parser_state->scope->parse_scope.op->common.aml_opcode == AML_WHILE_OP)) &&				(walk_state->control_state) &&				(walk_state->control_state->common.state ==					ACPI_CONTROL_PREDICATE_EXECUTING)) {				/*				 * A predicate was just completed, get the value of the				 * predicate and branch based on that value				 */				walk_state->op = NULL;				status = acpi_ds_get_predicate_value (walk_state, ACPI_TO_POINTER (TRUE));				if (ACPI_FAILURE (status) &&					((status & AE_CODE_MASK) != AE_CODE_CONTROL)) {					if (status == AE_AML_NO_RETURN_VALUE) {						ACPI_DEBUG_PRINT ((ACPI_DB_ERROR,							"Invoked method did not return a value, %s\n",							acpi_format_exception (status))); 					}					ACPI_DEBUG_PRINT ((ACPI_DB_ERROR,						"get_predicate Failed, %s\n",						acpi_format_exception (status)));					return_ACPI_STATUS (status);				} 				status = acpi_ps_next_parse_state (walk_state, op, status);			} 

Lines 1–500 of 1304.