xref: /netbsd-src/sys/external/bsd/acpica/dist/tools/acpiexec/aeregion.c (revision 046a29855e04359424fd074e8313af6b6be8cfb6)
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 
50 static 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 
66 ACPI_STATUS
AeRegionHandler(UINT32 Function,ACPI_PHYSICAL_ADDRESS Address,UINT32 BitWidth,UINT64 * Value,void * HandlerContext,void * RegionContext)67 AeRegionHandler (
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 
573 DoFunction:
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