Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
0002 /******************************************************************************
0003  *
0004  * Module Name: exfield - AML execution - field_unit read/write
0005  *
0006  * Copyright (C) 2000 - 2022, Intel Corp.
0007  *
0008  *****************************************************************************/
0009 
0010 #include <acpi/acpi.h>
0011 #include "accommon.h"
0012 #include "acdispat.h"
0013 #include "acinterp.h"
0014 #include "amlcode.h"
0015 
0016 #define _COMPONENT          ACPI_EXECUTER
0017 ACPI_MODULE_NAME("exfield")
0018 
0019 /*
0020  * This table maps the various Attrib protocols to the byte transfer
0021  * length. Used for the generic serial bus.
0022  */
0023 #define ACPI_INVALID_PROTOCOL_ID        0x80
0024 #define ACPI_MAX_PROTOCOL_ID            0x0F
0025 static const u8 acpi_protocol_lengths[] = {
0026     ACPI_INVALID_PROTOCOL_ID,   /* 0 - reserved */
0027     ACPI_INVALID_PROTOCOL_ID,   /* 1 - reserved */
0028     0x00,           /* 2 - ATTRIB_QUICK */
0029     ACPI_INVALID_PROTOCOL_ID,   /* 3 - reserved */
0030     0x01,           /* 4 - ATTRIB_SEND_RECEIVE */
0031     ACPI_INVALID_PROTOCOL_ID,   /* 5 - reserved */
0032     0x01,           /* 6 - ATTRIB_BYTE */
0033     ACPI_INVALID_PROTOCOL_ID,   /* 7 - reserved */
0034     0x02,           /* 8 - ATTRIB_WORD */
0035     ACPI_INVALID_PROTOCOL_ID,   /* 9 - reserved */
0036     0xFF,           /* A - ATTRIB_BLOCK  */
0037     0xFF,           /* B - ATTRIB_BYTES */
0038     0x02,           /* C - ATTRIB_PROCESS_CALL */
0039     0xFF,           /* D - ATTRIB_BLOCK_PROCESS_CALL */
0040     0xFF,           /* E - ATTRIB_RAW_BYTES */
0041     0xFF            /* F - ATTRIB_RAW_PROCESS_BYTES */
0042 };
0043 
0044 #define PCC_MASTER_SUBSPACE     3
0045 
0046 /*
0047  * The following macros determine a given offset is a COMD field.
0048  * According to the specification, generic subspaces (types 0-2) contains a
0049  * 2-byte COMD field at offset 4 and master subspaces (type 3) contains a 4-byte
0050  * COMD field starting at offset 12.
0051  */
0052 #define GENERIC_SUBSPACE_COMMAND(a)     (4 == a || a == 5)
0053 #define MASTER_SUBSPACE_COMMAND(a)      (12 <= a && a <= 15)
0054 
0055 /*******************************************************************************
0056  *
0057  * FUNCTION:    acpi_ex_get_protocol_buffer_length
0058  *
0059  * PARAMETERS:  protocol_id     - The type of the protocol indicated by region
0060  *                                field access attributes
0061  *              return_length   - Where the protocol byte transfer length is
0062  *                                returned
0063  *
0064  * RETURN:      Status and decoded byte transfer length
0065  *
0066  * DESCRIPTION: This routine returns the length of the generic_serial_bus
0067  *              protocol bytes
0068  *
0069  ******************************************************************************/
0070 
0071 acpi_status
0072 acpi_ex_get_protocol_buffer_length(u32 protocol_id, u32 *return_length)
0073 {
0074 
0075     if ((protocol_id > ACPI_MAX_PROTOCOL_ID) ||
0076         (acpi_protocol_lengths[protocol_id] == ACPI_INVALID_PROTOCOL_ID)) {
0077         ACPI_ERROR((AE_INFO,
0078                 "Invalid Field/AccessAs protocol ID: 0x%4.4X",
0079                 protocol_id));
0080 
0081         return (AE_AML_PROTOCOL);
0082     }
0083 
0084     *return_length = acpi_protocol_lengths[protocol_id];
0085     return (AE_OK);
0086 }
0087 
0088 /*******************************************************************************
0089  *
0090  * FUNCTION:    acpi_ex_read_data_from_field
0091  *
0092  * PARAMETERS:  walk_state          - Current execution state
0093  *              obj_desc            - The named field
0094  *              ret_buffer_desc     - Where the return data object is stored
0095  *
0096  * RETURN:      Status
0097  *
0098  * DESCRIPTION: Read from a named field. Returns either an Integer or a
0099  *              Buffer, depending on the size of the field and whether if a
0100  *              field is created by the create_field() operator.
0101  *
0102  ******************************************************************************/
0103 
0104 acpi_status
0105 acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
0106                  union acpi_operand_object *obj_desc,
0107                  union acpi_operand_object **ret_buffer_desc)
0108 {
0109     acpi_status status;
0110     union acpi_operand_object *buffer_desc;
0111     void *buffer;
0112     u32 buffer_length;
0113 
0114     ACPI_FUNCTION_TRACE_PTR(ex_read_data_from_field, obj_desc);
0115 
0116     /* Parameter validation */
0117 
0118     if (!obj_desc) {
0119         return_ACPI_STATUS(AE_AML_NO_OPERAND);
0120     }
0121     if (!ret_buffer_desc) {
0122         return_ACPI_STATUS(AE_BAD_PARAMETER);
0123     }
0124 
0125     if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) {
0126         /*
0127          * If the buffer_field arguments have not been previously evaluated,
0128          * evaluate them now and save the results.
0129          */
0130         if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
0131             status = acpi_ds_get_buffer_field_arguments(obj_desc);
0132             if (ACPI_FAILURE(status)) {
0133                 return_ACPI_STATUS(status);
0134             }
0135         }
0136     } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
0137            (obj_desc->field.region_obj->region.space_id ==
0138             ACPI_ADR_SPACE_SMBUS
0139             || obj_desc->field.region_obj->region.space_id ==
0140             ACPI_ADR_SPACE_GSBUS
0141             || obj_desc->field.region_obj->region.space_id ==
0142             ACPI_ADR_SPACE_IPMI
0143             || obj_desc->field.region_obj->region.space_id ==
0144             ACPI_ADR_SPACE_PLATFORM_RT)) {
0145 
0146         /* SMBus, GSBus, IPMI serial */
0147 
0148         status = acpi_ex_read_serial_bus(obj_desc, ret_buffer_desc);
0149         return_ACPI_STATUS(status);
0150     }
0151 
0152     /*
0153      * Allocate a buffer for the contents of the field.
0154      *
0155      * If the field is larger than the current integer width, create
0156      * a BUFFER to hold it. Otherwise, use an INTEGER. This allows
0157      * the use of arithmetic operators on the returned value if the
0158      * field size is equal or smaller than an Integer.
0159      *
0160      * However, all buffer fields created by create_field operator needs to
0161      * remain as a buffer to match other AML interpreter implementations.
0162      *
0163      * Note: Field.length is in bits.
0164      */
0165     buffer_length =
0166         (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.bit_length);
0167 
0168     if (buffer_length > acpi_gbl_integer_byte_width ||
0169         (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD &&
0170          obj_desc->buffer_field.is_create_field)) {
0171 
0172         /* Field is too large for an Integer, create a Buffer instead */
0173 
0174         buffer_desc = acpi_ut_create_buffer_object(buffer_length);
0175         if (!buffer_desc) {
0176             return_ACPI_STATUS(AE_NO_MEMORY);
0177         }
0178         buffer = buffer_desc->buffer.pointer;
0179     } else {
0180         /* Field will fit within an Integer (normal case) */
0181 
0182         buffer_desc = acpi_ut_create_integer_object((u64) 0);
0183         if (!buffer_desc) {
0184             return_ACPI_STATUS(AE_NO_MEMORY);
0185         }
0186 
0187         buffer_length = acpi_gbl_integer_byte_width;
0188         buffer = &buffer_desc->integer.value;
0189     }
0190 
0191     if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
0192         (obj_desc->field.region_obj->region.space_id ==
0193          ACPI_ADR_SPACE_GPIO)) {
0194 
0195         /* General Purpose I/O */
0196 
0197         status = acpi_ex_read_gpio(obj_desc, buffer);
0198         goto exit;
0199     } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
0200            (obj_desc->field.region_obj->region.space_id ==
0201             ACPI_ADR_SPACE_PLATFORM_COMM)) {
0202         /*
0203          * Reading from a PCC field unit does not require the handler because
0204          * it only requires reading from the internal_pcc_buffer.
0205          */
0206         ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
0207                   "PCC FieldRead bits %u\n",
0208                   obj_desc->field.bit_length));
0209 
0210         memcpy(buffer,
0211                obj_desc->field.region_obj->field.internal_pcc_buffer +
0212                obj_desc->field.base_byte_offset,
0213                (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.
0214                                   bit_length));
0215 
0216         *ret_buffer_desc = buffer_desc;
0217         return AE_OK;
0218     }
0219 
0220     ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
0221               "FieldRead [TO]:   Obj %p, Type %X, Buf %p, ByteLen %X\n",
0222               obj_desc, obj_desc->common.type, buffer,
0223               buffer_length));
0224     ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
0225               "FieldRead [FROM]: BitLen %X, BitOff %X, ByteOff %X\n",
0226               obj_desc->common_field.bit_length,
0227               obj_desc->common_field.start_field_bit_offset,
0228               obj_desc->common_field.base_byte_offset));
0229 
0230     /* Lock entire transaction if requested */
0231 
0232     acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
0233 
0234     /* Read from the field */
0235 
0236     status = acpi_ex_extract_from_field(obj_desc, buffer, buffer_length);
0237     acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
0238 
0239 exit:
0240     if (ACPI_FAILURE(status)) {
0241         acpi_ut_remove_reference(buffer_desc);
0242     } else {
0243         *ret_buffer_desc = buffer_desc;
0244     }
0245 
0246     return_ACPI_STATUS(status);
0247 }
0248 
0249 /*******************************************************************************
0250  *
0251  * FUNCTION:    acpi_ex_write_data_to_field
0252  *
0253  * PARAMETERS:  source_desc         - Contains data to write
0254  *              obj_desc            - The named field
0255  *              result_desc         - Where the return value is returned, if any
0256  *
0257  * RETURN:      Status
0258  *
0259  * DESCRIPTION: Write to a named field
0260  *
0261  ******************************************************************************/
0262 
0263 acpi_status
0264 acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
0265                 union acpi_operand_object *obj_desc,
0266                 union acpi_operand_object **result_desc)
0267 {
0268     acpi_status status;
0269     u32 buffer_length;
0270     u32 data_length;
0271     void *buffer;
0272 
0273     ACPI_FUNCTION_TRACE_PTR(ex_write_data_to_field, obj_desc);
0274 
0275     /* Parameter validation */
0276 
0277     if (!source_desc || !obj_desc) {
0278         return_ACPI_STATUS(AE_AML_NO_OPERAND);
0279     }
0280 
0281     if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) {
0282         /*
0283          * If the buffer_field arguments have not been previously evaluated,
0284          * evaluate them now and save the results.
0285          */
0286         if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
0287             status = acpi_ds_get_buffer_field_arguments(obj_desc);
0288             if (ACPI_FAILURE(status)) {
0289                 return_ACPI_STATUS(status);
0290             }
0291         }
0292     } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
0293            (obj_desc->field.region_obj->region.space_id ==
0294             ACPI_ADR_SPACE_GPIO)) {
0295 
0296         /* General Purpose I/O */
0297 
0298         status = acpi_ex_write_gpio(source_desc, obj_desc, result_desc);
0299         return_ACPI_STATUS(status);
0300     } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
0301            (obj_desc->field.region_obj->region.space_id ==
0302             ACPI_ADR_SPACE_SMBUS
0303             || obj_desc->field.region_obj->region.space_id ==
0304             ACPI_ADR_SPACE_GSBUS
0305             || obj_desc->field.region_obj->region.space_id ==
0306             ACPI_ADR_SPACE_IPMI
0307             || obj_desc->field.region_obj->region.space_id ==
0308             ACPI_ADR_SPACE_PLATFORM_RT)) {
0309 
0310         /* SMBus, GSBus, IPMI serial */
0311 
0312         status =
0313             acpi_ex_write_serial_bus(source_desc, obj_desc,
0314                          result_desc);
0315         return_ACPI_STATUS(status);
0316     } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
0317            (obj_desc->field.region_obj->region.space_id ==
0318             ACPI_ADR_SPACE_PLATFORM_COMM)) {
0319         /*
0320          * According to the spec a write to the COMD field will invoke the
0321          * region handler. Otherwise, write to the pcc_internal buffer. This
0322          * implementation will use the offsets specified rather than the name
0323          * of the field. This is considered safer because some firmware tools
0324          * are known to obfiscate named objects.
0325          */
0326         data_length =
0327             (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.
0328                                bit_length);
0329         memcpy(obj_desc->field.region_obj->field.internal_pcc_buffer +
0330                obj_desc->field.base_byte_offset,
0331                source_desc->buffer.pointer, data_length);
0332 
0333         if (MASTER_SUBSPACE_COMMAND(obj_desc->field.base_byte_offset)) {
0334 
0335             /* Perform the write */
0336 
0337             ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
0338                       "PCC COMD field has been written. Invoking PCC handler now.\n"));
0339 
0340             status =
0341                 acpi_ex_access_region(obj_desc, 0,
0342                           (u64 *)obj_desc->field.
0343                           region_obj->field.
0344                           internal_pcc_buffer,
0345                           ACPI_WRITE);
0346             return_ACPI_STATUS(status);
0347         }
0348         return (AE_OK);
0349     }
0350 
0351     /* Get a pointer to the data to be written */
0352 
0353     switch (source_desc->common.type) {
0354     case ACPI_TYPE_INTEGER:
0355 
0356         buffer = &source_desc->integer.value;
0357         buffer_length = sizeof(source_desc->integer.value);
0358         break;
0359 
0360     case ACPI_TYPE_BUFFER:
0361 
0362         buffer = source_desc->buffer.pointer;
0363         buffer_length = source_desc->buffer.length;
0364         break;
0365 
0366     case ACPI_TYPE_STRING:
0367 
0368         buffer = source_desc->string.pointer;
0369         buffer_length = source_desc->string.length;
0370         break;
0371 
0372     default:
0373         return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
0374     }
0375 
0376     ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
0377               "FieldWrite [FROM]: Obj %p (%s:%X), Buf %p, ByteLen %X\n",
0378               source_desc,
0379               acpi_ut_get_type_name(source_desc->common.type),
0380               source_desc->common.type, buffer, buffer_length));
0381 
0382     ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
0383               "FieldWrite [TO]:   Obj %p (%s:%X), BitLen %X, BitOff %X, ByteOff %X\n",
0384               obj_desc,
0385               acpi_ut_get_type_name(obj_desc->common.type),
0386               obj_desc->common.type,
0387               obj_desc->common_field.bit_length,
0388               obj_desc->common_field.start_field_bit_offset,
0389               obj_desc->common_field.base_byte_offset));
0390 
0391     /* Lock entire transaction if requested */
0392 
0393     acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
0394 
0395     /* Write to the field */
0396 
0397     status = acpi_ex_insert_into_field(obj_desc, buffer, buffer_length);
0398     acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
0399     return_ACPI_STATUS(status);
0400 }