1/******************************************************************************
2 *
3 * Module Name: exfield - AML execution - FieldUnit read/write
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 "acpi.h"
45#include "accommon.h"
46#include "acdispat.h"
47#include "acinterp.h"
48#include "amlcode.h"
49
50
51#define _COMPONENT          ACPI_EXECUTER
52        ACPI_MODULE_NAME    ("exfield")
53
54
55/*
56 * This table maps the various Attrib protocols to the byte transfer
57 * length. Used for the generic serial bus.
58 */
59#define ACPI_INVALID_PROTOCOL_ID        0x80
60#define ACPI_MAX_PROTOCOL_ID            0x0F
61
62static const UINT8      AcpiProtocolLengths[] =
63{
64    ACPI_INVALID_PROTOCOL_ID,   /* 0 - reserved */
65    ACPI_INVALID_PROTOCOL_ID,   /* 1 - reserved */
66    0x00,                       /* 2 - ATTRIB_QUICK */
67    ACPI_INVALID_PROTOCOL_ID,   /* 3 - reserved */
68    0x01,                       /* 4 - ATTRIB_SEND_RECEIVE */
69    ACPI_INVALID_PROTOCOL_ID,   /* 5 - reserved */
70    0x01,                       /* 6 - ATTRIB_BYTE */
71    ACPI_INVALID_PROTOCOL_ID,   /* 7 - reserved */
72    0x02,                       /* 8 - ATTRIB_WORD */
73    ACPI_INVALID_PROTOCOL_ID,   /* 9 - reserved */
74    0xFF,                       /* A - ATTRIB_BLOCK  */
75    0xFF,                       /* B - ATTRIB_BYTES */
76    0x02,                       /* C - ATTRIB_PROCESS_CALL */
77    0xFF,                       /* D - ATTRIB_BLOCK_PROCESS_CALL */
78    0xFF,                       /* E - ATTRIB_RAW_BYTES */
79    0xFF                        /* F - ATTRIB_RAW_PROCESS_BYTES */
80};
81
82#define PCC_MASTER_SUBSPACE     3
83
84/*
85 * The following macros determine a given offset is a COMD field.
86 * According to the specification, generic subspaces (types 0-2) contains a
87 * 2-byte COMD field at offset 4 and master subspaces (type 3) contains a 4-byte
88 * COMD field starting at offset 12.
89 */
90#define GENERIC_SUBSPACE_COMMAND(a)     (4 == a || a == 5)
91#define MASTER_SUBSPACE_COMMAND(a)      (12 <= a && a <= 15)
92
93
94/*******************************************************************************
95 *
96 * FUNCTION:    AcpiExGetProtocolBufferLength
97 *
98 * PARAMETERS:  ProtocolId      - The type of the protocol indicated by region
99 *                                field access attributes
100 *              ReturnLength    - Where the protocol byte transfer length is
101 *                                returned
102 *
103 * RETURN:      Status and decoded byte transfer length
104 *
105 * DESCRIPTION: This routine returns the length of the GenericSerialBus
106 *              protocol bytes
107 *
108 ******************************************************************************/
109
110ACPI_STATUS
111AcpiExGetProtocolBufferLength (
112    UINT32                  ProtocolId,
113    UINT32                  *ReturnLength)
114{
115
116    if ((ProtocolId > ACPI_MAX_PROTOCOL_ID) ||
117        (AcpiProtocolLengths[ProtocolId] == ACPI_INVALID_PROTOCOL_ID))
118    {
119        ACPI_ERROR ((AE_INFO,
120            "Invalid Field/AccessAs protocol ID: 0x%4.4X", ProtocolId));
121
122        return (AE_AML_PROTOCOL);
123    }
124
125    *ReturnLength = AcpiProtocolLengths[ProtocolId];
126    return (AE_OK);
127}
128
129
130/*******************************************************************************
131 *
132 * FUNCTION:    AcpiExReadDataFromField
133 *
134 * PARAMETERS:  WalkState           - Current execution state
135 *              ObjDesc             - The named field
136 *              RetBufferDesc       - Where the return data object is stored
137 *
138 * RETURN:      Status
139 *
140 * DESCRIPTION: Read from a named field. Returns either an Integer or a
141 *              Buffer, depending on the size of the field and whether if a
142 *              field is created by the CreateField() operator.
143 *
144 ******************************************************************************/
145
146ACPI_STATUS
147AcpiExReadDataFromField (
148    ACPI_WALK_STATE         *WalkState,
149    ACPI_OPERAND_OBJECT     *ObjDesc,
150    ACPI_OPERAND_OBJECT     **RetBufferDesc)
151{
152    ACPI_STATUS             Status;
153    ACPI_OPERAND_OBJECT     *BufferDesc;
154    void                    *Buffer;
155    UINT32                  BufferLength;
156
157
158    ACPI_FUNCTION_TRACE_PTR (ExReadDataFromField, ObjDesc);
159
160
161    /* Parameter validation */
162
163    if (!ObjDesc)
164    {
165        return_ACPI_STATUS (AE_AML_NO_OPERAND);
166    }
167    if (!RetBufferDesc)
168    {
169        return_ACPI_STATUS (AE_BAD_PARAMETER);
170    }
171
172    if (ObjDesc->Common.Type == ACPI_TYPE_BUFFER_FIELD)
173    {
174        /*
175         * If the BufferField arguments have not been previously evaluated,
176         * evaluate them now and save the results.
177         */
178        if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
179        {
180            Status = AcpiDsGetBufferFieldArguments (ObjDesc);
181            if (ACPI_FAILURE (Status))
182            {
183                return_ACPI_STATUS (Status);
184            }
185        }
186    }
187    else if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
188        (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS ||
189         ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS ||
190         ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_IPMI  ||
191         ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_PLATFORM_RT ||
192         ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_FIXED_HARDWARE))
193    {
194        /* SMBus, GSBus, IPMI serial */
195
196        Status = AcpiExReadSerialBus (ObjDesc, RetBufferDesc);
197        return_ACPI_STATUS (Status);
198    }
199
200    /*
201     * Allocate a buffer for the contents of the field.
202     *
203     * If the field is larger than the current integer width, create
204     * a BUFFER to hold it. Otherwise, use an INTEGER. This allows
205     * the use of arithmetic operators on the returned value if the
206     * field size is equal or smaller than an Integer.
207     *
208     * However, all buffer fields created by CreateField operator needs to
209     * remain as a buffer to match other AML interpreter implementations.
210     *
211     * Note: Field.length is in bits.
212     */
213    BufferLength = (ACPI_SIZE) ACPI_ROUND_BITS_UP_TO_BYTES (
214        ObjDesc->Field.BitLength);
215
216    if (BufferLength > AcpiGbl_IntegerByteWidth ||
217        (ObjDesc->Common.Type == ACPI_TYPE_BUFFER_FIELD &&
218        ObjDesc->BufferField.IsCreateField))
219    {
220        /* Field is too large for an Integer, create a Buffer instead */
221
222        BufferDesc = AcpiUtCreateBufferObject (BufferLength);
223        if (!BufferDesc)
224        {
225            return_ACPI_STATUS (AE_NO_MEMORY);
226        }
227        Buffer = BufferDesc->Buffer.Pointer;
228    }
229    else
230    {
231        /* Field will fit within an Integer (normal case) */
232
233        BufferDesc = AcpiUtCreateIntegerObject ((UINT64) 0);
234        if (!BufferDesc)
235        {
236            return_ACPI_STATUS (AE_NO_MEMORY);
237        }
238
239        BufferLength = AcpiGbl_IntegerByteWidth;
240        Buffer = &BufferDesc->Integer.Value;
241    }
242
243    if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
244        (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GPIO))
245    {
246        /* General Purpose I/O */
247
248        Status = AcpiExReadGpio (ObjDesc, Buffer);
249        goto Exit;
250    }
251    else if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
252        (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_PLATFORM_COMM))
253    {
254        /*
255         * Reading from a PCC field unit does not require the handler because
256         * it only requires reading from the InternalPccBuffer.
257         */
258        ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
259            "PCC FieldRead bits %u\n", ObjDesc->Field.BitLength));
260
261        memcpy (Buffer, ObjDesc->Field.RegionObj->Field.InternalPccBuffer +
262        ObjDesc->Field.BaseByteOffset, (ACPI_SIZE) ACPI_ROUND_BITS_UP_TO_BYTES (
263            ObjDesc->Field.BitLength));
264
265        *RetBufferDesc = BufferDesc;
266        return AE_OK;
267    }
268
269    ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
270        "FieldRead [TO]:   Obj %p, Type %X, Buf %p, ByteLen %X\n",
271        ObjDesc, ObjDesc->Common.Type, Buffer, BufferLength));
272    ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
273        "FieldRead [FROM]: BitLen %X, BitOff %X, ByteOff %X\n",
274        ObjDesc->CommonField.BitLength,
275        ObjDesc->CommonField.StartFieldBitOffset,
276        ObjDesc->CommonField.BaseByteOffset));
277
278    /* Lock entire transaction if requested */
279
280    AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags);
281
282    /* Read from the field */
283
284    Status = AcpiExExtractFromField (ObjDesc, Buffer, BufferLength);
285    AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags);
286
287
288Exit:
289    if (ACPI_FAILURE (Status))
290    {
291        AcpiUtRemoveReference (BufferDesc);
292    }
293    else
294    {
295        *RetBufferDesc = BufferDesc;
296    }
297
298    return_ACPI_STATUS (Status);
299}
300
301
302/*******************************************************************************
303 *
304 * FUNCTION:    AcpiExWriteDataToField
305 *
306 * PARAMETERS:  SourceDesc          - Contains data to write
307 *              ObjDesc             - The named field
308 *              ResultDesc          - Where the return value is returned, if any
309 *
310 * RETURN:      Status
311 *
312 * DESCRIPTION: Write to a named field
313 *
314 ******************************************************************************/
315
316ACPI_STATUS
317AcpiExWriteDataToField (
318    ACPI_OPERAND_OBJECT     *SourceDesc,
319    ACPI_OPERAND_OBJECT     *ObjDesc,
320    ACPI_OPERAND_OBJECT     **ResultDesc)
321{
322    ACPI_STATUS             Status;
323    UINT32                  BufferLength;
324    UINT32                  DataLength;
325    void                    *Buffer;
326
327
328    ACPI_FUNCTION_TRACE_PTR (ExWriteDataToField, ObjDesc);
329
330
331    /* Parameter validation */
332
333    if (!SourceDesc || !ObjDesc)
334    {
335        return_ACPI_STATUS (AE_AML_NO_OPERAND);
336    }
337
338    if (ObjDesc->Common.Type == ACPI_TYPE_BUFFER_FIELD)
339    {
340        /*
341         * If the BufferField arguments have not been previously evaluated,
342         * evaluate them now and save the results.
343         */
344        if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
345        {
346            Status = AcpiDsGetBufferFieldArguments (ObjDesc);
347            if (ACPI_FAILURE (Status))
348            {
349                return_ACPI_STATUS (Status);
350            }
351        }
352    }
353    else if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
354        (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GPIO))
355    {
356        /* General Purpose I/O */
357
358        Status = AcpiExWriteGpio (SourceDesc, ObjDesc, ResultDesc);
359        return_ACPI_STATUS (Status);
360    }
361    else if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
362        (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS ||
363         ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS ||
364         ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_IPMI  ||
365         ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_PLATFORM_RT ||
366         ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_FIXED_HARDWARE))
367    {
368        /* SMBus, GSBus, IPMI serial */
369
370        Status = AcpiExWriteSerialBus (SourceDesc, ObjDesc, ResultDesc);
371        return_ACPI_STATUS (Status);
372    }
373    else if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
374             (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_PLATFORM_COMM))
375    {
376        /*
377         * According to the spec a write to the COMD field will invoke the
378         * region handler. Otherwise, write to the PccInternal buffer. This
379         * implementation will use the offsets specified rather than the name
380         * of the field. This is considered safer because some firmware tools
381         * are known to obfiscate named objects.
382         */
383        DataLength = (ACPI_SIZE) ACPI_ROUND_BITS_UP_TO_BYTES (
384            ObjDesc->Field.BitLength);
385        memcpy (ObjDesc->Field.RegionObj->Field.InternalPccBuffer +
386            ObjDesc->Field.BaseByteOffset,
387            SourceDesc->Buffer.Pointer, DataLength);
388
389        if (MASTER_SUBSPACE_COMMAND (ObjDesc->Field.BaseByteOffset))
390        {
391            /* Perform the write */
392
393            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
394                "PCC COMD field has been written. Invoking PCC handler now.\n"));
395
396            Status = AcpiExAccessRegion (
397                ObjDesc, 0, (UINT64 *) ObjDesc->Field.RegionObj->Field.InternalPccBuffer,
398                ACPI_WRITE);
399            return_ACPI_STATUS (Status);
400        }
401        return (AE_OK);
402    }
403
404
405    /* Get a pointer to the data to be written */
406
407    switch (SourceDesc->Common.Type)
408    {
409    case ACPI_TYPE_INTEGER:
410
411        Buffer = &SourceDesc->Integer.Value;
412        BufferLength = sizeof (SourceDesc->Integer.Value);
413        break;
414
415    case ACPI_TYPE_BUFFER:
416
417        Buffer = SourceDesc->Buffer.Pointer;
418        BufferLength = SourceDesc->Buffer.Length;
419        break;
420
421    case ACPI_TYPE_STRING:
422
423        Buffer = SourceDesc->String.Pointer;
424        BufferLength = SourceDesc->String.Length;
425        break;
426
427    default:
428        return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
429    }
430
431    ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
432        "FieldWrite [FROM]: Obj %p (%s:%X), Buf %p, ByteLen %X\n",
433        SourceDesc, AcpiUtGetTypeName (SourceDesc->Common.Type),
434        SourceDesc->Common.Type, Buffer, BufferLength));
435
436    ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
437        "FieldWrite [TO]:   Obj %p (%s:%X), BitLen %X, BitOff %X, ByteOff %X\n",
438        ObjDesc, AcpiUtGetTypeName (ObjDesc->Common.Type),
439        ObjDesc->Common.Type,
440        ObjDesc->CommonField.BitLength,
441        ObjDesc->CommonField.StartFieldBitOffset,
442        ObjDesc->CommonField.BaseByteOffset));
443
444    /* Lock entire transaction if requested */
445
446    AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags);
447
448    /* Write to the field */
449
450    Status = AcpiExInsertIntoField (ObjDesc, Buffer, BufferLength);
451    AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags);
452    return_ACPI_STATUS (Status);
453}
454