1 /****************************************************************************** 2 * 3 * Module Name: exfield - ACPI AML (p-code) execution - field manipulation 4 * 5 *****************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2014, Intel Corp. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions, and the following disclaimer, 16 * without modification. 17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 18 * substantially similar to the "NO WARRANTY" disclaimer below 19 * ("Disclaimer") and any redistribution must be conditioned upon 20 * including a substantially similar Disclaimer requirement for further 21 * binary redistribution. 22 * 3. Neither the names of the above-listed copyright holders nor the names 23 * of any contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * Alternatively, this software may be distributed under the terms of the 27 * GNU General Public License ("GPL") version 2 as published by the Free 28 * Software Foundation. 29 * 30 * NO WARRANTY 31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 * POSSIBILITY OF SUCH DAMAGES. 42 */ 43 44 45 #define __EXFIELD_C__ 46 47 #include "acpi.h" 48 #include "accommon.h" 49 #include "acdispat.h" 50 #include "acinterp.h" 51 #include "amlcode.h" 52 53 54 #define _COMPONENT ACPI_EXECUTER 55 ACPI_MODULE_NAME ("exfield") 56 57 /* Local prototypes */ 58 59 static UINT32 60 AcpiExGetSerialAccessLength ( 61 UINT32 AccessorType, 62 UINT32 AccessLength); 63 64 65 /******************************************************************************* 66 * 67 * FUNCTION: AcpiExGetSerialAccessLength 68 * 69 * PARAMETERS: AccessorType - The type of the protocol indicated by region 70 * field access attributes 71 * AccessLength - The access length of the region field 72 * 73 * RETURN: Decoded access length 74 * 75 * DESCRIPTION: This routine returns the length of the GenericSerialBus 76 * protocol bytes 77 * 78 ******************************************************************************/ 79 80 static UINT32 81 AcpiExGetSerialAccessLength ( 82 UINT32 AccessorType, 83 UINT32 AccessLength) 84 { 85 UINT32 Length; 86 87 88 switch (AccessorType) 89 { 90 case AML_FIELD_ATTRIB_QUICK: 91 92 Length = 0; 93 break; 94 95 case AML_FIELD_ATTRIB_SEND_RCV: 96 case AML_FIELD_ATTRIB_BYTE: 97 98 Length = 1; 99 break; 100 101 case AML_FIELD_ATTRIB_WORD: 102 case AML_FIELD_ATTRIB_WORD_CALL: 103 104 Length = 2; 105 break; 106 107 case AML_FIELD_ATTRIB_MULTIBYTE: 108 case AML_FIELD_ATTRIB_RAW_BYTES: 109 case AML_FIELD_ATTRIB_RAW_PROCESS: 110 111 Length = AccessLength; 112 break; 113 114 case AML_FIELD_ATTRIB_BLOCK: 115 case AML_FIELD_ATTRIB_BLOCK_CALL: 116 default: 117 118 Length = ACPI_GSBUS_BUFFER_SIZE - 2; 119 break; 120 } 121 122 return (Length); 123 } 124 125 126 /******************************************************************************* 127 * 128 * FUNCTION: AcpiExReadDataFromField 129 * 130 * PARAMETERS: WalkState - Current execution state 131 * ObjDesc - The named field 132 * RetBufferDesc - Where the return data object is stored 133 * 134 * RETURN: Status 135 * 136 * DESCRIPTION: Read from a named field. Returns either an Integer or a 137 * Buffer, depending on the size of the field. 138 * 139 ******************************************************************************/ 140 141 ACPI_STATUS 142 AcpiExReadDataFromField ( 143 ACPI_WALK_STATE *WalkState, 144 ACPI_OPERAND_OBJECT *ObjDesc, 145 ACPI_OPERAND_OBJECT **RetBufferDesc) 146 { 147 ACPI_STATUS Status; 148 ACPI_OPERAND_OBJECT *BufferDesc; 149 ACPI_SIZE Length; 150 void *Buffer; 151 UINT32 Function; 152 UINT16 AccessorType; 153 154 155 ACPI_FUNCTION_TRACE_PTR (ExReadDataFromField, ObjDesc); 156 157 158 /* Parameter validation */ 159 160 if (!ObjDesc) 161 { 162 return_ACPI_STATUS (AE_AML_NO_OPERAND); 163 } 164 if (!RetBufferDesc) 165 { 166 return_ACPI_STATUS (AE_BAD_PARAMETER); 167 } 168 169 if (ObjDesc->Common.Type == ACPI_TYPE_BUFFER_FIELD) 170 { 171 /* 172 * If the BufferField arguments have not been previously evaluated, 173 * evaluate them now and save the results. 174 */ 175 if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID)) 176 { 177 Status = AcpiDsGetBufferFieldArguments (ObjDesc); 178 if (ACPI_FAILURE (Status)) 179 { 180 return_ACPI_STATUS (Status); 181 } 182 } 183 } 184 else if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) && 185 (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS || 186 ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS || 187 ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_IPMI)) 188 { 189 /* 190 * This is an SMBus, GSBus or IPMI read. We must create a buffer to hold 191 * the data and then directly access the region handler. 192 * 193 * Note: SMBus and GSBus protocol value is passed in upper 16-bits of Function 194 */ 195 if (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS) 196 { 197 Length = ACPI_SMBUS_BUFFER_SIZE; 198 Function = ACPI_READ | (ObjDesc->Field.Attribute << 16); 199 } 200 else if (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS) 201 { 202 AccessorType = ObjDesc->Field.Attribute; 203 Length = AcpiExGetSerialAccessLength (AccessorType, 204 ObjDesc->Field.AccessLength); 205 206 /* 207 * Add additional 2 bytes for the GenericSerialBus data buffer: 208 * 209 * Status; (Byte 0 of the data buffer) 210 * Length; (Byte 1 of the data buffer) 211 * Data[x-1]; (Bytes 2-x of the arbitrary length data buffer) 212 */ 213 Length += 2; 214 Function = ACPI_READ | (AccessorType << 16); 215 } 216 else /* IPMI */ 217 { 218 Length = ACPI_IPMI_BUFFER_SIZE; 219 Function = ACPI_READ; 220 } 221 222 BufferDesc = AcpiUtCreateBufferObject (Length); 223 if (!BufferDesc) 224 { 225 return_ACPI_STATUS (AE_NO_MEMORY); 226 } 227 228 /* Lock entire transaction if requested */ 229 230 AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags); 231 232 /* Call the region handler for the read */ 233 234 Status = AcpiExAccessRegion (ObjDesc, 0, 235 ACPI_CAST_PTR (UINT64, BufferDesc->Buffer.Pointer), 236 Function); 237 AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags); 238 goto Exit; 239 } 240 241 /* 242 * Allocate a buffer for the contents of the field. 243 * 244 * If the field is larger than the current integer width, create 245 * a BUFFER to hold it. Otherwise, use an INTEGER. This allows 246 * the use of arithmetic operators on the returned value if the 247 * field size is equal or smaller than an Integer. 248 * 249 * Note: Field.length is in bits. 250 */ 251 Length = (ACPI_SIZE) ACPI_ROUND_BITS_UP_TO_BYTES (ObjDesc->Field.BitLength); 252 if (Length > AcpiGbl_IntegerByteWidth) 253 { 254 /* Field is too large for an Integer, create a Buffer instead */ 255 256 BufferDesc = AcpiUtCreateBufferObject (Length); 257 if (!BufferDesc) 258 { 259 return_ACPI_STATUS (AE_NO_MEMORY); 260 } 261 Buffer = BufferDesc->Buffer.Pointer; 262 } 263 else 264 { 265 /* Field will fit within an Integer (normal case) */ 266 267 BufferDesc = AcpiUtCreateIntegerObject ((UINT64) 0); 268 if (!BufferDesc) 269 { 270 return_ACPI_STATUS (AE_NO_MEMORY); 271 } 272 273 Length = AcpiGbl_IntegerByteWidth; 274 Buffer = &BufferDesc->Integer.Value; 275 } 276 277 ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, 278 "FieldRead [TO]: Obj %p, Type %X, Buf %p, ByteLen %X\n", 279 ObjDesc, ObjDesc->Common.Type, Buffer, (UINT32) Length)); 280 ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, 281 "FieldRead [FROM]: BitLen %X, BitOff %X, ByteOff %X\n", 282 ObjDesc->CommonField.BitLength, 283 ObjDesc->CommonField.StartFieldBitOffset, 284 ObjDesc->CommonField.BaseByteOffset)); 285 286 /* Lock entire transaction if requested */ 287 288 AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags); 289 290 /* Read from the field */ 291 292 Status = AcpiExExtractFromField (ObjDesc, Buffer, (UINT32) Length); 293 AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags); 294 295 296 Exit: 297 if (ACPI_FAILURE (Status)) 298 { 299 AcpiUtRemoveReference (BufferDesc); 300 } 301 else 302 { 303 *RetBufferDesc = BufferDesc; 304 } 305 306 return_ACPI_STATUS (Status); 307 } 308 309 310 /******************************************************************************* 311 * 312 * FUNCTION: AcpiExWriteDataToField 313 * 314 * PARAMETERS: SourceDesc - Contains data to write 315 * ObjDesc - The named field 316 * ResultDesc - Where the return value is returned, if any 317 * 318 * RETURN: Status 319 * 320 * DESCRIPTION: Write to a named field 321 * 322 ******************************************************************************/ 323 324 ACPI_STATUS 325 AcpiExWriteDataToField ( 326 ACPI_OPERAND_OBJECT *SourceDesc, 327 ACPI_OPERAND_OBJECT *ObjDesc, 328 ACPI_OPERAND_OBJECT **ResultDesc) 329 { 330 ACPI_STATUS Status; 331 UINT32 Length; 332 void *Buffer; 333 ACPI_OPERAND_OBJECT *BufferDesc; 334 UINT32 Function; 335 UINT16 AccessorType; 336 337 338 ACPI_FUNCTION_TRACE_PTR (ExWriteDataToField, ObjDesc); 339 340 341 /* Parameter validation */ 342 343 if (!SourceDesc || !ObjDesc) 344 { 345 return_ACPI_STATUS (AE_AML_NO_OPERAND); 346 } 347 348 if (ObjDesc->Common.Type == ACPI_TYPE_BUFFER_FIELD) 349 { 350 /* 351 * If the BufferField arguments have not been previously evaluated, 352 * evaluate them now and save the results. 353 */ 354 if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID)) 355 { 356 Status = AcpiDsGetBufferFieldArguments (ObjDesc); 357 if (ACPI_FAILURE (Status)) 358 { 359 return_ACPI_STATUS (Status); 360 } 361 } 362 } 363 else if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) && 364 (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS || 365 ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS || 366 ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_IPMI)) 367 { 368 /* 369 * This is an SMBus, GSBus or IPMI write. We will bypass the entire field 370 * mechanism and handoff the buffer directly to the handler. For 371 * these address spaces, the buffer is bi-directional; on a write, 372 * return data is returned in the same buffer. 373 * 374 * Source must be a buffer of sufficient size: 375 * ACPI_SMBUS_BUFFER_SIZE, ACPI_GSBUS_BUFFER_SIZE, or ACPI_IPMI_BUFFER_SIZE. 376 * 377 * Note: SMBus and GSBus protocol type is passed in upper 16-bits of Function 378 */ 379 if (SourceDesc->Common.Type != ACPI_TYPE_BUFFER) 380 { 381 ACPI_ERROR ((AE_INFO, 382 "SMBus/IPMI/GenericSerialBus write requires Buffer, found type %s", 383 AcpiUtGetObjectTypeName (SourceDesc))); 384 385 return_ACPI_STATUS (AE_AML_OPERAND_TYPE); 386 } 387 388 if (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS) 389 { 390 Length = ACPI_SMBUS_BUFFER_SIZE; 391 Function = ACPI_WRITE | (ObjDesc->Field.Attribute << 16); 392 } 393 else if (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS) 394 { 395 AccessorType = ObjDesc->Field.Attribute; 396 Length = AcpiExGetSerialAccessLength (AccessorType, 397 ObjDesc->Field.AccessLength); 398 399 /* 400 * Add additional 2 bytes for the GenericSerialBus data buffer: 401 * 402 * Status; (Byte 0 of the data buffer) 403 * Length; (Byte 1 of the data buffer) 404 * Data[x-1]; (Bytes 2-x of the arbitrary length data buffer) 405 */ 406 Length += 2; 407 Function = ACPI_WRITE | (AccessorType << 16); 408 } 409 else /* IPMI */ 410 { 411 Length = ACPI_IPMI_BUFFER_SIZE; 412 Function = ACPI_WRITE; 413 } 414 415 if (SourceDesc->Buffer.Length < Length) 416 { 417 ACPI_ERROR ((AE_INFO, 418 "SMBus/IPMI/GenericSerialBus write requires Buffer of length %u, found length %u", 419 Length, SourceDesc->Buffer.Length)); 420 421 return_ACPI_STATUS (AE_AML_BUFFER_LIMIT); 422 } 423 424 /* Create the bi-directional buffer */ 425 426 BufferDesc = AcpiUtCreateBufferObject (Length); 427 if (!BufferDesc) 428 { 429 return_ACPI_STATUS (AE_NO_MEMORY); 430 } 431 432 Buffer = BufferDesc->Buffer.Pointer; 433 ACPI_MEMCPY (Buffer, SourceDesc->Buffer.Pointer, Length); 434 435 /* Lock entire transaction if requested */ 436 437 AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags); 438 439 /* 440 * Perform the write (returns status and perhaps data in the 441 * same buffer) 442 */ 443 Status = AcpiExAccessRegion (ObjDesc, 0, 444 (UINT64 *) Buffer, Function); 445 AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags); 446 447 *ResultDesc = BufferDesc; 448 return_ACPI_STATUS (Status); 449 } 450 451 /* Get a pointer to the data to be written */ 452 453 switch (SourceDesc->Common.Type) 454 { 455 case ACPI_TYPE_INTEGER: 456 457 Buffer = &SourceDesc->Integer.Value; 458 Length = sizeof (SourceDesc->Integer.Value); 459 break; 460 461 case ACPI_TYPE_BUFFER: 462 463 Buffer = SourceDesc->Buffer.Pointer; 464 Length = SourceDesc->Buffer.Length; 465 break; 466 467 case ACPI_TYPE_STRING: 468 469 Buffer = SourceDesc->String.Pointer; 470 Length = SourceDesc->String.Length; 471 break; 472 473 default: 474 475 return_ACPI_STATUS (AE_AML_OPERAND_TYPE); 476 } 477 478 ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, 479 "FieldWrite [FROM]: Obj %p (%s:%X), Buf %p, ByteLen %X\n", 480 SourceDesc, AcpiUtGetTypeName (SourceDesc->Common.Type), 481 SourceDesc->Common.Type, Buffer, Length)); 482 483 ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, 484 "FieldWrite [TO]: Obj %p (%s:%X), BitLen %X, BitOff %X, ByteOff %X\n", 485 ObjDesc, AcpiUtGetTypeName (ObjDesc->Common.Type), 486 ObjDesc->Common.Type, 487 ObjDesc->CommonField.BitLength, 488 ObjDesc->CommonField.StartFieldBitOffset, 489 ObjDesc->CommonField.BaseByteOffset)); 490 491 /* Lock entire transaction if requested */ 492 493 AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags); 494 495 /* Write to the field */ 496 497 Status = AcpiExInsertIntoField (ObjDesc, Buffer, Length); 498 AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags); 499 500 return_ACPI_STATUS (Status); 501 } 502