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