linux/drivers/acpi/acpica/dscontrol.c
<<
>>
Prefs
   1/******************************************************************************
   2 *
   3 * Module Name: dscontrol - Support for execution control opcodes -
   4 *                          if/else/while/return
   5 *
   6 *****************************************************************************/
   7
   8/*
   9 * Copyright (C) 2000 - 2013, Intel Corp.
  10 * All rights reserved.
  11 *
  12 * Redistribution and use in source and binary forms, with or without
  13 * modification, are permitted provided that the following conditions
  14 * are met:
  15 * 1. Redistributions of source code must retain the above copyright
  16 *    notice, this list of conditions, and the following disclaimer,
  17 *    without modification.
  18 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
  19 *    substantially similar to the "NO WARRANTY" disclaimer below
  20 *    ("Disclaimer") and any redistribution must be conditioned upon
  21 *    including a substantially similar Disclaimer requirement for further
  22 *    binary redistribution.
  23 * 3. Neither the names of the above-listed copyright holders nor the names
  24 *    of any contributors may be used to endorse or promote products derived
  25 *    from this software without specific prior written permission.
  26 *
  27 * Alternatively, this software may be distributed under the terms of the
  28 * GNU General Public License ("GPL") version 2 as published by the Free
  29 * Software Foundation.
  30 *
  31 * NO WARRANTY
  32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  33 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  34 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
  35 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  36 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
  41 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  42 * POSSIBILITY OF SUCH DAMAGES.
  43 */
  44
  45#include <acpi/acpi.h>
  46#include "accommon.h"
  47#include "amlcode.h"
  48#include "acdispat.h"
  49#include "acinterp.h"
  50
  51#define _COMPONENT          ACPI_DISPATCHER
  52ACPI_MODULE_NAME("dscontrol")
  53
  54/*******************************************************************************
  55 *
  56 * FUNCTION:    acpi_ds_exec_begin_control_op
  57 *
  58 * PARAMETERS:  walk_list       - The list that owns the walk stack
  59 *              op              - The control Op
  60 *
  61 * RETURN:      Status
  62 *
  63 * DESCRIPTION: Handles all control ops encountered during control method
  64 *              execution.
  65 *
  66 ******************************************************************************/
  67acpi_status
  68acpi_ds_exec_begin_control_op(struct acpi_walk_state *walk_state,
  69                              union acpi_parse_object *op)
  70{
  71        acpi_status status = AE_OK;
  72        union acpi_generic_state *control_state;
  73
  74        ACPI_FUNCTION_NAME(ds_exec_begin_control_op);
  75
  76        ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Op=%p Opcode=%2.2X State=%p\n",
  77                          op, op->common.aml_opcode, walk_state));
  78
  79        switch (op->common.aml_opcode) {
  80        case AML_WHILE_OP:
  81
  82                /*
  83                 * If this is an additional iteration of a while loop, continue.
  84                 * There is no need to allocate a new control state.
  85                 */
  86                if (walk_state->control_state) {
  87                        if (walk_state->control_state->control.
  88                            aml_predicate_start ==
  89                            (walk_state->parser_state.aml - 1)) {
  90
  91                                /* Reset the state to start-of-loop */
  92
  93                                walk_state->control_state->common.state =
  94                                    ACPI_CONTROL_CONDITIONAL_EXECUTING;
  95                                break;
  96                        }
  97                }
  98
  99                /*lint -fallthrough */
 100
 101        case AML_IF_OP:
 102
 103                /*
 104                 * IF/WHILE: Create a new control state to manage these
 105                 * constructs. We need to manage these as a stack, in order
 106                 * to handle nesting.
 107                 */
 108                control_state = acpi_ut_create_control_state();
 109                if (!control_state) {
 110                        status = AE_NO_MEMORY;
 111                        break;
 112                }
 113                /*
 114                 * Save a pointer to the predicate for multiple executions
 115                 * of a loop
 116                 */
 117                control_state->control.aml_predicate_start =
 118                    walk_state->parser_state.aml - 1;
 119                control_state->control.package_end =
 120                    walk_state->parser_state.pkg_end;
 121                control_state->control.opcode = op->common.aml_opcode;
 122
 123                /* Push the control state on this walk's control stack */
 124
 125                acpi_ut_push_generic_state(&walk_state->control_state,
 126                                           control_state);
 127                break;
 128
 129        case AML_ELSE_OP:
 130
 131                /* Predicate is in the state object */
 132                /* If predicate is true, the IF was executed, ignore ELSE part */
 133
 134                if (walk_state->last_predicate) {
 135                        status = AE_CTRL_TRUE;
 136                }
 137
 138                break;
 139
 140        case AML_RETURN_OP:
 141
 142                break;
 143
 144        default:
 145                break;
 146        }
 147
 148        return (status);
 149}
 150
 151/*******************************************************************************
 152 *
 153 * FUNCTION:    acpi_ds_exec_end_control_op
 154 *
 155 * PARAMETERS:  walk_list       - The list that owns the walk stack
 156 *              op              - The control Op
 157 *
 158 * RETURN:      Status
 159 *
 160 * DESCRIPTION: Handles all control ops encountered during control method
 161 *              execution.
 162 *
 163 ******************************************************************************/
 164
 165acpi_status
 166acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state,
 167                            union acpi_parse_object * op)
 168{
 169        acpi_status status = AE_OK;
 170        union acpi_generic_state *control_state;
 171
 172        ACPI_FUNCTION_NAME(ds_exec_end_control_op);
 173
 174        switch (op->common.aml_opcode) {
 175        case AML_IF_OP:
 176
 177                ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "[IF_OP] Op=%p\n", op));
 178
 179                /*
 180                 * Save the result of the predicate in case there is an
 181                 * ELSE to come
 182                 */
 183                walk_state->last_predicate =
 184                    (u8)walk_state->control_state->common.value;
 185
 186                /*
 187                 * Pop the control state that was created at the start
 188                 * of the IF and free it
 189                 */
 190                control_state =
 191                    acpi_ut_pop_generic_state(&walk_state->control_state);
 192                acpi_ut_delete_generic_state(control_state);
 193                break;
 194
 195        case AML_ELSE_OP:
 196
 197                break;
 198
 199        case AML_WHILE_OP:
 200
 201                ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "[WHILE_OP] Op=%p\n", op));
 202
 203                control_state = walk_state->control_state;
 204                if (control_state->common.value) {
 205
 206                        /* Predicate was true, the body of the loop was just executed */
 207
 208                        /*
 209                         * This loop counter mechanism allows the interpreter to escape
 210                         * possibly infinite loops. This can occur in poorly written AML
 211                         * when the hardware does not respond within a while loop and the
 212                         * loop does not implement a timeout.
 213                         */
 214                        control_state->control.loop_count++;
 215                        if (control_state->control.loop_count >
 216                            ACPI_MAX_LOOP_ITERATIONS) {
 217                                status = AE_AML_INFINITE_LOOP;
 218                                break;
 219                        }
 220
 221                        /*
 222                         * Go back and evaluate the predicate and maybe execute the loop
 223                         * another time
 224                         */
 225                        status = AE_CTRL_PENDING;
 226                        walk_state->aml_last_while =
 227                            control_state->control.aml_predicate_start;
 228                        break;
 229                }
 230
 231                /* Predicate was false, terminate this while loop */
 232
 233                ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
 234                                  "[WHILE_OP] termination! Op=%p\n", op));
 235
 236                /* Pop this control state and free it */
 237
 238                control_state =
 239                    acpi_ut_pop_generic_state(&walk_state->control_state);
 240                acpi_ut_delete_generic_state(control_state);
 241                break;
 242
 243        case AML_RETURN_OP:
 244
 245                ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
 246                                  "[RETURN_OP] Op=%p Arg=%p\n", op,
 247                                  op->common.value.arg));
 248
 249                /*
 250                 * One optional operand -- the return value
 251                 * It can be either an immediate operand or a result that
 252                 * has been bubbled up the tree
 253                 */
 254                if (op->common.value.arg) {
 255
 256                        /* Since we have a real Return(), delete any implicit return */
 257
 258                        acpi_ds_clear_implicit_return(walk_state);
 259
 260                        /* Return statement has an immediate operand */
 261
 262                        status =
 263                            acpi_ds_create_operands(walk_state,
 264                                                    op->common.value.arg);
 265                        if (ACPI_FAILURE(status)) {
 266                                return (status);
 267                        }
 268
 269                        /*
 270                         * If value being returned is a Reference (such as
 271                         * an arg or local), resolve it now because it may
 272                         * cease to exist at the end of the method.
 273                         */
 274                        status =
 275                            acpi_ex_resolve_to_value(&walk_state->operands[0],
 276                                                     walk_state);
 277                        if (ACPI_FAILURE(status)) {
 278                                return (status);
 279                        }
 280
 281                        /*
 282                         * Get the return value and save as the last result
 283                         * value. This is the only place where walk_state->return_desc
 284                         * is set to anything other than zero!
 285                         */
 286                        walk_state->return_desc = walk_state->operands[0];
 287                } else if (walk_state->result_count) {
 288
 289                        /* Since we have a real Return(), delete any implicit return */
 290
 291                        acpi_ds_clear_implicit_return(walk_state);
 292
 293                        /*
 294                         * The return value has come from a previous calculation.
 295                         *
 296                         * If value being returned is a Reference (such as
 297                         * an arg or local), resolve it now because it may
 298                         * cease to exist at the end of the method.
 299                         *
 300                         * Allow references created by the Index operator to return
 301                         * unchanged.
 302                         */
 303                        if ((ACPI_GET_DESCRIPTOR_TYPE
 304                             (walk_state->results->results.obj_desc[0]) ==
 305                             ACPI_DESC_TYPE_OPERAND)
 306                            && ((walk_state->results->results.obj_desc[0])->
 307                                common.type == ACPI_TYPE_LOCAL_REFERENCE)
 308                            && ((walk_state->results->results.obj_desc[0])->
 309                                reference.class != ACPI_REFCLASS_INDEX)) {
 310                                status =
 311                                    acpi_ex_resolve_to_value(&walk_state->
 312                                                             results->results.
 313                                                             obj_desc[0],
 314                                                             walk_state);
 315                                if (ACPI_FAILURE(status)) {
 316                                        return (status);
 317                                }
 318                        }
 319
 320                        walk_state->return_desc =
 321                            walk_state->results->results.obj_desc[0];
 322                } else {
 323                        /* No return operand */
 324
 325                        if (walk_state->num_operands) {
 326                                acpi_ut_remove_reference(walk_state->
 327                                                         operands[0]);
 328                        }
 329
 330                        walk_state->operands[0] = NULL;
 331                        walk_state->num_operands = 0;
 332                        walk_state->return_desc = NULL;
 333                }
 334
 335                ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
 336                                  "Completed RETURN_OP State=%p, RetVal=%p\n",
 337                                  walk_state, walk_state->return_desc));
 338
 339                /* End the control method execution right now */
 340
 341                status = AE_CTRL_TERMINATE;
 342                break;
 343
 344        case AML_NOOP_OP:
 345
 346                /* Just do nothing! */
 347                break;
 348
 349        case AML_BREAK_POINT_OP:
 350
 351                /*
 352                 * Set the single-step flag. This will cause the debugger (if present)
 353                 * to break to the console within the AML debugger at the start of the
 354                 * next AML instruction.
 355                 */
 356                ACPI_DEBUGGER_EXEC(acpi_gbl_cm_single_step = TRUE);
 357                ACPI_DEBUGGER_EXEC(acpi_os_printf
 358                                   ("**break** Executed AML BreakPoint opcode\n"));
 359
 360                /* Call to the OSL in case OS wants a piece of the action */
 361
 362                status = acpi_os_signal(ACPI_SIGNAL_BREAKPOINT,
 363                                        "Executed AML Breakpoint opcode");
 364                break;
 365
 366        case AML_BREAK_OP:
 367        case AML_CONTINUE_OP:   /* ACPI 2.0 */
 368
 369                /* Pop and delete control states until we find a while */
 370
 371                while (walk_state->control_state &&
 372                       (walk_state->control_state->control.opcode !=
 373                        AML_WHILE_OP)) {
 374                        control_state =
 375                            acpi_ut_pop_generic_state(&walk_state->
 376                                                      control_state);
 377                        acpi_ut_delete_generic_state(control_state);
 378                }
 379
 380                /* No while found? */
 381
 382                if (!walk_state->control_state) {
 383                        return (AE_AML_NO_WHILE);
 384                }
 385
 386                /* Was: walk_state->aml_last_while = walk_state->control_state->Control.aml_predicate_start; */
 387
 388                walk_state->aml_last_while =
 389                    walk_state->control_state->control.package_end;
 390
 391                /* Return status depending on opcode */
 392
 393                if (op->common.aml_opcode == AML_BREAK_OP) {
 394                        status = AE_CTRL_BREAK;
 395                } else {
 396                        status = AE_CTRL_CONTINUE;
 397                }
 398                break;
 399
 400        default:
 401
 402                ACPI_ERROR((AE_INFO, "Unknown control opcode=0x%X Op=%p",
 403                            op->common.aml_opcode, op));
 404
 405                status = AE_AML_BAD_OPCODE;
 406                break;
 407        }
 408
 409        return (status);
 410}
 411