1/******************************************************************************
2 *
3 * Module Name: exfield - ACPI AML (p-code) execution - field manipulation
4 *
5 *****************************************************************************/
6
7/*
8 * Copyright (C) 2000 - 2011, 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
52
53#define _COMPONENT          ACPI_EXECUTER
54        ACPI_MODULE_NAME    ("exfield")
55
56
57/*******************************************************************************
58 *
59 * FUNCTION:    AcpiExReadDataFromField
60 *
61 * PARAMETERS:  WalkState           - Current execution state
62 *              ObjDesc             - The named field
63 *              RetBufferDesc       - Where the return data object is stored
64 *
65 * RETURN:      Status
66 *
67 * DESCRIPTION: Read from a named field.  Returns either an Integer or a
68 *              Buffer, depending on the size of the field.
69 *
70 ******************************************************************************/
71
72ACPI_STATUS
73AcpiExReadDataFromField (
74    ACPI_WALK_STATE         *WalkState,
75    ACPI_OPERAND_OBJECT     *ObjDesc,
76    ACPI_OPERAND_OBJECT     **RetBufferDesc)
77{
78    ACPI_STATUS             Status;
79    ACPI_OPERAND_OBJECT     *BufferDesc;
80    ACPI_SIZE               Length;
81    void                    *Buffer;
82    UINT32                  Function;
83
84
85    ACPI_FUNCTION_TRACE_PTR (ExReadDataFromField, ObjDesc);
86
87
88    /* Parameter validation */
89
90    if (!ObjDesc)
91    {
92        return_ACPI_STATUS (AE_AML_NO_OPERAND);
93    }
94    if (!RetBufferDesc)
95    {
96        return_ACPI_STATUS (AE_BAD_PARAMETER);
97    }
98
99    if (ObjDesc->Common.Type == ACPI_TYPE_BUFFER_FIELD)
100    {
101        /*
102         * If the BufferField arguments have not been previously evaluated,
103         * evaluate them now and save the results.
104         */
105        if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
106        {
107            Status = AcpiDsGetBufferFieldArguments (ObjDesc);
108            if (ACPI_FAILURE (Status))
109            {
110                return_ACPI_STATUS (Status);
111            }
112        }
113    }
114    else if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
115             (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS ||
116              ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_IPMI))
117    {
118        /*
119         * This is an SMBus or IPMI read. We must create a buffer to hold
120         * the data and then directly access the region handler.
121         *
122         * Note: Smbus protocol value is passed in upper 16-bits of Function
123         */
124        if (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS)
125        {
126            Length = ACPI_SMBUS_BUFFER_SIZE;
127            Function = ACPI_READ | (ObjDesc->Field.Attribute << 16);
128        }
129        else /* IPMI */
130        {
131            Length = ACPI_IPMI_BUFFER_SIZE;
132            Function = ACPI_READ;
133        }
134
135        BufferDesc = AcpiUtCreateBufferObject (Length);
136        if (!BufferDesc)
137        {
138            return_ACPI_STATUS (AE_NO_MEMORY);
139        }
140
141        /* Lock entire transaction if requested */
142
143        AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags);
144
145        /* Call the region handler for the read */
146
147        Status = AcpiExAccessRegion (ObjDesc, 0,
148                    ACPI_CAST_PTR (UINT64, BufferDesc->Buffer.Pointer),
149                    Function);
150        AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags);
151        goto Exit;
152    }
153
154    /*
155     * Allocate a buffer for the contents of the field.
156     *
157     * If the field is larger than the current integer width, create
158     * a BUFFER to hold it.  Otherwise, use an INTEGER.  This allows
159     * the use of arithmetic operators on the returned value if the
160     * field size is equal or smaller than an Integer.
161     *
162     * Note: Field.length is in bits.
163     */
164    Length = (ACPI_SIZE) ACPI_ROUND_BITS_UP_TO_BYTES (ObjDesc->Field.BitLength);
165    if (Length > AcpiGbl_IntegerByteWidth)
166    {
167        /* Field is too large for an Integer, create a Buffer instead */
168
169        BufferDesc = AcpiUtCreateBufferObject (Length);
170        if (!BufferDesc)
171        {
172            return_ACPI_STATUS (AE_NO_MEMORY);
173        }
174        Buffer = BufferDesc->Buffer.Pointer;
175    }
176    else
177    {
178        /* Field will fit within an Integer (normal case) */
179
180        BufferDesc = AcpiUtCreateIntegerObject ((UINT64) 0);
181        if (!BufferDesc)
182        {
183            return_ACPI_STATUS (AE_NO_MEMORY);
184        }
185
186        Length = AcpiGbl_IntegerByteWidth;
187        Buffer = &BufferDesc->Integer.Value;
188    }
189
190    ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
191        "FieldRead [TO]:   Obj %p, Type %X, Buf %p, ByteLen %X\n",
192        ObjDesc, ObjDesc->Common.Type, Buffer, (UINT32) Length));
193    ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
194        "FieldRead [FROM]: BitLen %X, BitOff %X, ByteOff %X\n",
195        ObjDesc->CommonField.BitLength,
196        ObjDesc->CommonField.StartFieldBitOffset,
197        ObjDesc->CommonField.BaseByteOffset));
198
199    /* Lock entire transaction if requested */
200
201    AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags);
202
203    /* Read from the field */
204
205    Status = AcpiExExtractFromField (ObjDesc, Buffer, (UINT32) Length);
206    AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags);
207
208
209Exit:
210    if (ACPI_FAILURE (Status))
211    {
212        AcpiUtRemoveReference (BufferDesc);
213    }
214    else
215    {
216        *RetBufferDesc = BufferDesc;
217    }
218
219    return_ACPI_STATUS (Status);
220}
221
222
223/*******************************************************************************
224 *
225 * FUNCTION:    AcpiExWriteDataToField
226 *
227 * PARAMETERS:  SourceDesc          - Contains data to write
228 *              ObjDesc             - The named field
229 *              ResultDesc          - Where the return value is returned, if any
230 *
231 * RETURN:      Status
232 *
233 * DESCRIPTION: Write to a named field
234 *
235 ******************************************************************************/
236
237ACPI_STATUS
238AcpiExWriteDataToField (
239    ACPI_OPERAND_OBJECT     *SourceDesc,
240    ACPI_OPERAND_OBJECT     *ObjDesc,
241    ACPI_OPERAND_OBJECT     **ResultDesc)
242{
243    ACPI_STATUS             Status;
244    UINT32                  Length;
245    void                    *Buffer;
246    ACPI_OPERAND_OBJECT     *BufferDesc;
247    UINT32                  Function;
248
249
250    ACPI_FUNCTION_TRACE_PTR (ExWriteDataToField, ObjDesc);
251
252
253    /* Parameter validation */
254
255    if (!SourceDesc || !ObjDesc)
256    {
257        return_ACPI_STATUS (AE_AML_NO_OPERAND);
258    }
259
260    if (ObjDesc->Common.Type == ACPI_TYPE_BUFFER_FIELD)
261    {
262        /*
263         * If the BufferField arguments have not been previously evaluated,
264         * evaluate them now and save the results.
265         */
266        if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
267        {
268            Status = AcpiDsGetBufferFieldArguments (ObjDesc);
269            if (ACPI_FAILURE (Status))
270            {
271                return_ACPI_STATUS (Status);
272            }
273        }
274    }
275    else if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
276             (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS ||
277              ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_IPMI))
278    {
279        /*
280         * This is an SMBus or IPMI write. We will bypass the entire field
281         * mechanism and handoff the buffer directly to the handler. For
282         * these address spaces, the buffer is bi-directional; on a write,
283         * return data is returned in the same buffer.
284         *
285         * Source must be a buffer of sufficient size:
286         * ACPI_SMBUS_BUFFER_SIZE or ACPI_IPMI_BUFFER_SIZE.
287         *
288         * Note: SMBus protocol type is passed in upper 16-bits of Function
289         */
290        if (SourceDesc->Common.Type != ACPI_TYPE_BUFFER)
291        {
292            ACPI_ERROR ((AE_INFO,
293                "SMBus or IPMI write requires Buffer, found type %s",
294                AcpiUtGetObjectTypeName (SourceDesc)));
295
296            return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
297        }
298
299        if (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS)
300        {
301            Length = ACPI_SMBUS_BUFFER_SIZE;
302            Function = ACPI_WRITE | (ObjDesc->Field.Attribute << 16);
303        }
304        else /* IPMI */
305        {
306            Length = ACPI_IPMI_BUFFER_SIZE;
307            Function = ACPI_WRITE;
308        }
309
310        if (SourceDesc->Buffer.Length < Length)
311        {
312            ACPI_ERROR ((AE_INFO,
313                "SMBus or IPMI write requires Buffer of length %u, found length %u",
314                Length, SourceDesc->Buffer.Length));
315
316            return_ACPI_STATUS (AE_AML_BUFFER_LIMIT);
317        }
318
319        /* Create the bi-directional buffer */
320
321        BufferDesc = AcpiUtCreateBufferObject (Length);
322        if (!BufferDesc)
323        {
324            return_ACPI_STATUS (AE_NO_MEMORY);
325        }
326
327        Buffer = BufferDesc->Buffer.Pointer;
328        ACPI_MEMCPY (Buffer, SourceDesc->Buffer.Pointer, Length);
329
330        /* Lock entire transaction if requested */
331
332        AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags);
333
334        /*
335         * Perform the write (returns status and perhaps data in the
336         * same buffer)
337         */
338        Status = AcpiExAccessRegion (ObjDesc, 0,
339                    (UINT64 *) Buffer, Function);
340        AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags);
341
342        *ResultDesc = BufferDesc;
343        return_ACPI_STATUS (Status);
344    }
345
346    /* Get a pointer to the data to be written */
347
348    switch (SourceDesc->Common.Type)
349    {
350    case ACPI_TYPE_INTEGER:
351        Buffer = &SourceDesc->Integer.Value;
352        Length = sizeof (SourceDesc->Integer.Value);
353        break;
354
355    case ACPI_TYPE_BUFFER:
356        Buffer = SourceDesc->Buffer.Pointer;
357        Length = SourceDesc->Buffer.Length;
358        break;
359
360    case ACPI_TYPE_STRING:
361        Buffer = SourceDesc->String.Pointer;
362        Length = SourceDesc->String.Length;
363        break;
364
365    default:
366        return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
367    }
368
369    ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
370        "FieldWrite [FROM]: Obj %p (%s:%X), Buf %p, ByteLen %X\n",
371        SourceDesc, AcpiUtGetTypeName (SourceDesc->Common.Type),
372        SourceDesc->Common.Type, Buffer, Length));
373
374    ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
375        "FieldWrite [TO]:   Obj %p (%s:%X), BitLen %X, BitOff %X, ByteOff %X\n",
376        ObjDesc, AcpiUtGetTypeName (ObjDesc->Common.Type),
377        ObjDesc->Common.Type,
378        ObjDesc->CommonField.BitLength,
379        ObjDesc->CommonField.StartFieldBitOffset,
380        ObjDesc->CommonField.BaseByteOffset));
381
382    /* Lock entire transaction if requested */
383
384    AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags);
385
386    /* Write to the field */
387
388    Status = AcpiExInsertIntoField (ObjDesc, Buffer, Length);
389    AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags);
390
391    return_ACPI_STATUS (Status);
392}
393
394
395