1/****************************************************************************** 2 * 3 * Module Name: aeregion - Handler for operation regions 4 * 5 *****************************************************************************/ 6 7/* 8 * Copyright (C) 2000 - 2023, 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 MERCHANTABILITY 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#include "aecommon.h" 45 46#define _COMPONENT ACPI_TOOLS 47 ACPI_MODULE_NAME ("aeregion") 48 49 50static AE_DEBUG_REGIONS AeRegions; 51 52 53/****************************************************************************** 54 * 55 * FUNCTION: AeRegionHandler 56 * 57 * PARAMETERS: Standard region handler parameters 58 * 59 * RETURN: Status 60 * 61 * DESCRIPTION: Test handler - Handles some dummy regions via memory that can 62 * be manipulated in Ring 3. Simulates actual reads and writes. 63 * 64 *****************************************************************************/ 65 66ACPI_STATUS 67AeRegionHandler ( 68 UINT32 Function, 69 ACPI_PHYSICAL_ADDRESS Address, 70 UINT32 BitWidth, 71 UINT64 *Value, 72 void *HandlerContext, 73 void *RegionContext) 74{ 75 76 ACPI_OPERAND_OBJECT *RegionObject = ACPI_CAST_PTR (ACPI_OPERAND_OBJECT, RegionContext); 77 UINT8 *Buffer = ACPI_CAST_PTR (UINT8, Value); 78 UINT8 *OldBuffer; 79 UINT8 *NewBuffer; 80 ACPI_PHYSICAL_ADDRESS BaseAddress; 81 ACPI_PHYSICAL_ADDRESS BaseAddressEnd; 82 ACPI_PHYSICAL_ADDRESS RegionAddress; 83 ACPI_PHYSICAL_ADDRESS RegionAddressEnd; 84 UINT32 Length; 85 UINT8 DataLength; 86 UINT8 *DataBuffer; 87 BOOLEAN BufferExists; 88 BOOLEAN BufferResize; 89 AE_REGION *RegionElement; 90 void *BufferValue; 91 ACPI_STATUS Status; 92 UINT32 ByteWidth; 93 UINT32 RegionLength; 94 UINT32 i; 95 UINT8 SpaceId; 96 ACPI_CONNECTION_INFO *MyContext; 97 UINT32 Value1; 98 UINT32 Value2; 99 ACPI_RESOURCE *Resource; 100 char Uuid[ACPI_PRM_INPUT_BUFFER_SIZE + 1]; 101 102 103 ACPI_FUNCTION_NAME (AeRegionHandler); 104 105 106 /* If the object is not a region, simply return */ 107 108 if (RegionObject->Region.Type != ACPI_TYPE_REGION) 109 { 110 return (AE_OK); 111 } 112 113 /* Check that we actually got back our context parameter */ 114 115 if (HandlerContext != &AeMyContext) 116 { 117 AcpiOsPrintf ( 118 "Region handler received incorrect context %p, should be %p\n", 119 HandlerContext, &AeMyContext); 120 } 121 122 MyContext = ACPI_CAST_PTR (ACPI_CONNECTION_INFO, HandlerContext); 123 124 /* 125 * Find the region's address space and length before searching 126 * the linked list. 127 */ 128 BaseAddress = RegionObject->Region.Address; 129 Length = RegionObject->Region.Length; 130 SpaceId = RegionObject->Region.SpaceId; 131 132 ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, 133 "Operation Region request on %s at 0x%X, BitWidth 0x%X, RegionLength 0x%X\n", 134 AcpiUtGetRegionName (RegionObject->Region.SpaceId), 135 (UINT32) Address, BitWidth, (UINT32) Length)); 136 137 /* 138 * Region support can be disabled with the -do option. 139 * We use this to support dynamically loaded tables where we pass a valid 140 * address to the AML. 141 */ 142 if (AcpiGbl_DbOpt_NoRegionSupport) 143 { 144 BufferValue = ACPI_TO_POINTER (Address); 145 ByteWidth = (BitWidth / 8); 146 147 if (BitWidth % 8) 148 { 149 ByteWidth += 1; 150 } 151 goto DoFunction; 152 } 153 154 switch (SpaceId) 155 { 156 case ACPI_ADR_SPACE_SYSTEM_IO: 157 /* 158 * For I/O space, exercise the port validation 159 * Note: ReadPort currently always returns all ones, length=BitLength 160 */ 161 switch (Function & ACPI_IO_MASK) 162 { 163 case ACPI_READ: 164 165 if (BitWidth == 64) 166 { 167 /* Split the 64-bit request into two 32-bit requests */ 168 169 Status = AcpiHwReadPort (Address, &Value1, 32); 170 ACPI_CHECK_OK (AcpiHwReadPort, Status); 171 Status = AcpiHwReadPort (Address+4, &Value2, 32); 172 ACPI_CHECK_OK (AcpiHwReadPort, Status); 173 174 *Value = Value1 | ((UINT64) Value2 << 32); 175 } 176 else 177 { 178 Status = AcpiHwReadPort (Address, &Value1, BitWidth); 179 ACPI_CHECK_OK (AcpiHwReadPort, Status); 180 *Value = (UINT64) Value1; 181 } 182 break; 183 184 case ACPI_WRITE: 185 186 if (BitWidth == 64) 187 { 188 /* Split the 64-bit request into two 32-bit requests */ 189 190 Status = AcpiHwWritePort (Address, ACPI_LODWORD (*Value), 32); 191 ACPI_CHECK_OK (AcpiHwWritePort, Status); 192 Status = AcpiHwWritePort (Address+4, ACPI_HIDWORD (*Value), 32); 193 ACPI_CHECK_OK (AcpiHwWritePort, Status); 194 } 195 else 196 { 197 Status = AcpiHwWritePort (Address, (UINT32) *Value, BitWidth); 198 ACPI_CHECK_OK (AcpiHwWritePort, Status); 199 } 200 break; 201 202 default: 203 204 Status = AE_BAD_PARAMETER; 205 break; 206 } 207 208 if (ACPI_FAILURE (Status)) 209 { 210 return (Status); 211 } 212 213 /* Now go ahead and simulate the hardware */ 214 break; 215 216 /* 217 * SMBus and GenericSerialBus support the various bidirectional 218 * protocols. 219 */ 220 case ACPI_ADR_SPACE_SMBUS: 221 case ACPI_ADR_SPACE_GSBUS: /* ACPI 5.0 */ 222 223 Status = AcpiExGetProtocolBufferLength ((Function >> 16), &Length); 224 if (ACPI_FAILURE (Status)) 225 { 226 AcpiOsPrintf ("AcpiExec: Invalid SMbus/GSbus protocol ID: 0x%X\n", 227 (Function >> 16)); 228 return (Status); 229 } 230 231 /* Adjust for fixed SMBus buffer size */ 232 233 if ((SpaceId == ACPI_ADR_SPACE_SMBUS) && 234 (Length > ACPI_SMBUS_DATA_SIZE)) 235 { 236 Length = ACPI_SMBUS_DATA_SIZE; /* SMBus buffer is fixed-length */ 237 } 238 239 if (AcpiGbl_DisplayRegionAccess) 240 { 241 AcpiOsPrintf ("AcpiExec: %s " 242 "%s: Attr %X Addr %.4X BaseAddr %.4X Length %.2X BitWidth %X BufLen %X\n", 243 AcpiUtGetRegionName (SpaceId), 244 (Function & ACPI_IO_MASK) ? "Write" : "Read ", 245 (UINT32) (Function >> 16), 246 (UINT32) Address, (UINT32) BaseAddress, 247 Length, BitWidth, Buffer[1]); 248 249 /* GenericSerialBus has a Connection() parameter */ 250 251 if ((SpaceId == ACPI_ADR_SPACE_GSBUS) && MyContext) 252 { 253 Status = AcpiBufferToResource (MyContext->Connection, 254 MyContext->Length, &Resource); 255 if (ACPI_SUCCESS (Status)) 256 { 257 ACPI_FREE (Resource); 258 } 259 260 AcpiOsPrintf (" [AccessLength %.2X Connection %p]", 261 MyContext->AccessLength, MyContext->Connection); 262 } 263 264 AcpiOsPrintf ("\n"); 265 } 266 267 DataBuffer = &Buffer[2]; 268 DataLength = (UINT8) Length; 269 270 /* Setup the return buffer. Note: ASLTS depends on these fill values */ 271 272 if (Length == ACPI_MAX_GSBUS_DATA_SIZE) 273 { 274 DataLength = 0x20; /* For ASLTS only */ 275 } 276 277 for (i = 0; i < Length; i++) 278 { 279 DataBuffer[i] = (UINT8) (0xA0 + i); 280 } 281 282 Buffer[0] = 0; /* Return Status, OK */ 283 Buffer[1] = DataLength; /* Length of valid data */ 284 return (AE_OK); 285 286 case ACPI_ADR_SPACE_IPMI: /* ACPI 4.0 */ 287 288 if (AcpiGbl_DisplayRegionAccess) 289 { 290 AcpiOsPrintf ("AcpiExec: IPMI " 291 "%s: Attr %X Addr %.4X BaseAddr %.4X Len %.2X Width %X BufLen %X\n", 292 (Function & ACPI_IO_MASK) ? "Write" : "Read ", 293 (UINT32) (Function >> 16), (UINT32) Address, (UINT32) BaseAddress, 294 Length, BitWidth, Buffer[1]); 295 } 296 297 /* 298 * Regardless of a READ or WRITE, this handler is passed a 66-byte 299 * buffer in which to return the IPMI status/length/data. 300 * 301 * Return some example data to show use of the bidirectional buffer 302 */ 303 Buffer[0] = 0; /* Status byte */ 304 Buffer[1] = ACPI_IPMI_DATA_SIZE; /* Return buffer data length */ 305 Buffer[2] = 0; /* Completion code */ 306 Buffer[3] = 0; /* Reserved */ 307 308 /* 309 * Fill the 66-byte buffer with the return data. 310 * Note: ASLTS depends on these fill values. 311 */ 312 for (i = 4; i < ACPI_IPMI_BUFFER_SIZE; i++) 313 { 314 Buffer[i] = (UINT8) (i); 315 } 316 return (AE_OK); 317 318 /* 319 * GPIO has some special semantics: 320 * 1) Address is the pin number index into the Connection() pin list 321 * 2) BitWidth is the actual number of bits (pins) defined by the field 322 */ 323 case ACPI_ADR_SPACE_GPIO: /* ACPI 5.0 */ 324 325 if (AcpiGbl_DisplayRegionAccess) 326 { 327 AcpiOsPrintf ("AcpiExec: GPIO " 328 "%s: Address %.4X Length %X BitWidth %X Conn %p\n", 329 (Function & ACPI_IO_MASK) ? "Write" : "Read ", 330 (UINT32) Address, Length, BitWidth, MyContext->Connection); 331 } 332 333 /* Now perform the "normal" SystemMemory handling, for AcpiExec only */ 334 break; 335 336 /* 337 * PCC operation region will write the entire subspace's data and expect 338 * a response from the hardware. For acpiexec, we'll fill the buffer with 339 * default values. Note: ASLTS will depend on these values. 340 */ 341 case ACPI_ADR_SPACE_PLATFORM_COMM: /* ACPI 6.3 */ 342 343 if (AcpiGbl_DisplayRegionAccess) 344 { 345 AcpiOsPrintf ("AcpiExec: PCC Write : Addr %.4X Width %X\n", 346 (UINT32) Address, BitWidth); 347 } 348 for (i = 0; i < Length; ++i) 349 { 350 Buffer[i] = (UINT8) i; 351 } 352 return (AE_OK); 353 354 case ACPI_ADR_SPACE_PLATFORM_RT: 355 356 AcpiOsPrintf ("Acpiexec: PRM %s invoked\n", 357 (Function & ACPI_IO_MASK) ? "Write" : "Read "); 358 359 if ((Function & ACPI_IO_MASK) == ACPI_WRITE) 360 { 361 AcpiUtConvertUuidToString((char *) Buffer + 10, Uuid); 362 AcpiOsPrintf ("Mode: %u GUID: %s\n", Buffer[0], Uuid); 363 } 364 365 /* Unpack the input buffer and print the contents for debug */ 366 367 break; 368 369 default: 370 break; 371 } 372 373 /* 374 * Search through the linked list for this region's buffer 375 */ 376 BufferExists = FALSE; 377 BufferResize = FALSE; 378 RegionElement = AeRegions.RegionList; 379 380 if (AeRegions.NumberOfRegions) 381 { 382 BaseAddressEnd = BaseAddress + Length - 1; 383 while (!BufferExists && RegionElement) 384 { 385 RegionAddress = RegionElement->Address; 386 RegionAddressEnd = RegionElement->Address + RegionElement->Length - 1; 387 RegionLength = RegionElement->Length; 388 389 /* 390 * Overlapping Region Support 391 * 392 * While searching through the region buffer list, determine if an 393 * overlap exists between the requested buffer space and the current 394 * RegionElement space. If there is an overlap then replace the old 395 * buffer with a new buffer of increased size before continuing to 396 * do the read or write 397 */ 398 if (RegionElement->SpaceId != SpaceId || 399 BaseAddressEnd < RegionAddress || 400 BaseAddress > RegionAddressEnd) 401 { 402 /* 403 * Requested buffer is outside of the current RegionElement 404 * bounds 405 */ 406 RegionElement = RegionElement->NextRegion; 407 } 408 else 409 { 410 /* 411 * Some amount of buffer space sharing exists. There are 4 cases 412 * to consider: 413 * 414 * 1. Right overlap 415 * 2. Left overlap 416 * 3. Left and right overlap 417 * 4. Fully contained - no resizing required 418 */ 419 BufferExists = TRUE; 420 421 if ((BaseAddress >= RegionAddress) && 422 (BaseAddress <= RegionAddressEnd) && 423 (BaseAddressEnd > RegionAddressEnd)) 424 { 425 /* Right overlap */ 426 427 RegionElement->Length = (UINT32) (BaseAddress - 428 RegionAddress + Length); 429 BufferResize = TRUE; 430 } 431 432 else if ((BaseAddressEnd >= RegionAddress) && 433 (BaseAddressEnd <= RegionAddressEnd) && 434 (BaseAddress < RegionAddress)) 435 { 436 /* Left overlap */ 437 438 RegionElement->Address = BaseAddress; 439 RegionElement->Length = (UINT32) (RegionAddress - 440 BaseAddress + RegionElement->Length); 441 BufferResize = TRUE; 442 } 443 444 else if ((BaseAddress < RegionAddress) && 445 (BaseAddressEnd > RegionAddressEnd)) 446 { 447 /* Left and right overlap */ 448 449 RegionElement->Address = BaseAddress; 450 RegionElement->Length = Length; 451 BufferResize = TRUE; 452 } 453 454 /* 455 * only remaining case is fully contained for which we don't 456 * need to do anything 457 */ 458 if (BufferResize) 459 { 460 NewBuffer = AcpiOsAllocate (RegionElement->Length); 461 if (!NewBuffer) 462 { 463 return (AE_NO_MEMORY); 464 } 465 466 OldBuffer = RegionElement->Buffer; 467 RegionElement->Buffer = NewBuffer; 468 NewBuffer = NULL; 469 470 /* Initialize the region with the default fill value */ 471 472 memset (RegionElement->Buffer, 473 AcpiGbl_RegionFillValue, RegionElement->Length); 474 475 /* 476 * Get BufferValue to point (within the new buffer) to the 477 * base address of the old buffer 478 */ 479 BufferValue = (UINT8 *) RegionElement->Buffer + 480 (UINT64) RegionAddress - 481 (UINT64) RegionElement->Address; 482 483 /* 484 * Copy the old buffer to its same location within the new 485 * buffer 486 */ 487 memcpy (BufferValue, OldBuffer, RegionLength); 488 AcpiOsFree (OldBuffer); 489 } 490 } 491 } 492 } 493 494 /* 495 * If the Region buffer does not exist, create it now 496 */ 497 if (!BufferExists) 498 { 499 /* Do the memory allocations first */ 500 501 RegionElement = AcpiOsAllocate (sizeof (AE_REGION)); 502 if (!RegionElement) 503 { 504 return (AE_NO_MEMORY); 505 } 506 507 RegionElement->Buffer = AcpiOsAllocate (Length); 508 if (!RegionElement->Buffer) 509 { 510 AcpiOsFree (RegionElement); 511 return (AE_NO_MEMORY); 512 } 513 514 /* Initialize the region with the default fill value */ 515 516 memset (RegionElement->Buffer, AcpiGbl_RegionFillValue, Length); 517 518 RegionElement->Address = BaseAddress; 519 RegionElement->Length = Length; 520 RegionElement->SpaceId = SpaceId; 521 RegionElement->NextRegion = NULL; 522 523 /* 524 * Increment the number of regions and put this one 525 * at the head of the list as it will probably get accessed 526 * more often anyway. 527 */ 528 AeRegions.NumberOfRegions += 1; 529 530 if (AeRegions.RegionList) 531 { 532 RegionElement->NextRegion = AeRegions.RegionList; 533 } 534 535 AeRegions.RegionList = RegionElement; 536 } 537 538 /* Calculate the size of the memory copy */ 539 540 ByteWidth = (BitWidth / 8); 541 if (BitWidth % 8) 542 { 543 ByteWidth += 1; 544 } 545 546 /* 547 * The buffer exists and is pointed to by RegionElement. 548 * We now need to verify the request is valid and perform the operation. 549 * 550 * NOTE: RegionElement->Length is in bytes, therefore it we compare against 551 * ByteWidth (see above) 552 */ 553 if ((RegionObject->Region.SpaceId != ACPI_ADR_SPACE_GPIO) && 554 ((UINT64) Address + ByteWidth) > 555 ((UINT64)(RegionElement->Address) + RegionElement->Length)) 556 { 557 ACPI_WARNING ((AE_INFO, 558 "Request on [%4.4s] is beyond region limit " 559 "Req-0x%X+0x%X, Base=0x%X, Len-0x%X", 560 (RegionObject->Region.Node)->Name.Ascii, (UINT32) Address, 561 ByteWidth, (UINT32)(RegionElement->Address), 562 RegionElement->Length)); 563 564 return (AE_AML_REGION_LIMIT); 565 } 566 567 /* 568 * Get BufferValue to point to the "address" in the buffer 569 */ 570 BufferValue = ((UINT8 *) RegionElement->Buffer + 571 ((UINT64) Address - (UINT64) RegionElement->Address)); 572 573DoFunction: 574 /* 575 * Perform a read or write to the buffer space 576 */ 577 switch (Function) 578 { 579 case ACPI_READ: 580 /* 581 * Set the pointer Value to whatever is in the buffer 582 */ 583 memcpy (Value, BufferValue, ByteWidth); 584 break; 585 586 case ACPI_WRITE: 587 /* 588 * Write the contents of Value to the buffer 589 */ 590 memcpy (BufferValue, Value, ByteWidth); 591 break; 592 593 default: 594 595 return (AE_BAD_PARAMETER); 596 } 597 598 if (AcpiGbl_DisplayRegionAccess) 599 { 600 switch (SpaceId) 601 { 602 case ACPI_ADR_SPACE_SYSTEM_MEMORY: 603 604 AcpiOsPrintf ("AcpiExec: SystemMemory " 605 "%s: Val %.8X Addr %.4X BitWidth %X [REGION: BaseAddr %.4X Len %.2X]\n", 606 (Function & ACPI_IO_MASK) ? "Write" : "Read ", 607 (UINT32) *Value, (UINT32) Address, BitWidth, (UINT32) BaseAddress, Length); 608 break; 609 610 case ACPI_ADR_SPACE_GSBUS: 611 612 AcpiOsPrintf ("AcpiExec: GenericSerialBus\n"); 613 break; 614 615 case ACPI_ADR_SPACE_GPIO: /* ACPI 5.0 */ 616 617 /* This space is required to always be ByteAcc */ 618 619 Status = AcpiBufferToResource (MyContext->Connection, 620 MyContext->Length, &Resource); 621 622 AcpiOsPrintf ("AcpiExec: GeneralPurposeIo " 623 "%s: %.8X Addr %.4X BaseAddr %.4X Length %.2X " 624 "BitWidth %X AccLen %.2X Conn %p\n", 625 (Function & ACPI_IO_MASK) ? "Write" : "Read ", (UINT32) *Value, 626 (UINT32) Address, (UINT32) BaseAddress, Length, BitWidth, 627 MyContext->AccessLength, MyContext->Connection); 628 if (ACPI_SUCCESS (Status)) 629 { 630 ACPI_FREE (Resource); 631 } 632 break; 633 634 default: 635 636 AcpiOsPrintf ("AcpiExec: Region access on SpaceId %2.2X\n", SpaceId); 637 break; 638 } 639 } 640 641 return (AE_OK); 642} 643