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