1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2 /****************************************************************************** 3 * 4 * Module Name: exfield - AML execution - field_unit read/write 5 * 6 * Copyright (C) 2000 - 2018, Intel Corp. 7 * 8 *****************************************************************************/ 9 10 #include <acpi/acpi.h> 11 #include "accommon.h" 12 #include "acdispat.h" 13 #include "acinterp.h" 14 #include "amlcode.h" 15 16 #define _COMPONENT ACPI_EXECUTER 17 ACPI_MODULE_NAME("exfield") 18 19 /* 20 * This table maps the various Attrib protocols to the byte transfer 21 * length. Used for the generic serial bus. 22 */ 23 #define ACPI_INVALID_PROTOCOL_ID 0x80 24 #define ACPI_MAX_PROTOCOL_ID 0x0F 25 const u8 acpi_protocol_lengths[] = { 26 ACPI_INVALID_PROTOCOL_ID, /* 0 - reserved */ 27 ACPI_INVALID_PROTOCOL_ID, /* 1 - reserved */ 28 0x00, /* 2 - ATTRIB_QUICK */ 29 ACPI_INVALID_PROTOCOL_ID, /* 3 - reserved */ 30 0x01, /* 4 - ATTRIB_SEND_RECEIVE */ 31 ACPI_INVALID_PROTOCOL_ID, /* 5 - reserved */ 32 0x01, /* 6 - ATTRIB_BYTE */ 33 ACPI_INVALID_PROTOCOL_ID, /* 7 - reserved */ 34 0x02, /* 8 - ATTRIB_WORD */ 35 ACPI_INVALID_PROTOCOL_ID, /* 9 - reserved */ 36 0xFF, /* A - ATTRIB_BLOCK */ 37 0xFF, /* B - ATTRIB_BYTES */ 38 0x02, /* C - ATTRIB_PROCESS_CALL */ 39 0xFF, /* D - ATTRIB_BLOCK_PROCESS_CALL */ 40 0xFF, /* E - ATTRIB_RAW_BYTES */ 41 0xFF /* F - ATTRIB_RAW_PROCESS_BYTES */ 42 }; 43 44 /******************************************************************************* 45 * 46 * FUNCTION: acpi_ex_get_protocol_buffer_length 47 * 48 * PARAMETERS: protocol_id - The type of the protocol indicated by region 49 * field access attributes 50 * return_length - Where the protocol byte transfer length is 51 * returned 52 * 53 * RETURN: Status and decoded byte transfer length 54 * 55 * DESCRIPTION: This routine returns the length of the generic_serial_bus 56 * protocol bytes 57 * 58 ******************************************************************************/ 59 60 acpi_status 61 acpi_ex_get_protocol_buffer_length(u32 protocol_id, u32 *return_length) 62 { 63 64 if ((protocol_id > ACPI_MAX_PROTOCOL_ID) || 65 (acpi_protocol_lengths[protocol_id] == ACPI_INVALID_PROTOCOL_ID)) { 66 ACPI_ERROR((AE_INFO, 67 "Invalid Field/AccessAs protocol ID: 0x%4.4X", 68 protocol_id)); 69 70 return (AE_AML_PROTOCOL); 71 } 72 73 *return_length = acpi_protocol_lengths[protocol_id]; 74 return (AE_OK); 75 } 76 77 /******************************************************************************* 78 * 79 * FUNCTION: acpi_ex_read_data_from_field 80 * 81 * PARAMETERS: walk_state - Current execution state 82 * obj_desc - The named field 83 * ret_buffer_desc - Where the return data object is stored 84 * 85 * RETURN: Status 86 * 87 * DESCRIPTION: Read from a named field. Returns either an Integer or a 88 * Buffer, depending on the size of the field. 89 * 90 ******************************************************************************/ 91 92 acpi_status 93 acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, 94 union acpi_operand_object *obj_desc, 95 union acpi_operand_object **ret_buffer_desc) 96 { 97 acpi_status status; 98 union acpi_operand_object *buffer_desc; 99 void *buffer; 100 u32 buffer_length; 101 102 ACPI_FUNCTION_TRACE_PTR(ex_read_data_from_field, obj_desc); 103 104 /* Parameter validation */ 105 106 if (!obj_desc) { 107 return_ACPI_STATUS(AE_AML_NO_OPERAND); 108 } 109 if (!ret_buffer_desc) { 110 return_ACPI_STATUS(AE_BAD_PARAMETER); 111 } 112 113 if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) { 114 /* 115 * If the buffer_field arguments have not been previously evaluated, 116 * evaluate them now and save the results. 117 */ 118 if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { 119 status = acpi_ds_get_buffer_field_arguments(obj_desc); 120 if (ACPI_FAILURE(status)) { 121 return_ACPI_STATUS(status); 122 } 123 } 124 } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) && 125 (obj_desc->field.region_obj->region.space_id == 126 ACPI_ADR_SPACE_SMBUS 127 || obj_desc->field.region_obj->region.space_id == 128 ACPI_ADR_SPACE_GSBUS 129 || obj_desc->field.region_obj->region.space_id == 130 ACPI_ADR_SPACE_IPMI)) { 131 132 /* SMBus, GSBus, IPMI serial */ 133 134 status = acpi_ex_read_serial_bus(obj_desc, ret_buffer_desc); 135 return_ACPI_STATUS(status); 136 } 137 138 /* 139 * Allocate a buffer for the contents of the field. 140 * 141 * If the field is larger than the current integer width, create 142 * a BUFFER to hold it. Otherwise, use an INTEGER. This allows 143 * the use of arithmetic operators on the returned value if the 144 * field size is equal or smaller than an Integer. 145 * 146 * Note: Field.length is in bits. 147 */ 148 buffer_length = 149 (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.bit_length); 150 151 if (buffer_length > acpi_gbl_integer_byte_width) { 152 153 /* Field is too large for an Integer, create a Buffer instead */ 154 155 buffer_desc = acpi_ut_create_buffer_object(buffer_length); 156 if (!buffer_desc) { 157 return_ACPI_STATUS(AE_NO_MEMORY); 158 } 159 buffer = buffer_desc->buffer.pointer; 160 } else { 161 /* Field will fit within an Integer (normal case) */ 162 163 buffer_desc = acpi_ut_create_integer_object((u64) 0); 164 if (!buffer_desc) { 165 return_ACPI_STATUS(AE_NO_MEMORY); 166 } 167 168 buffer_length = acpi_gbl_integer_byte_width; 169 buffer = &buffer_desc->integer.value; 170 } 171 172 if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) && 173 (obj_desc->field.region_obj->region.space_id == 174 ACPI_ADR_SPACE_GPIO)) { 175 176 /* General Purpose I/O */ 177 178 status = acpi_ex_read_gpio(obj_desc, buffer); 179 goto exit; 180 } 181 182 ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, 183 "FieldRead [TO]: Obj %p, Type %X, Buf %p, ByteLen %X\n", 184 obj_desc, obj_desc->common.type, buffer, 185 buffer_length)); 186 ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, 187 "FieldRead [FROM]: BitLen %X, BitOff %X, ByteOff %X\n", 188 obj_desc->common_field.bit_length, 189 obj_desc->common_field.start_field_bit_offset, 190 obj_desc->common_field.base_byte_offset)); 191 192 /* Lock entire transaction if requested */ 193 194 acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); 195 196 /* Read from the field */ 197 198 status = acpi_ex_extract_from_field(obj_desc, buffer, buffer_length); 199 acpi_ex_release_global_lock(obj_desc->common_field.field_flags); 200 201 exit: 202 if (ACPI_FAILURE(status)) { 203 acpi_ut_remove_reference(buffer_desc); 204 } else { 205 *ret_buffer_desc = buffer_desc; 206 } 207 208 return_ACPI_STATUS(status); 209 } 210 211 /******************************************************************************* 212 * 213 * FUNCTION: acpi_ex_write_data_to_field 214 * 215 * PARAMETERS: source_desc - Contains data to write 216 * obj_desc - The named field 217 * result_desc - Where the return value is returned, if any 218 * 219 * RETURN: Status 220 * 221 * DESCRIPTION: Write to a named field 222 * 223 ******************************************************************************/ 224 225 acpi_status 226 acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, 227 union acpi_operand_object *obj_desc, 228 union acpi_operand_object **result_desc) 229 { 230 acpi_status status; 231 u32 buffer_length; 232 void *buffer; 233 234 ACPI_FUNCTION_TRACE_PTR(ex_write_data_to_field, obj_desc); 235 236 /* Parameter validation */ 237 238 if (!source_desc || !obj_desc) { 239 return_ACPI_STATUS(AE_AML_NO_OPERAND); 240 } 241 242 if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) { 243 /* 244 * If the buffer_field arguments have not been previously evaluated, 245 * evaluate them now and save the results. 246 */ 247 if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { 248 status = acpi_ds_get_buffer_field_arguments(obj_desc); 249 if (ACPI_FAILURE(status)) { 250 return_ACPI_STATUS(status); 251 } 252 } 253 } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) && 254 (obj_desc->field.region_obj->region.space_id == 255 ACPI_ADR_SPACE_GPIO)) { 256 257 /* General Purpose I/O */ 258 259 status = acpi_ex_write_gpio(source_desc, obj_desc, result_desc); 260 return_ACPI_STATUS(status); 261 } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) && 262 (obj_desc->field.region_obj->region.space_id == 263 ACPI_ADR_SPACE_SMBUS 264 || obj_desc->field.region_obj->region.space_id == 265 ACPI_ADR_SPACE_GSBUS 266 || obj_desc->field.region_obj->region.space_id == 267 ACPI_ADR_SPACE_IPMI)) { 268 269 /* SMBus, GSBus, IPMI serial */ 270 271 status = 272 acpi_ex_write_serial_bus(source_desc, obj_desc, 273 result_desc); 274 return_ACPI_STATUS(status); 275 } 276 277 /* Get a pointer to the data to be written */ 278 279 switch (source_desc->common.type) { 280 case ACPI_TYPE_INTEGER: 281 282 buffer = &source_desc->integer.value; 283 buffer_length = sizeof(source_desc->integer.value); 284 break; 285 286 case ACPI_TYPE_BUFFER: 287 288 buffer = source_desc->buffer.pointer; 289 buffer_length = source_desc->buffer.length; 290 break; 291 292 case ACPI_TYPE_STRING: 293 294 buffer = source_desc->string.pointer; 295 buffer_length = source_desc->string.length; 296 break; 297 298 default: 299 return_ACPI_STATUS(AE_AML_OPERAND_TYPE); 300 } 301 302 ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, 303 "FieldWrite [FROM]: Obj %p (%s:%X), Buf %p, ByteLen %X\n", 304 source_desc, 305 acpi_ut_get_type_name(source_desc->common.type), 306 source_desc->common.type, buffer, buffer_length)); 307 308 ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, 309 "FieldWrite [TO]: Obj %p (%s:%X), BitLen %X, BitOff %X, ByteOff %X\n", 310 obj_desc, 311 acpi_ut_get_type_name(obj_desc->common.type), 312 obj_desc->common.type, 313 obj_desc->common_field.bit_length, 314 obj_desc->common_field.start_field_bit_offset, 315 obj_desc->common_field.base_byte_offset)); 316 317 /* Lock entire transaction if requested */ 318 319 acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); 320 321 /* Write to the field */ 322 323 status = acpi_ex_insert_into_field(obj_desc, buffer, buffer_length); 324 acpi_ex_release_global_lock(obj_desc->common_field.field_flags); 325 return_ACPI_STATUS(status); 326 } 327