| /****************************************************************************** | |
| * | |
| * Module Name: exfldio - Aml Field I/O | |
| * | |
| *****************************************************************************/ | |
| /* | |
| * Copyright (C) 2000 - 2008, Intel Corp. | |
| * 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. | |
| */ | |
| #include <acpi/acpi.h> | |
| #include <acpi/acinterp.h> | |
| #include <acpi/amlcode.h> | |
| #include <acpi/acevents.h> | |
| #include <acpi/acdispat.h> | |
| #define _COMPONENT ACPI_EXECUTER | |
| ACPI_MODULE_NAME("exfldio") | |
| /* Local prototypes */ | |
| static acpi_status | |
| acpi_ex_field_datum_io(union acpi_operand_object *obj_desc, | |
| u32 field_datum_byte_offset, | |
| acpi_integer * value, u32 read_write); | |
| static u8 | |
| acpi_ex_register_overflow(union acpi_operand_object *obj_desc, | |
| acpi_integer value); | |
| static acpi_status | |
| acpi_ex_setup_region(union acpi_operand_object *obj_desc, | |
| u32 field_datum_byte_offset); | |
| /******************************************************************************* | |
| * | |
| * FUNCTION: acpi_ex_setup_region | |
| * | |
| * PARAMETERS: obj_desc - Field to be read or written | |
| * field_datum_byte_offset - Byte offset of this datum within the | |
| * parent field | |
| * | |
| * RETURN: Status | |
| * | |
| * DESCRIPTION: Common processing for acpi_ex_extract_from_field and | |
| * acpi_ex_insert_into_field. Initialize the Region if necessary and | |
| * validate the request. | |
| * | |
| ******************************************************************************/ | |
| static acpi_status | |
| acpi_ex_setup_region(union acpi_operand_object *obj_desc, | |
| u32 field_datum_byte_offset) | |
| { | |
| acpi_status status = AE_OK; | |
| union acpi_operand_object *rgn_desc; | |
| ACPI_FUNCTION_TRACE_U32(ex_setup_region, field_datum_byte_offset); | |
| rgn_desc = obj_desc->common_field.region_obj; | |
| /* We must have a valid region */ | |
| if (ACPI_GET_OBJECT_TYPE(rgn_desc) != ACPI_TYPE_REGION) { | |
| ACPI_ERROR((AE_INFO, "Needed Region, found type %X (%s)", | |
| ACPI_GET_OBJECT_TYPE(rgn_desc), | |
| acpi_ut_get_object_type_name(rgn_desc))); | |
| return_ACPI_STATUS(AE_AML_OPERAND_TYPE); | |
| } | |
| /* | |
| * If the Region Address and Length have not been previously evaluated, | |
| * evaluate them now and save the results. | |
| */ | |
| if (!(rgn_desc->common.flags & AOPOBJ_DATA_VALID)) { | |
| status = acpi_ds_get_region_arguments(rgn_desc); | |
| if (ACPI_FAILURE(status)) { | |
| return_ACPI_STATUS(status); | |
| } | |
| } | |
| /* Exit if Address/Length have been disallowed by the host OS */ | |
| if (rgn_desc->common.flags & AOPOBJ_INVALID) { | |
| return_ACPI_STATUS(AE_AML_ILLEGAL_ADDRESS); | |
| } | |
| /* | |
| * Exit now for SMBus address space, it has a non-linear address space | |
| * and the request cannot be directly validated | |
| */ | |
| if (rgn_desc->region.space_id == ACPI_ADR_SPACE_SMBUS) { | |
| /* SMBus has a non-linear address space */ | |
| return_ACPI_STATUS(AE_OK); | |
| } | |
| #ifdef ACPI_UNDER_DEVELOPMENT | |
| /* | |
| * If the Field access is any_acc, we can now compute the optimal | |
| * access (because we know know the length of the parent region) | |
| */ | |
| if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { | |
| if (ACPI_FAILURE(status)) { | |
| return_ACPI_STATUS(status); | |
| } | |
| } | |
| #endif | |
| /* | |
| * Validate the request. The entire request from the byte offset for a | |
| * length of one field datum (access width) must fit within the region. | |
| * (Region length is specified in bytes) | |
| */ | |
| if (rgn_desc->region.length < | |
| (obj_desc->common_field.base_byte_offset + | |
| field_datum_byte_offset + | |
| obj_desc->common_field.access_byte_width)) { | |
| if (acpi_gbl_enable_interpreter_slack) { | |
| /* | |
| * Slack mode only: We will go ahead and allow access to this | |
| * field if it is within the region length rounded up to the next | |
| * access width boundary. | |
| */ | |
| if (ACPI_ROUND_UP(rgn_desc->region.length, | |
| obj_desc->common_field. | |
| access_byte_width) >= | |
| (obj_desc->common_field.base_byte_offset + | |
| (acpi_native_uint) obj_desc->common_field. | |
| access_byte_width + field_datum_byte_offset)) { | |
| return_ACPI_STATUS(AE_OK); | |
| } | |
| } | |
| if (rgn_desc->region.length < | |
| obj_desc->common_field.access_byte_width) { | |
| /* | |
| * This is the case where the access_type (acc_word, etc.) is wider | |
| * than the region itself. For example, a region of length one | |
| * byte, and a field with Dword access specified. | |
| */ | |
| ACPI_ERROR((AE_INFO, | |
| "Field [%4.4s] access width (%d bytes) too large for region [%4.4s] (length %X)", | |
| acpi_ut_get_node_name(obj_desc-> | |
| common_field.node), | |
| obj_desc->common_field.access_byte_width, | |
| acpi_ut_get_node_name(rgn_desc->region. | |
| node), | |
| rgn_desc->region.length)); | |
| } | |
| /* | |
| * Offset rounded up to next multiple of field width | |
| * exceeds region length, indicate an error | |
| */ | |
| ACPI_ERROR((AE_INFO, | |
| "Field [%4.4s] Base+Offset+Width %X+%X+%X is beyond end of region [%4.4s] (length %X)", | |
| acpi_ut_get_node_name(obj_desc->common_field.node), | |
| obj_desc->common_field.base_byte_offset, | |
| field_datum_byte_offset, | |
| obj_desc->common_field.access_byte_width, | |
| acpi_ut_get_node_name(rgn_desc->region.node), | |
| rgn_desc->region.length)); | |
| return_ACPI_STATUS(AE_AML_REGION_LIMIT); | |
| } | |
| return_ACPI_STATUS(AE_OK); | |
| } | |
| /******************************************************************************* | |
| * | |
| * FUNCTION: acpi_ex_access_region | |
| * | |
| * PARAMETERS: obj_desc - Field to be read | |
| * field_datum_byte_offset - Byte offset of this datum within the | |
| * parent field | |
| * Value - Where to store value (must at least | |
| * the size of acpi_integer) | |
| * Function - Read or Write flag plus other region- | |
| * dependent flags | |
| * | |
| * RETURN: Status | |
| * | |
| * DESCRIPTION: Read or Write a single field datum to an Operation Region. | |
| * | |
| ******************************************************************************/ | |
| acpi_status | |
| acpi_ex_access_region(union acpi_operand_object *obj_desc, | |
| u32 field_datum_byte_offset, | |
| acpi_integer * value, u32 function) | |
| { | |
| acpi_status status; | |
| union acpi_operand_object *rgn_desc; | |
| acpi_physical_address address; | |
| ACPI_FUNCTION_TRACE(ex_access_region); | |
| /* | |
| * Ensure that the region operands are fully evaluated and verify | |
| * the validity of the request | |
| */ | |
| status = acpi_ex_setup_region(obj_desc, field_datum_byte_offset); | |
| if (ACPI_FAILURE(status)) { | |
| return_ACPI_STATUS(status); | |
| } | |
| /* | |
| * The physical address of this field datum is: | |
| * | |
| * 1) The base of the region, plus | |
| * 2) The base offset of the field, plus | |
| * 3) The current offset into the field | |
| */ | |
| rgn_desc = obj_desc->common_field.region_obj; | |
| address = rgn_desc->region.address + | |
| obj_desc->common_field.base_byte_offset + field_datum_byte_offset; | |
| if ((function & ACPI_IO_MASK) == ACPI_READ) { | |
| ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[READ]")); | |
| } else { | |
| ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[WRITE]")); | |
| } | |
| ACPI_DEBUG_PRINT_RAW((ACPI_DB_BFIELD, | |
| " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %p\n", | |
| acpi_ut_get_region_name(rgn_desc->region. | |
| space_id), | |
| rgn_desc->region.space_id, | |
| obj_desc->common_field.access_byte_width, | |
| obj_desc->common_field.base_byte_offset, | |
| field_datum_byte_offset, ACPI_CAST_PTR(void, | |
| address))); | |
| /* Invoke the appropriate address_space/op_region handler */ | |
| status = acpi_ev_address_space_dispatch(rgn_desc, function, | |
| address, | |
| ACPI_MUL_8(obj_desc-> | |
| common_field. | |
| access_byte_width), | |
| value); | |
| if (ACPI_FAILURE(status)) { | |
| if (status == AE_NOT_IMPLEMENTED) { | |
| ACPI_ERROR((AE_INFO, | |
| "Region %s(%X) not implemented", | |
| acpi_ut_get_region_name(rgn_desc->region. | |
| space_id), | |
| rgn_desc->region.space_id)); | |
| } else if (status == AE_NOT_EXIST) { | |
| ACPI_ERROR((AE_INFO, | |
| "Region %s(%X) has no handler", | |
| acpi_ut_get_region_name(rgn_desc->region. | |
| space_id), | |
| rgn_desc->region.space_id)); | |
| } | |
| } | |
| return_ACPI_STATUS(status); | |
| } | |
| /******************************************************************************* | |
| * | |
| * FUNCTION: acpi_ex_register_overflow | |
| * | |
| * PARAMETERS: obj_desc - Register(Field) to be written | |
| * Value - Value to be stored | |
| * | |
| * RETURN: TRUE if value overflows the field, FALSE otherwise | |
| * | |
| * DESCRIPTION: Check if a value is out of range of the field being written. | |
| * Used to check if the values written to Index and Bank registers | |
| * are out of range. Normally, the value is simply truncated | |
| * to fit the field, but this case is most likely a serious | |
| * coding error in the ASL. | |
| * | |
| ******************************************************************************/ | |
| static u8 | |
| acpi_ex_register_overflow(union acpi_operand_object *obj_desc, | |
| acpi_integer value) | |
| { | |
| if (obj_desc->common_field.bit_length >= ACPI_INTEGER_BIT_SIZE) { | |
| /* | |
| * The field is large enough to hold the maximum integer, so we can | |
| * never overflow it. | |
| */ | |
| return (FALSE); | |
| } | |
| if (value >= ((acpi_integer) 1 << obj_desc->common_field.bit_length)) { | |
| /* | |
| * The Value is larger than the maximum value that can fit into | |
| * the register. | |
| */ | |
| return (TRUE); | |
| } | |
| /* The Value will fit into the field with no truncation */ | |
| return (FALSE); | |
| } | |
| /******************************************************************************* | |
| * | |
| * FUNCTION: acpi_ex_field_datum_io | |
| * | |
| * PARAMETERS: obj_desc - Field to be read | |
| * field_datum_byte_offset - Byte offset of this datum within the | |
| * parent field | |
| * Value - Where to store value (must be 64 bits) | |
| * read_write - Read or Write flag | |
| * | |
| * RETURN: Status | |
| * | |
| * DESCRIPTION: Read or Write a single datum of a field. The field_type is | |
| * demultiplexed here to handle the different types of fields | |
| * (buffer_field, region_field, index_field, bank_field) | |
| * | |
| ******************************************************************************/ | |
| static acpi_status | |
| acpi_ex_field_datum_io(union acpi_operand_object *obj_desc, | |
| u32 field_datum_byte_offset, | |
| acpi_integer * value, u32 read_write) | |
| { | |
| acpi_status status; | |
| acpi_integer local_value; | |
| ACPI_FUNCTION_TRACE_U32(ex_field_datum_io, field_datum_byte_offset); | |
| if (read_write == ACPI_READ) { | |
| if (!value) { | |
| local_value = 0; | |
| /* To support reads without saving return value */ | |
| value = &local_value; | |
| } | |
| /* Clear the entire return buffer first, [Very Important!] */ | |
| *value = 0; | |
| } | |
| /* | |
| * The four types of fields are: | |
| * | |
| * buffer_field - Read/write from/to a Buffer | |
| * region_field - Read/write from/to a Operation Region. | |
| * bank_field - Write to a Bank Register, then read/write from/to an | |
| * operation_region | |
| * index_field - Write to an Index Register, then read/write from/to a | |
| * Data Register | |
| */ | |
| switch (ACPI_GET_OBJECT_TYPE(obj_desc)) { | |
| case ACPI_TYPE_BUFFER_FIELD: | |
| /* | |
| * If the buffer_field arguments have not been previously evaluated, | |
| * evaluate them now and save the results. | |
| */ | |
| if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { | |
| status = acpi_ds_get_buffer_field_arguments(obj_desc); | |
| if (ACPI_FAILURE(status)) { | |
| return_ACPI_STATUS(status); | |
| } | |
| } | |
| if (read_write == ACPI_READ) { | |
| /* | |
| * Copy the data from the source buffer. | |
| * Length is the field width in bytes. | |
| */ | |
| ACPI_MEMCPY(value, | |
| (obj_desc->buffer_field.buffer_obj)->buffer. | |
| pointer + | |
| obj_desc->buffer_field.base_byte_offset + | |
| field_datum_byte_offset, | |
| obj_desc->common_field.access_byte_width); | |
| } else { | |
| /* | |
| * Copy the data to the target buffer. | |
| * Length is the field width in bytes. | |
| */ | |
| ACPI_MEMCPY((obj_desc->buffer_field.buffer_obj)->buffer. | |
| pointer + | |
| obj_desc->buffer_field.base_byte_offset + | |
| field_datum_byte_offset, value, | |
| obj_desc->common_field.access_byte_width); | |
| } | |
| status = AE_OK; | |
| break; | |
| case ACPI_TYPE_LOCAL_BANK_FIELD: | |
| /* | |
| * Ensure that the bank_value is not beyond the capacity of | |
| * the register | |
| */ | |
| if (acpi_ex_register_overflow(obj_desc->bank_field.bank_obj, | |
| (acpi_integer) obj_desc-> | |
| bank_field.value)) { | |
| return_ACPI_STATUS(AE_AML_REGISTER_LIMIT); | |
| } | |
| /* | |
| * For bank_fields, we must write the bank_value to the bank_register | |
| * (itself a region_field) before we can access the data. | |
| */ | |
| status = | |
| acpi_ex_insert_into_field(obj_desc->bank_field.bank_obj, | |
| &obj_desc->bank_field.value, | |
| sizeof(obj_desc->bank_field. | |
| value)); | |
| if (ACPI_FAILURE(status)) { | |
| return_ACPI_STATUS(status); | |
| } | |
| /* | |
| * Now that the Bank has been selected, fall through to the | |
| * region_field case and write the datum to the Operation Region | |
| */ | |
| /*lint -fallthrough */ | |
| case ACPI_TYPE_LOCAL_REGION_FIELD: | |
| /* | |
| * For simple region_fields, we just directly access the owning | |
| * Operation Region. | |
| */ | |
| status = | |
| acpi_ex_access_region(obj_desc, field_datum_byte_offset, | |
| value, read_write); | |
| break; | |
| case ACPI_TYPE_LOCAL_INDEX_FIELD: | |
| /* | |
| * Ensure that the index_value is not beyond the capacity of | |
| * the register | |
| */ | |
| if (acpi_ex_register_overflow(obj_desc->index_field.index_obj, | |
| (acpi_integer) obj_desc-> | |
| index_field.value)) { | |
| return_ACPI_STATUS(AE_AML_REGISTER_LIMIT); | |
| } | |
| /* Write the index value to the index_register (itself a region_field) */ | |
| field_datum_byte_offset += obj_desc->index_field.value; | |
| ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, | |
| "Write to Index Register: Value %8.8X\n", | |
| field_datum_byte_offset)); | |
| status = | |
| acpi_ex_insert_into_field(obj_desc->index_field.index_obj, | |
| &field_datum_byte_offset, | |
| sizeof(field_datum_byte_offset)); | |
| if (ACPI_FAILURE(status)) { | |
| return_ACPI_STATUS(status); | |
| } | |
| ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, | |
| "I/O to Data Register: ValuePtr %p\n", | |
| value)); | |
| if (read_write == ACPI_READ) { | |
| /* Read the datum from the data_register */ | |
| status = | |
| acpi_ex_extract_from_field(obj_desc->index_field. | |
| data_obj, value, | |
| sizeof(acpi_integer)); | |
| } else { | |
| /* Write the datum to the data_register */ | |
| status = | |
| acpi_ex_insert_into_field(obj_desc->index_field. | |
| data_obj, value, | |
| sizeof(acpi_integer)); | |
| } | |
| break; | |
| default: | |
| ACPI_ERROR((AE_INFO, "Wrong object type in field I/O %X", | |
| ACPI_GET_OBJECT_TYPE(obj_desc))); | |
| status = AE_AML_INTERNAL; | |
| break; | |
| } | |
| if (ACPI_SUCCESS(status)) { | |
| if (read_write == ACPI_READ) { | |
| ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, | |
| "Value Read %8.8X%8.8X, Width %d\n", | |
| ACPI_FORMAT_UINT64(*value), | |
| obj_desc->common_field. | |
| access_byte_width)); | |
| } else { | |
| ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, | |
| "Value Written %8.8X%8.8X, Width %d\n", | |
| ACPI_FORMAT_UINT64(*value), | |
| obj_desc->common_field. | |
| access_byte_width)); | |
| } | |
| } | |
| return_ACPI_STATUS(status); | |
| } | |
| /******************************************************************************* | |
| * | |
| * FUNCTION: acpi_ex_write_with_update_rule | |
| * | |
| * PARAMETERS: obj_desc - Field to be written | |
| * Mask - bitmask within field datum | |
| * field_value - Value to write | |
| * field_datum_byte_offset - Offset of datum within field | |
| * | |
| * RETURN: Status | |
| * | |
| * DESCRIPTION: Apply the field update rule to a field write | |
| * | |
| ******************************************************************************/ | |
| acpi_status | |
| acpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc, | |
| acpi_integer mask, | |
| acpi_integer field_value, | |
| u32 field_datum_byte_offset) | |
| { | |
| acpi_status status = AE_OK; | |
| acpi_integer merged_value; | |
| acpi_integer current_value; | |
| ACPI_FUNCTION_TRACE_U32(ex_write_with_update_rule, mask); | |
| /* Start with the new bits */ | |
| merged_value = field_value; | |
| /* If the mask is all ones, we don't need to worry about the update rule */ | |
| if (mask != ACPI_INTEGER_MAX) { | |
| /* Decode the update rule */ | |
| switch (obj_desc->common_field. | |
| field_flags & AML_FIELD_UPDATE_RULE_MASK) { | |
| case AML_FIELD_UPDATE_PRESERVE: | |
| /* | |
| * Check if update rule needs to be applied (not if mask is all | |
| * ones) The left shift drops the bits we want to ignore. | |
| */ | |
| if ((~mask << (ACPI_MUL_8(sizeof(mask)) - | |
| ACPI_MUL_8(obj_desc->common_field. | |
| access_byte_width))) != 0) { | |
| /* | |
| * Read the current contents of the byte/word/dword containing | |
| * the field, and merge with the new field value. | |
| */ | |
| status = | |
| acpi_ex_field_datum_io(obj_desc, | |
| field_datum_byte_offset, | |
| ¤t_value, | |
| ACPI_READ); | |
| if (ACPI_FAILURE(status)) { | |
| return_ACPI_STATUS(status); | |
| } | |
| merged_value |= (current_value & ~mask); | |
| } | |
| break; | |
| case AML_FIELD_UPDATE_WRITE_AS_ONES: | |
| /* Set positions outside the field to all ones */ | |
| merged_value |= ~mask; | |
| break; | |
| case AML_FIELD_UPDATE_WRITE_AS_ZEROS: | |
| /* Set positions outside the field to all zeros */ | |
| merged_value &= mask; | |
| break; | |
| default: | |
| ACPI_ERROR((AE_INFO, | |
| "Unknown UpdateRule value: %X", | |
| (obj_desc->common_field. | |
| field_flags & | |
| AML_FIELD_UPDATE_RULE_MASK))); | |
| return_ACPI_STATUS(AE_AML_OPERAND_VALUE); | |
| } | |
| } | |
| ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, | |
| "Mask %8.8X%8.8X, DatumOffset %X, Width %X, Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n", | |
| ACPI_FORMAT_UINT64(mask), | |
| field_datum_byte_offset, | |
| obj_desc->common_field.access_byte_width, | |
| ACPI_FORMAT_UINT64(field_value), | |
| ACPI_FORMAT_UINT64(merged_value))); | |
| /* Write the merged value */ | |
| status = acpi_ex_field_datum_io(obj_desc, field_datum_byte_offset, | |
| &merged_value, ACPI_WRITE); | |
| return_ACPI_STATUS(status); | |
| } | |
| /******************************************************************************* | |
| * | |
| * FUNCTION: acpi_ex_extract_from_field | |
| * | |
| * PARAMETERS: obj_desc - Field to be read | |
| * Buffer - Where to store the field data | |
| * buffer_length - Length of Buffer | |
| * | |
| * RETURN: Status | |
| * | |
| * DESCRIPTION: Retrieve the current value of the given field | |
| * | |
| ******************************************************************************/ | |
| acpi_status | |
| acpi_ex_extract_from_field(union acpi_operand_object *obj_desc, | |
| void *buffer, u32 buffer_length) | |
| { | |
| acpi_status status; | |
| acpi_integer raw_datum; | |
| acpi_integer merged_datum; | |
| u32 field_offset = 0; | |
| u32 buffer_offset = 0; | |
| u32 buffer_tail_bits; | |
| u32 datum_count; | |
| u32 field_datum_count; | |
| u32 i; | |
| ACPI_FUNCTION_TRACE(ex_extract_from_field); | |
| /* Validate target buffer and clear it */ | |
| if (buffer_length < | |
| ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length)) { | |
| ACPI_ERROR((AE_INFO, | |
| "Field size %X (bits) is too large for buffer (%X)", | |
| obj_desc->common_field.bit_length, buffer_length)); | |
| return_ACPI_STATUS(AE_BUFFER_OVERFLOW); | |
| } | |
| ACPI_MEMSET(buffer, 0, buffer_length); | |
| /* Compute the number of datums (access width data items) */ | |
| datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length, | |
| obj_desc->common_field.access_bit_width); | |
| field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length + | |
| obj_desc->common_field. | |
| start_field_bit_offset, | |
| obj_desc->common_field. | |
| access_bit_width); | |
| /* Priming read from the field */ | |
| status = | |
| acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum, | |
| ACPI_READ); | |
| if (ACPI_FAILURE(status)) { | |
| return_ACPI_STATUS(status); | |
| } | |
| merged_datum = | |
| raw_datum >> obj_desc->common_field.start_field_bit_offset; | |
| /* Read the rest of the field */ | |
| for (i = 1; i < field_datum_count; i++) { | |
| /* Get next input datum from the field */ | |
| field_offset += obj_desc->common_field.access_byte_width; | |
| status = acpi_ex_field_datum_io(obj_desc, field_offset, | |
| &raw_datum, ACPI_READ); | |
| if (ACPI_FAILURE(status)) { | |
| return_ACPI_STATUS(status); | |
| } | |
| /* | |
| * Merge with previous datum if necessary. | |
| * | |
| * Note: Before the shift, check if the shift value will be larger than | |
| * the integer size. If so, there is no need to perform the operation. | |
| * This avoids the differences in behavior between different compilers | |
| * concerning shift values larger than the target data width. | |
| */ | |
| if ((obj_desc->common_field.access_bit_width - | |
| obj_desc->common_field.start_field_bit_offset) < | |
| ACPI_INTEGER_BIT_SIZE) { | |
| merged_datum |= | |
| raw_datum << (obj_desc->common_field. | |
| access_bit_width - | |
| obj_desc->common_field. | |
| start_field_bit_offset); | |
| } | |
| if (i == datum_count) { | |
| break; | |
| } | |
| /* Write merged datum to target buffer */ | |
| ACPI_MEMCPY(((char *)buffer) + buffer_offset, &merged_datum, | |
| ACPI_MIN(obj_desc->common_field.access_byte_width, | |
| buffer_length - buffer_offset)); | |
| buffer_offset += obj_desc->common_field.access_byte_width; | |
| merged_datum = | |
| raw_datum >> obj_desc->common_field.start_field_bit_offset; | |
| } | |
| /* Mask off any extra bits in the last datum */ | |
| buffer_tail_bits = obj_desc->common_field.bit_length % | |
| obj_desc->common_field.access_bit_width; | |
| if (buffer_tail_bits) { | |
| merged_datum &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits); | |
| } | |
| /* Write the last datum to the buffer */ | |
| ACPI_MEMCPY(((char *)buffer) + buffer_offset, &merged_datum, | |
| ACPI_MIN(obj_desc->common_field.access_byte_width, | |
| buffer_length - buffer_offset)); | |
| return_ACPI_STATUS(AE_OK); | |
| } | |
| /******************************************************************************* | |
| * | |
| * FUNCTION: acpi_ex_insert_into_field | |
| * | |
| * PARAMETERS: obj_desc - Field to be written | |
| * Buffer - Data to be written | |
| * buffer_length - Length of Buffer | |
| * | |
| * RETURN: Status | |
| * | |
| * DESCRIPTION: Store the Buffer contents into the given field | |
| * | |
| ******************************************************************************/ | |
| acpi_status | |
| acpi_ex_insert_into_field(union acpi_operand_object *obj_desc, | |
| void *buffer, u32 buffer_length) | |
| { | |
| acpi_status status; | |
| acpi_integer mask; | |
| acpi_integer width_mask; | |
| acpi_integer merged_datum; | |
| acpi_integer raw_datum = 0; | |
| u32 field_offset = 0; | |
| u32 buffer_offset = 0; | |
| u32 buffer_tail_bits; | |
| u32 datum_count; | |
| u32 field_datum_count; | |
| u32 i; | |
| u32 required_length; | |
| void *new_buffer; | |
| ACPI_FUNCTION_TRACE(ex_insert_into_field); | |
| /* Validate input buffer */ | |
| new_buffer = NULL; | |
| required_length = | |
| ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length); | |
| /* | |
| * We must have a buffer that is at least as long as the field | |
| * we are writing to. This is because individual fields are | |
| * indivisible and partial writes are not supported -- as per | |
| * the ACPI specification. | |
| */ | |
| if (buffer_length < required_length) { | |
| /* We need to create a new buffer */ | |
| new_buffer = ACPI_ALLOCATE_ZEROED(required_length); | |
| if (!new_buffer) { | |
| return_ACPI_STATUS(AE_NO_MEMORY); | |
| } | |
| /* | |
| * Copy the original data to the new buffer, starting | |
| * at Byte zero. All unused (upper) bytes of the | |
| * buffer will be 0. | |
| */ | |
| ACPI_MEMCPY((char *)new_buffer, (char *)buffer, buffer_length); | |
| buffer = new_buffer; | |
| buffer_length = required_length; | |
| } | |
| /* | |
| * Create the bitmasks used for bit insertion. | |
| * Note: This if/else is used to bypass compiler differences with the | |
| * shift operator | |
| */ | |
| if (obj_desc->common_field.access_bit_width == ACPI_INTEGER_BIT_SIZE) { | |
| width_mask = ACPI_INTEGER_MAX; | |
| } else { | |
| width_mask = | |
| ACPI_MASK_BITS_ABOVE(obj_desc->common_field. | |
| access_bit_width); | |
| } | |
| mask = width_mask & | |
| ACPI_MASK_BITS_BELOW(obj_desc->common_field.start_field_bit_offset); | |
| /* Compute the number of datums (access width data items) */ | |
| datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length, | |
| obj_desc->common_field.access_bit_width); | |
| field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length + | |
| obj_desc->common_field. | |
| start_field_bit_offset, | |
| obj_desc->common_field. | |
| access_bit_width); | |
| /* Get initial Datum from the input buffer */ | |
| ACPI_MEMCPY(&raw_datum, buffer, | |
| ACPI_MIN(obj_desc->common_field.access_byte_width, | |
| buffer_length - buffer_offset)); | |
| merged_datum = | |
| raw_datum << obj_desc->common_field.start_field_bit_offset; | |
| /* Write the entire field */ | |
| for (i = 1; i < field_datum_count; i++) { | |
| /* Write merged datum to the target field */ | |
| merged_datum &= mask; | |
| status = acpi_ex_write_with_update_rule(obj_desc, mask, | |
| merged_datum, | |
| field_offset); | |
| if (ACPI_FAILURE(status)) { | |
| goto exit; | |
| } | |
| field_offset += obj_desc->common_field.access_byte_width; | |
| /* | |
| * Start new output datum by merging with previous input datum | |
| * if necessary. | |
| * | |
| * Note: Before the shift, check if the shift value will be larger than | |
| * the integer size. If so, there is no need to perform the operation. | |
| * This avoids the differences in behavior between different compilers | |
| * concerning shift values larger than the target data width. | |
| */ | |
| if ((obj_desc->common_field.access_bit_width - | |
| obj_desc->common_field.start_field_bit_offset) < | |
| ACPI_INTEGER_BIT_SIZE) { | |
| merged_datum = | |
| raw_datum >> (obj_desc->common_field. | |
| access_bit_width - | |
| obj_desc->common_field. | |
| start_field_bit_offset); | |
| } else { | |
| merged_datum = 0; | |
| } | |
| mask = width_mask; | |
| if (i == datum_count) { | |
| break; | |
| } | |
| /* Get the next input datum from the buffer */ | |
| buffer_offset += obj_desc->common_field.access_byte_width; | |
| ACPI_MEMCPY(&raw_datum, ((char *)buffer) + buffer_offset, | |
| ACPI_MIN(obj_desc->common_field.access_byte_width, | |
| buffer_length - buffer_offset)); | |
| merged_datum |= | |
| raw_datum << obj_desc->common_field.start_field_bit_offset; | |
| } | |
| /* Mask off any extra bits in the last datum */ | |
| buffer_tail_bits = (obj_desc->common_field.bit_length + | |
| obj_desc->common_field.start_field_bit_offset) % | |
| obj_desc->common_field.access_bit_width; | |
| if (buffer_tail_bits) { | |
| mask &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits); | |
| } | |
| /* Write the last datum to the field */ | |
| merged_datum &= mask; | |
| status = acpi_ex_write_with_update_rule(obj_desc, | |
| mask, merged_datum, | |
| field_offset); | |
| exit: | |
| /* Free temporary buffer if we used one */ | |
| if (new_buffer) { | |
| ACPI_FREE(new_buffer); | |
| } | |
| return_ACPI_STATUS(status); | |
| } |