/****************************************************************************** * * Module Name: ammisc - ACPI AML (p-code) execution - specific opcodes * *****************************************************************************/ /* * Copyright (C) 2000 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 */ #include "acpi.h" #include "parser.h" #include "interp.h" #include "amlcode.h" #include "dispatch.h" #define _COMPONENT INTERPRETER MODULE_NAME ("ammisc"); /******************************************************************************* * * FUNCTION: Acpi_aml_exec_fatal * * PARAMETERS: none * * RETURN: Status. If the OS returns from the OSD call, we just keep * on going. * * DESCRIPTION: Execute Fatal operator * * ACPI SPECIFICATION REFERENCES: * Def_fatal := Fatal_op Fatal_type Fatal_code Fatal_arg * Fatal_type := Byte_data * Fatal_code := DWord_data * Fatal_arg := Term_arg=>Integer * ******************************************************************************/ ACPI_STATUS acpi_aml_exec_fatal ( ACPI_WALK_STATE *walk_state) { ACPI_OBJECT_INTERNAL *type_desc; ACPI_OBJECT_INTERNAL *code_desc; ACPI_OBJECT_INTERNAL *arg_desc; ACPI_STATUS status; /* Resolve operands */ status = acpi_aml_resolve_operands (AML_FATAL_OP, WALK_OPERANDS); /* Get operands */ status |= acpi_ds_obj_stack_pop_object (&arg_desc, walk_state); status |= acpi_ds_obj_stack_pop_object (&code_desc, walk_state); status |= acpi_ds_obj_stack_pop_object (&type_desc, walk_state); if (status != AE_OK) { /* invalid parameters on object stack */ acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, (u16) AML_FATAL_OP, WALK_OPERANDS, 3); goto cleanup; } /* Def_fatal := Fatal_op Fatal_type Fatal_code Fatal_arg */ /* * TBD: [Unhandled] call OSD interface to notify OS of fatal error * requiring shutdown! */ cleanup: /* Free the operands */ acpi_cm_remove_reference (arg_desc); acpi_cm_remove_reference (code_desc); acpi_cm_remove_reference (type_desc); /* If we get back from the OS call, we might as well keep going. */ return (AE_OK); } /******************************************************************************* * * FUNCTION: Acpi_aml_exec_index * * PARAMETERS: none * * RETURN: Status * * DESCRIPTION: Execute Index operator * * ALLOCATION: Deletes one operand descriptor -- other remains on stack * * ACPI SPECIFICATION REFERENCES: * Def_index := Index_op Buff_pkg_obj Index_value Result * Index_value := Term_arg=>Integer * Name_string := | * Result := Super_name * Super_name := Name_string | Arg_obj | Local_obj | Debug_obj | Def_index * Local4_op | Local5_op | Local6_op | Local7_op * ******************************************************************************/ ACPI_STATUS acpi_aml_exec_index ( ACPI_WALK_STATE *walk_state, ACPI_OBJECT_INTERNAL **return_desc) { ACPI_OBJECT_INTERNAL *obj_desc; ACPI_OBJECT_INTERNAL *idx_desc; ACPI_OBJECT_INTERNAL *res_desc; ACPI_OBJECT_INTERNAL *ret_desc = NULL; ACPI_OBJECT_INTERNAL *tmp_desc; ACPI_STATUS status; /* Resolve operands */ /* First operand can be either a package or a buffer */ status = acpi_aml_resolve_operands (AML_INDEX_OP, WALK_OPERANDS); /* Get all operands */ status |= acpi_ds_obj_stack_pop_object (&res_desc, walk_state); status |= acpi_ds_obj_stack_pop_object (&idx_desc, walk_state); status |= acpi_ds_obj_stack_pop_object (&obj_desc, walk_state); if (status != AE_OK) { /* Invalid parameters on object stack */ acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, (u16) AML_INDEX_OP, WALK_OPERANDS, 3); goto cleanup; } /* Create the internal return object */ ret_desc = acpi_cm_create_internal_object (INTERNAL_TYPE_REFERENCE); if (!ret_desc) { status = AE_NO_MEMORY; goto cleanup; } /* * At this point, the Obj_desc operand is either a Package or a Buffer */ if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { /* Object to be indexed is a Package */ if (idx_desc->number.value >= obj_desc->package.count) { status = AE_AML_PACKAGE_LIMIT; goto cleanup; } if ((res_desc->common.type == INTERNAL_TYPE_REFERENCE) && (res_desc->reference.op_code == AML_ZERO_OP)) { /* * There is no actual result descriptor (the Zero_op Result * descriptor is a placeholder), so just delete the placeholder and * return a reference to the package element */ acpi_cm_remove_reference (res_desc); } else { /* * Each element of the package is an internal object. Get the one * we are after. */ tmp_desc = obj_desc->package.elements[idx_desc->number.value]; ret_desc->reference.op_code = AML_INDEX_OP; ret_desc->reference.target_type = tmp_desc->common.type; ret_desc->reference.object = tmp_desc; status = acpi_aml_exec_store (ret_desc, res_desc); ret_desc->reference.object = NULL; } /* * The local return object must always be a reference to the package element, * not the element itself. */ ret_desc->reference.op_code = AML_INDEX_OP; ret_desc->reference.target_type = ACPI_TYPE_PACKAGE; ret_desc->reference.where = &obj_desc->package.elements[idx_desc->number.value]; } else { /* Object to be indexed is a Buffer */ if (idx_desc->number.value >= obj_desc->buffer.length) { status = AE_AML_BUFFER_LIMIT; goto cleanup; } ret_desc->reference.op_code = AML_INDEX_OP; ret_desc->reference.target_type = ACPI_TYPE_BUFFER_FIELD; ret_desc->reference.object = obj_desc; ret_desc->reference.offset = idx_desc->number.value; status = acpi_aml_exec_store (ret_desc, res_desc); } cleanup: /* Always delete operands */ acpi_cm_remove_reference (obj_desc); acpi_cm_remove_reference (idx_desc); /* Delete return object on error */ if (ACPI_FAILURE (status)) { acpi_cm_remove_reference (res_desc); if (ret_desc) { acpi_cm_remove_reference (ret_desc); ret_desc = NULL; } } /* Set the return object and exit */ *return_desc = ret_desc; return (status); } /******************************************************************************* * * FUNCTION: Acpi_aml_exec_match * * PARAMETERS: none * * RETURN: Status * * DESCRIPTION: Execute Match operator * * ACPI SPECIFICATION REFERENCES: * Def_match := Match_op Search_pkg Opcode1 Operand1 * Opcode2 Operand2 Start_index * Opcode1 := Byte_data: MTR, MEQ, MLE, MLT, MGE, or MGT * Opcode2 := Byte_data: MTR, MEQ, MLE, MLT, MGE, or MGT * Operand1 := Term_arg=>Integer * Operand2 := Term_arg=>Integer * Search_pkg := Term_arg=>Package_object * Start_index := Term_arg=>Integer * ******************************************************************************/ ACPI_STATUS acpi_aml_exec_match ( ACPI_WALK_STATE *walk_state, ACPI_OBJECT_INTERNAL **return_desc) { ACPI_OBJECT_INTERNAL *pkg_desc; ACPI_OBJECT_INTERNAL *op1_desc; ACPI_OBJECT_INTERNAL *V1_desc; ACPI_OBJECT_INTERNAL *op2_desc; ACPI_OBJECT_INTERNAL *V2_desc; ACPI_OBJECT_INTERNAL *start_desc; ACPI_OBJECT_INTERNAL *ret_desc = NULL; ACPI_STATUS status; u32 index; u32 match_value = (u32) -1; /* Resolve all operands */ status = acpi_aml_resolve_operands (AML_MATCH_OP, WALK_OPERANDS); /* Get all operands */ status |= acpi_ds_obj_stack_pop_object (&start_desc, walk_state); status |= acpi_ds_obj_stack_pop_object (&V2_desc, walk_state); status |= acpi_ds_obj_stack_pop_object (&op2_desc, walk_state); status |= acpi_ds_obj_stack_pop_object (&V1_desc, walk_state); status |= acpi_ds_obj_stack_pop_object (&op1_desc, walk_state); status |= acpi_ds_obj_stack_pop_object (&pkg_desc, walk_state); if (status != AE_OK) { /* Invalid parameters on object stack */ acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, (u16) AML_MATCH_OP, WALK_OPERANDS, 6); goto cleanup; } /* Validate match comparison sub-opcodes */ if ((op1_desc->number.value > MAX_MATCH_OPERATOR) || (op2_desc->number.value > MAX_MATCH_OPERATOR)) { status = AE_AML_OPERAND_VALUE; goto cleanup; } index = start_desc->number.value; if (index >= (u32) pkg_desc->package.count) { status = AE_AML_PACKAGE_LIMIT; goto cleanup; } ret_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); if (!ret_desc) { status = AE_NO_MEMORY; goto cleanup; } /* * Examine each element until a match is found. Within the loop, * "continue" signifies that the current element does not match * and the next should be examined. * Upon finding a match, the loop will terminate via "break" at * the bottom. If it terminates "normally", Match_value will be -1 * (its initial value) indicating that no match was found. When * returned as a Number, this will produce the Ones value as specified. */ for ( ; index < pkg_desc->package.count; ++index) { /* * Treat any NULL or non-numeric elements as non-matching. * TBD [Unhandled] - if an element is a Name, * should we examine its value? */ if (!pkg_desc->package.elements[index] || ACPI_TYPE_NUMBER != pkg_desc->package.elements[index]->common.type) { continue; } /* * Within these switch statements: * "break" (exit from the switch) signifies a match; * "continue" (proceed to next iteration of enclosing * "for" loop) signifies a non-match. */ switch (op1_desc->number.value) { case MATCH_MTR: /* always true */ break; case MATCH_MEQ: /* true if equal */ if (pkg_desc->package.elements[index]->number.value != V1_desc->number.value) { continue; } break; case MATCH_MLE: /* true if less than or equal */ if (pkg_desc->package.elements[index]->number.value > V1_desc->number.value) { continue; } break; case MATCH_MLT: /* true if less than */ if (pkg_desc->package.elements[index]->number.value >= V1_desc->number.value) { continue; } break; case MATCH_MGE: /* true if greater than or equal */ if (pkg_desc->package.elements[index]->number.value < V1_desc->number.value) { continue; } break; case MATCH_MGT: /* true if greater than */ if (pkg_desc->package.elements[index]->number.value <= V1_desc->number.value) { continue; } break; default: /* undefined */ continue; } switch(op2_desc->number.value) { case MATCH_MTR: break; case MATCH_MEQ: if (pkg_desc->package.elements[index]->number.value != V2_desc->number.value) { continue; } break; case MATCH_MLE: if (pkg_desc->package.elements[index]->number.value > V2_desc->number.value) { continue; } break; case MATCH_MLT: if (pkg_desc->package.elements[index]->number.value >= V2_desc->number.value) { continue; } break; case MATCH_MGE: if (pkg_desc->package.elements[index]->number.value < V2_desc->number.value) { continue; } break; case MATCH_MGT: if (pkg_desc->package.elements[index]->number.value <= V2_desc->number.value) { continue; } break; default: continue; } /* Match found: exit from loop */ match_value = index; break; } /* Match_value is the return value */ ret_desc->number.value = match_value; cleanup: /* Free the operands */ acpi_cm_remove_reference (start_desc); acpi_cm_remove_reference (V2_desc); acpi_cm_remove_reference (op2_desc); acpi_cm_remove_reference (V1_desc); acpi_cm_remove_reference (op1_desc); acpi_cm_remove_reference (pkg_desc); /* Delete return object on error */ if (ACPI_FAILURE (status) && (ret_desc)) { acpi_cm_remove_reference (ret_desc); ret_desc = NULL; } /* Set the return object and exit */ *return_desc = ret_desc; return (status); }