xref: /minix3/minix/drivers/power/acpi/executer/exfldio.c (revision 29492bb71c7148a089a5afafa0c99409161218df)
1433d6423SLionel Sambuc /******************************************************************************
2433d6423SLionel Sambuc  *
3433d6423SLionel Sambuc  * Module Name: exfldio - Aml Field I/O
4433d6423SLionel Sambuc  *
5433d6423SLionel Sambuc  *****************************************************************************/
6433d6423SLionel Sambuc 
7*29492bb7SDavid van Moolenbroek /*
8*29492bb7SDavid van Moolenbroek  * Copyright (C) 2000 - 2014, Intel Corp.
9433d6423SLionel Sambuc  * All rights reserved.
10433d6423SLionel Sambuc  *
11*29492bb7SDavid van Moolenbroek  * Redistribution and use in source and binary forms, with or without
12*29492bb7SDavid van Moolenbroek  * modification, are permitted provided that the following conditions
13*29492bb7SDavid van Moolenbroek  * are met:
14*29492bb7SDavid van Moolenbroek  * 1. Redistributions of source code must retain the above copyright
15*29492bb7SDavid van Moolenbroek  *    notice, this list of conditions, and the following disclaimer,
16*29492bb7SDavid van Moolenbroek  *    without modification.
17*29492bb7SDavid van Moolenbroek  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18*29492bb7SDavid van Moolenbroek  *    substantially similar to the "NO WARRANTY" disclaimer below
19*29492bb7SDavid van Moolenbroek  *    ("Disclaimer") and any redistribution must be conditioned upon
20*29492bb7SDavid van Moolenbroek  *    including a substantially similar Disclaimer requirement for further
21*29492bb7SDavid van Moolenbroek  *    binary redistribution.
22*29492bb7SDavid van Moolenbroek  * 3. Neither the names of the above-listed copyright holders nor the names
23*29492bb7SDavid van Moolenbroek  *    of any contributors may be used to endorse or promote products derived
24*29492bb7SDavid van Moolenbroek  *    from this software without specific prior written permission.
25433d6423SLionel Sambuc  *
26*29492bb7SDavid van Moolenbroek  * Alternatively, this software may be distributed under the terms of the
27*29492bb7SDavid van Moolenbroek  * GNU General Public License ("GPL") version 2 as published by the Free
28*29492bb7SDavid van Moolenbroek  * Software Foundation.
29433d6423SLionel Sambuc  *
30*29492bb7SDavid van Moolenbroek  * NO WARRANTY
31*29492bb7SDavid van Moolenbroek  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32*29492bb7SDavid van Moolenbroek  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33*29492bb7SDavid van Moolenbroek  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34*29492bb7SDavid van Moolenbroek  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35*29492bb7SDavid van Moolenbroek  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36*29492bb7SDavid van Moolenbroek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37*29492bb7SDavid van Moolenbroek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38*29492bb7SDavid van Moolenbroek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39*29492bb7SDavid van Moolenbroek  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40*29492bb7SDavid van Moolenbroek  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41*29492bb7SDavid van Moolenbroek  * POSSIBILITY OF SUCH DAMAGES.
42*29492bb7SDavid van Moolenbroek  */
43433d6423SLionel Sambuc 
44433d6423SLionel Sambuc #include "acpi.h"
45433d6423SLionel Sambuc #include "accommon.h"
46433d6423SLionel Sambuc #include "acinterp.h"
47433d6423SLionel Sambuc #include "amlcode.h"
48433d6423SLionel Sambuc #include "acevents.h"
49433d6423SLionel Sambuc #include "acdispat.h"
50433d6423SLionel Sambuc 
51433d6423SLionel Sambuc 
52433d6423SLionel Sambuc #define _COMPONENT          ACPI_EXECUTER
53433d6423SLionel Sambuc         ACPI_MODULE_NAME    ("exfldio")
54433d6423SLionel Sambuc 
55433d6423SLionel Sambuc /* Local prototypes */
56433d6423SLionel Sambuc 
57433d6423SLionel Sambuc static ACPI_STATUS
58433d6423SLionel Sambuc AcpiExFieldDatumIo (
59433d6423SLionel Sambuc     ACPI_OPERAND_OBJECT     *ObjDesc,
60433d6423SLionel Sambuc     UINT32                  FieldDatumByteOffset,
61433d6423SLionel Sambuc     UINT64                  *Value,
62433d6423SLionel Sambuc     UINT32                  ReadWrite);
63433d6423SLionel Sambuc 
64433d6423SLionel Sambuc static BOOLEAN
65433d6423SLionel Sambuc AcpiExRegisterOverflow (
66433d6423SLionel Sambuc     ACPI_OPERAND_OBJECT     *ObjDesc,
67433d6423SLionel Sambuc     UINT64                  Value);
68433d6423SLionel Sambuc 
69433d6423SLionel Sambuc static ACPI_STATUS
70433d6423SLionel Sambuc AcpiExSetupRegion (
71433d6423SLionel Sambuc     ACPI_OPERAND_OBJECT     *ObjDesc,
72433d6423SLionel Sambuc     UINT32                  FieldDatumByteOffset);
73433d6423SLionel Sambuc 
74433d6423SLionel Sambuc 
75433d6423SLionel Sambuc /*******************************************************************************
76433d6423SLionel Sambuc  *
77433d6423SLionel Sambuc  * FUNCTION:    AcpiExSetupRegion
78433d6423SLionel Sambuc  *
79433d6423SLionel Sambuc  * PARAMETERS:  ObjDesc                 - Field to be read or written
80433d6423SLionel Sambuc  *              FieldDatumByteOffset    - Byte offset of this datum within the
81433d6423SLionel Sambuc  *                                        parent field
82433d6423SLionel Sambuc  *
83433d6423SLionel Sambuc  * RETURN:      Status
84433d6423SLionel Sambuc  *
85433d6423SLionel Sambuc  * DESCRIPTION: Common processing for AcpiExExtractFromField and
86433d6423SLionel Sambuc  *              AcpiExInsertIntoField. Initialize the Region if necessary and
87433d6423SLionel Sambuc  *              validate the request.
88433d6423SLionel Sambuc  *
89433d6423SLionel Sambuc  ******************************************************************************/
90433d6423SLionel Sambuc 
91433d6423SLionel Sambuc static ACPI_STATUS
AcpiExSetupRegion(ACPI_OPERAND_OBJECT * ObjDesc,UINT32 FieldDatumByteOffset)92433d6423SLionel Sambuc AcpiExSetupRegion (
93433d6423SLionel Sambuc     ACPI_OPERAND_OBJECT     *ObjDesc,
94433d6423SLionel Sambuc     UINT32                  FieldDatumByteOffset)
95433d6423SLionel Sambuc {
96433d6423SLionel Sambuc     ACPI_STATUS             Status = AE_OK;
97433d6423SLionel Sambuc     ACPI_OPERAND_OBJECT     *RgnDesc;
98*29492bb7SDavid van Moolenbroek     UINT8                   SpaceId;
99433d6423SLionel Sambuc 
100433d6423SLionel Sambuc 
101433d6423SLionel Sambuc     ACPI_FUNCTION_TRACE_U32 (ExSetupRegion, FieldDatumByteOffset);
102433d6423SLionel Sambuc 
103433d6423SLionel Sambuc 
104433d6423SLionel Sambuc     RgnDesc = ObjDesc->CommonField.RegionObj;
105433d6423SLionel Sambuc 
106433d6423SLionel Sambuc     /* We must have a valid region */
107433d6423SLionel Sambuc 
108433d6423SLionel Sambuc     if (RgnDesc->Common.Type != ACPI_TYPE_REGION)
109433d6423SLionel Sambuc     {
110433d6423SLionel Sambuc         ACPI_ERROR ((AE_INFO, "Needed Region, found type 0x%X (%s)",
111433d6423SLionel Sambuc             RgnDesc->Common.Type,
112433d6423SLionel Sambuc             AcpiUtGetObjectTypeName (RgnDesc)));
113433d6423SLionel Sambuc 
114433d6423SLionel Sambuc         return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
115433d6423SLionel Sambuc     }
116433d6423SLionel Sambuc 
117*29492bb7SDavid van Moolenbroek     SpaceId = RgnDesc->Region.SpaceId;
118*29492bb7SDavid van Moolenbroek 
119*29492bb7SDavid van Moolenbroek     /* Validate the Space ID */
120*29492bb7SDavid van Moolenbroek 
121*29492bb7SDavid van Moolenbroek     if (!AcpiIsValidSpaceId (SpaceId))
122*29492bb7SDavid van Moolenbroek     {
123*29492bb7SDavid van Moolenbroek         ACPI_ERROR ((AE_INFO, "Invalid/unknown Address Space ID: 0x%2.2X", SpaceId));
124*29492bb7SDavid van Moolenbroek         return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID);
125*29492bb7SDavid van Moolenbroek     }
126*29492bb7SDavid van Moolenbroek 
127433d6423SLionel Sambuc     /*
128433d6423SLionel Sambuc      * If the Region Address and Length have not been previously evaluated,
129433d6423SLionel Sambuc      * evaluate them now and save the results.
130433d6423SLionel Sambuc      */
131433d6423SLionel Sambuc     if (!(RgnDesc->Common.Flags & AOPOBJ_DATA_VALID))
132433d6423SLionel Sambuc     {
133433d6423SLionel Sambuc         Status = AcpiDsGetRegionArguments (RgnDesc);
134433d6423SLionel Sambuc         if (ACPI_FAILURE (Status))
135433d6423SLionel Sambuc         {
136433d6423SLionel Sambuc             return_ACPI_STATUS (Status);
137433d6423SLionel Sambuc         }
138433d6423SLionel Sambuc     }
139433d6423SLionel Sambuc 
140433d6423SLionel Sambuc     /*
141*29492bb7SDavid van Moolenbroek      * Exit now for SMBus, GSBus or IPMI address space, it has a non-linear
142*29492bb7SDavid van Moolenbroek      * address space and the request cannot be directly validated
143433d6423SLionel Sambuc      */
144*29492bb7SDavid van Moolenbroek     if (SpaceId == ACPI_ADR_SPACE_SMBUS ||
145*29492bb7SDavid van Moolenbroek         SpaceId == ACPI_ADR_SPACE_GSBUS ||
146*29492bb7SDavid van Moolenbroek         SpaceId == ACPI_ADR_SPACE_IPMI)
147433d6423SLionel Sambuc     {
148433d6423SLionel Sambuc         /* SMBus or IPMI has a non-linear address space */
149433d6423SLionel Sambuc 
150433d6423SLionel Sambuc         return_ACPI_STATUS (AE_OK);
151433d6423SLionel Sambuc     }
152433d6423SLionel Sambuc 
153433d6423SLionel Sambuc #ifdef ACPI_UNDER_DEVELOPMENT
154433d6423SLionel Sambuc     /*
155433d6423SLionel Sambuc      * If the Field access is AnyAcc, we can now compute the optimal
156433d6423SLionel Sambuc      * access (because we know know the length of the parent region)
157433d6423SLionel Sambuc      */
158433d6423SLionel Sambuc     if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
159433d6423SLionel Sambuc     {
160433d6423SLionel Sambuc         if (ACPI_FAILURE (Status))
161433d6423SLionel Sambuc         {
162433d6423SLionel Sambuc             return_ACPI_STATUS (Status);
163433d6423SLionel Sambuc         }
164433d6423SLionel Sambuc     }
165433d6423SLionel Sambuc #endif
166433d6423SLionel Sambuc 
167433d6423SLionel Sambuc     /*
168433d6423SLionel Sambuc      * Validate the request. The entire request from the byte offset for a
169433d6423SLionel Sambuc      * length of one field datum (access width) must fit within the region.
170433d6423SLionel Sambuc      * (Region length is specified in bytes)
171433d6423SLionel Sambuc      */
172433d6423SLionel Sambuc     if (RgnDesc->Region.Length <
173*29492bb7SDavid van Moolenbroek             (ObjDesc->CommonField.BaseByteOffset + FieldDatumByteOffset +
174433d6423SLionel Sambuc             ObjDesc->CommonField.AccessByteWidth))
175433d6423SLionel Sambuc     {
176433d6423SLionel Sambuc         if (AcpiGbl_EnableInterpreterSlack)
177433d6423SLionel Sambuc         {
178433d6423SLionel Sambuc             /*
179433d6423SLionel Sambuc              * Slack mode only:  We will go ahead and allow access to this
180433d6423SLionel Sambuc              * field if it is within the region length rounded up to the next
181433d6423SLionel Sambuc              * access width boundary. ACPI_SIZE cast for 64-bit compile.
182433d6423SLionel Sambuc              */
183433d6423SLionel Sambuc             if (ACPI_ROUND_UP (RgnDesc->Region.Length,
184433d6423SLionel Sambuc                     ObjDesc->CommonField.AccessByteWidth) >=
185433d6423SLionel Sambuc                 ((ACPI_SIZE) ObjDesc->CommonField.BaseByteOffset +
186433d6423SLionel Sambuc                     ObjDesc->CommonField.AccessByteWidth +
187433d6423SLionel Sambuc                     FieldDatumByteOffset))
188433d6423SLionel Sambuc             {
189433d6423SLionel Sambuc                 return_ACPI_STATUS (AE_OK);
190433d6423SLionel Sambuc             }
191433d6423SLionel Sambuc         }
192433d6423SLionel Sambuc 
193433d6423SLionel Sambuc         if (RgnDesc->Region.Length < ObjDesc->CommonField.AccessByteWidth)
194433d6423SLionel Sambuc         {
195433d6423SLionel Sambuc             /*
196433d6423SLionel Sambuc              * This is the case where the AccessType (AccWord, etc.) is wider
197433d6423SLionel Sambuc              * than the region itself. For example, a region of length one
198433d6423SLionel Sambuc              * byte, and a field with Dword access specified.
199433d6423SLionel Sambuc              */
200433d6423SLionel Sambuc             ACPI_ERROR ((AE_INFO,
201433d6423SLionel Sambuc                 "Field [%4.4s] access width (%u bytes) too large for region [%4.4s] (length %u)",
202433d6423SLionel Sambuc                 AcpiUtGetNodeName (ObjDesc->CommonField.Node),
203433d6423SLionel Sambuc                 ObjDesc->CommonField.AccessByteWidth,
204433d6423SLionel Sambuc                 AcpiUtGetNodeName (RgnDesc->Region.Node),
205433d6423SLionel Sambuc                 RgnDesc->Region.Length));
206433d6423SLionel Sambuc         }
207433d6423SLionel Sambuc 
208433d6423SLionel Sambuc         /*
209433d6423SLionel Sambuc          * Offset rounded up to next multiple of field width
210433d6423SLionel Sambuc          * exceeds region length, indicate an error
211433d6423SLionel Sambuc          */
212433d6423SLionel Sambuc         ACPI_ERROR ((AE_INFO,
213433d6423SLionel Sambuc             "Field [%4.4s] Base+Offset+Width %u+%u+%u is beyond end of region [%4.4s] (length %u)",
214433d6423SLionel Sambuc             AcpiUtGetNodeName (ObjDesc->CommonField.Node),
215433d6423SLionel Sambuc             ObjDesc->CommonField.BaseByteOffset,
216433d6423SLionel Sambuc             FieldDatumByteOffset, ObjDesc->CommonField.AccessByteWidth,
217433d6423SLionel Sambuc             AcpiUtGetNodeName (RgnDesc->Region.Node),
218433d6423SLionel Sambuc             RgnDesc->Region.Length));
219433d6423SLionel Sambuc 
220433d6423SLionel Sambuc         return_ACPI_STATUS (AE_AML_REGION_LIMIT);
221433d6423SLionel Sambuc     }
222433d6423SLionel Sambuc 
223433d6423SLionel Sambuc     return_ACPI_STATUS (AE_OK);
224433d6423SLionel Sambuc }
225433d6423SLionel Sambuc 
226433d6423SLionel Sambuc 
227433d6423SLionel Sambuc /*******************************************************************************
228433d6423SLionel Sambuc  *
229433d6423SLionel Sambuc  * FUNCTION:    AcpiExAccessRegion
230433d6423SLionel Sambuc  *
231433d6423SLionel Sambuc  * PARAMETERS:  ObjDesc                 - Field to be read
232433d6423SLionel Sambuc  *              FieldDatumByteOffset    - Byte offset of this datum within the
233433d6423SLionel Sambuc  *                                        parent field
234433d6423SLionel Sambuc  *              Value                   - Where to store value (must at least
235433d6423SLionel Sambuc  *                                        64 bits)
236433d6423SLionel Sambuc  *              Function                - Read or Write flag plus other region-
237433d6423SLionel Sambuc  *                                        dependent flags
238433d6423SLionel Sambuc  *
239433d6423SLionel Sambuc  * RETURN:      Status
240433d6423SLionel Sambuc  *
241433d6423SLionel Sambuc  * DESCRIPTION: Read or Write a single field datum to an Operation Region.
242433d6423SLionel Sambuc  *
243433d6423SLionel Sambuc  ******************************************************************************/
244433d6423SLionel Sambuc 
245433d6423SLionel Sambuc ACPI_STATUS
AcpiExAccessRegion(ACPI_OPERAND_OBJECT * ObjDesc,UINT32 FieldDatumByteOffset,UINT64 * Value,UINT32 Function)246433d6423SLionel Sambuc AcpiExAccessRegion (
247433d6423SLionel Sambuc     ACPI_OPERAND_OBJECT     *ObjDesc,
248433d6423SLionel Sambuc     UINT32                  FieldDatumByteOffset,
249433d6423SLionel Sambuc     UINT64                  *Value,
250433d6423SLionel Sambuc     UINT32                  Function)
251433d6423SLionel Sambuc {
252433d6423SLionel Sambuc     ACPI_STATUS             Status;
253433d6423SLionel Sambuc     ACPI_OPERAND_OBJECT     *RgnDesc;
254433d6423SLionel Sambuc     UINT32                  RegionOffset;
255433d6423SLionel Sambuc 
256433d6423SLionel Sambuc 
257433d6423SLionel Sambuc     ACPI_FUNCTION_TRACE (ExAccessRegion);
258433d6423SLionel Sambuc 
259433d6423SLionel Sambuc 
260433d6423SLionel Sambuc     /*
261433d6423SLionel Sambuc      * Ensure that the region operands are fully evaluated and verify
262433d6423SLionel Sambuc      * the validity of the request
263433d6423SLionel Sambuc      */
264433d6423SLionel Sambuc     Status = AcpiExSetupRegion (ObjDesc, FieldDatumByteOffset);
265433d6423SLionel Sambuc     if (ACPI_FAILURE (Status))
266433d6423SLionel Sambuc     {
267433d6423SLionel Sambuc         return_ACPI_STATUS (Status);
268433d6423SLionel Sambuc     }
269433d6423SLionel Sambuc 
270433d6423SLionel Sambuc     /*
271433d6423SLionel Sambuc      * The physical address of this field datum is:
272433d6423SLionel Sambuc      *
273433d6423SLionel Sambuc      * 1) The base of the region, plus
274433d6423SLionel Sambuc      * 2) The base offset of the field, plus
275433d6423SLionel Sambuc      * 3) The current offset into the field
276433d6423SLionel Sambuc      */
277433d6423SLionel Sambuc     RgnDesc = ObjDesc->CommonField.RegionObj;
278433d6423SLionel Sambuc     RegionOffset =
279433d6423SLionel Sambuc         ObjDesc->CommonField.BaseByteOffset +
280433d6423SLionel Sambuc         FieldDatumByteOffset;
281433d6423SLionel Sambuc 
282433d6423SLionel Sambuc     if ((Function & ACPI_IO_MASK) == ACPI_READ)
283433d6423SLionel Sambuc     {
284433d6423SLionel Sambuc         ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "[READ]"));
285433d6423SLionel Sambuc     }
286433d6423SLionel Sambuc     else
287433d6423SLionel Sambuc     {
288433d6423SLionel Sambuc         ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "[WRITE]"));
289433d6423SLionel Sambuc     }
290433d6423SLionel Sambuc 
291433d6423SLionel Sambuc     ACPI_DEBUG_PRINT_RAW ((ACPI_DB_BFIELD,
292433d6423SLionel Sambuc         " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %p\n",
293433d6423SLionel Sambuc         AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
294433d6423SLionel Sambuc         RgnDesc->Region.SpaceId,
295433d6423SLionel Sambuc         ObjDesc->CommonField.AccessByteWidth,
296433d6423SLionel Sambuc         ObjDesc->CommonField.BaseByteOffset,
297433d6423SLionel Sambuc         FieldDatumByteOffset,
298433d6423SLionel Sambuc         ACPI_CAST_PTR (void, (RgnDesc->Region.Address + RegionOffset))));
299433d6423SLionel Sambuc 
300433d6423SLionel Sambuc     /* Invoke the appropriate AddressSpace/OpRegion handler */
301433d6423SLionel Sambuc 
302*29492bb7SDavid van Moolenbroek     Status = AcpiEvAddressSpaceDispatch (RgnDesc, ObjDesc,
303*29492bb7SDavid van Moolenbroek                 Function, RegionOffset,
304433d6423SLionel Sambuc                 ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth), Value);
305433d6423SLionel Sambuc 
306433d6423SLionel Sambuc     if (ACPI_FAILURE (Status))
307433d6423SLionel Sambuc     {
308433d6423SLionel Sambuc         if (Status == AE_NOT_IMPLEMENTED)
309433d6423SLionel Sambuc         {
310433d6423SLionel Sambuc             ACPI_ERROR ((AE_INFO,
311*29492bb7SDavid van Moolenbroek                 "Region %s (ID=%u) not implemented",
312433d6423SLionel Sambuc                 AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
313433d6423SLionel Sambuc                 RgnDesc->Region.SpaceId));
314433d6423SLionel Sambuc         }
315433d6423SLionel Sambuc         else if (Status == AE_NOT_EXIST)
316433d6423SLionel Sambuc         {
317433d6423SLionel Sambuc             ACPI_ERROR ((AE_INFO,
318*29492bb7SDavid van Moolenbroek                 "Region %s (ID=%u) has no handler",
319433d6423SLionel Sambuc                 AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
320433d6423SLionel Sambuc                 RgnDesc->Region.SpaceId));
321433d6423SLionel Sambuc         }
322433d6423SLionel Sambuc     }
323433d6423SLionel Sambuc 
324433d6423SLionel Sambuc     return_ACPI_STATUS (Status);
325433d6423SLionel Sambuc }
326433d6423SLionel Sambuc 
327433d6423SLionel Sambuc 
328433d6423SLionel Sambuc /*******************************************************************************
329433d6423SLionel Sambuc  *
330433d6423SLionel Sambuc  * FUNCTION:    AcpiExRegisterOverflow
331433d6423SLionel Sambuc  *
332433d6423SLionel Sambuc  * PARAMETERS:  ObjDesc                 - Register(Field) to be written
333433d6423SLionel Sambuc  *              Value                   - Value to be stored
334433d6423SLionel Sambuc  *
335433d6423SLionel Sambuc  * RETURN:      TRUE if value overflows the field, FALSE otherwise
336433d6423SLionel Sambuc  *
337433d6423SLionel Sambuc  * DESCRIPTION: Check if a value is out of range of the field being written.
338433d6423SLionel Sambuc  *              Used to check if the values written to Index and Bank registers
339433d6423SLionel Sambuc  *              are out of range. Normally, the value is simply truncated
340433d6423SLionel Sambuc  *              to fit the field, but this case is most likely a serious
341433d6423SLionel Sambuc  *              coding error in the ASL.
342433d6423SLionel Sambuc  *
343433d6423SLionel Sambuc  ******************************************************************************/
344433d6423SLionel Sambuc 
345433d6423SLionel Sambuc static BOOLEAN
AcpiExRegisterOverflow(ACPI_OPERAND_OBJECT * ObjDesc,UINT64 Value)346433d6423SLionel Sambuc AcpiExRegisterOverflow (
347433d6423SLionel Sambuc     ACPI_OPERAND_OBJECT     *ObjDesc,
348433d6423SLionel Sambuc     UINT64                  Value)
349433d6423SLionel Sambuc {
350433d6423SLionel Sambuc 
351433d6423SLionel Sambuc     if (ObjDesc->CommonField.BitLength >= ACPI_INTEGER_BIT_SIZE)
352433d6423SLionel Sambuc     {
353433d6423SLionel Sambuc         /*
354433d6423SLionel Sambuc          * The field is large enough to hold the maximum integer, so we can
355433d6423SLionel Sambuc          * never overflow it.
356433d6423SLionel Sambuc          */
357433d6423SLionel Sambuc         return (FALSE);
358433d6423SLionel Sambuc     }
359433d6423SLionel Sambuc 
360433d6423SLionel Sambuc     if (Value >= ((UINT64) 1 << ObjDesc->CommonField.BitLength))
361433d6423SLionel Sambuc     {
362433d6423SLionel Sambuc         /*
363433d6423SLionel Sambuc          * The Value is larger than the maximum value that can fit into
364433d6423SLionel Sambuc          * the register.
365433d6423SLionel Sambuc          */
366*29492bb7SDavid van Moolenbroek         ACPI_ERROR ((AE_INFO,
367*29492bb7SDavid van Moolenbroek             "Index value 0x%8.8X%8.8X overflows field width 0x%X",
368*29492bb7SDavid van Moolenbroek             ACPI_FORMAT_UINT64 (Value),
369*29492bb7SDavid van Moolenbroek             ObjDesc->CommonField.BitLength));
370*29492bb7SDavid van Moolenbroek 
371433d6423SLionel Sambuc         return (TRUE);
372433d6423SLionel Sambuc     }
373433d6423SLionel Sambuc 
374433d6423SLionel Sambuc     /* The Value will fit into the field with no truncation */
375433d6423SLionel Sambuc 
376433d6423SLionel Sambuc     return (FALSE);
377433d6423SLionel Sambuc }
378433d6423SLionel Sambuc 
379433d6423SLionel Sambuc 
380433d6423SLionel Sambuc /*******************************************************************************
381433d6423SLionel Sambuc  *
382433d6423SLionel Sambuc  * FUNCTION:    AcpiExFieldDatumIo
383433d6423SLionel Sambuc  *
384433d6423SLionel Sambuc  * PARAMETERS:  ObjDesc                 - Field to be read
385433d6423SLionel Sambuc  *              FieldDatumByteOffset    - Byte offset of this datum within the
386433d6423SLionel Sambuc  *                                        parent field
387433d6423SLionel Sambuc  *              Value                   - Where to store value (must be 64 bits)
388433d6423SLionel Sambuc  *              ReadWrite               - Read or Write flag
389433d6423SLionel Sambuc  *
390433d6423SLionel Sambuc  * RETURN:      Status
391433d6423SLionel Sambuc  *
392433d6423SLionel Sambuc  * DESCRIPTION: Read or Write a single datum of a field. The FieldType is
393433d6423SLionel Sambuc  *              demultiplexed here to handle the different types of fields
394433d6423SLionel Sambuc  *              (BufferField, RegionField, IndexField, BankField)
395433d6423SLionel Sambuc  *
396433d6423SLionel Sambuc  ******************************************************************************/
397433d6423SLionel Sambuc 
398433d6423SLionel Sambuc static ACPI_STATUS
AcpiExFieldDatumIo(ACPI_OPERAND_OBJECT * ObjDesc,UINT32 FieldDatumByteOffset,UINT64 * Value,UINT32 ReadWrite)399433d6423SLionel Sambuc AcpiExFieldDatumIo (
400433d6423SLionel Sambuc     ACPI_OPERAND_OBJECT     *ObjDesc,
401433d6423SLionel Sambuc     UINT32                  FieldDatumByteOffset,
402433d6423SLionel Sambuc     UINT64                  *Value,
403433d6423SLionel Sambuc     UINT32                  ReadWrite)
404433d6423SLionel Sambuc {
405433d6423SLionel Sambuc     ACPI_STATUS             Status;
406433d6423SLionel Sambuc     UINT64                  LocalValue;
407433d6423SLionel Sambuc 
408433d6423SLionel Sambuc 
409433d6423SLionel Sambuc     ACPI_FUNCTION_TRACE_U32 (ExFieldDatumIo, FieldDatumByteOffset);
410433d6423SLionel Sambuc 
411433d6423SLionel Sambuc 
412433d6423SLionel Sambuc     if (ReadWrite == ACPI_READ)
413433d6423SLionel Sambuc     {
414433d6423SLionel Sambuc         if (!Value)
415433d6423SLionel Sambuc         {
416433d6423SLionel Sambuc             LocalValue = 0;
417433d6423SLionel Sambuc 
418433d6423SLionel Sambuc             /* To support reads without saving return value */
419433d6423SLionel Sambuc             Value = &LocalValue;
420433d6423SLionel Sambuc         }
421433d6423SLionel Sambuc 
422433d6423SLionel Sambuc         /* Clear the entire return buffer first, [Very Important!] */
423433d6423SLionel Sambuc 
424433d6423SLionel Sambuc         *Value = 0;
425433d6423SLionel Sambuc     }
426433d6423SLionel Sambuc 
427433d6423SLionel Sambuc     /*
428433d6423SLionel Sambuc      * The four types of fields are:
429433d6423SLionel Sambuc      *
430433d6423SLionel Sambuc      * BufferField - Read/write from/to a Buffer
431433d6423SLionel Sambuc      * RegionField - Read/write from/to a Operation Region.
432433d6423SLionel Sambuc      * BankField   - Write to a Bank Register, then read/write from/to an
433433d6423SLionel Sambuc      *               OperationRegion
434433d6423SLionel Sambuc      * IndexField  - Write to an Index Register, then read/write from/to a
435433d6423SLionel Sambuc      *               Data Register
436433d6423SLionel Sambuc      */
437433d6423SLionel Sambuc     switch (ObjDesc->Common.Type)
438433d6423SLionel Sambuc     {
439433d6423SLionel Sambuc     case ACPI_TYPE_BUFFER_FIELD:
440433d6423SLionel Sambuc         /*
441433d6423SLionel Sambuc          * If the BufferField arguments have not been previously evaluated,
442433d6423SLionel Sambuc          * evaluate them now and save the results.
443433d6423SLionel Sambuc          */
444433d6423SLionel Sambuc         if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
445433d6423SLionel Sambuc         {
446433d6423SLionel Sambuc             Status = AcpiDsGetBufferFieldArguments (ObjDesc);
447433d6423SLionel Sambuc             if (ACPI_FAILURE (Status))
448433d6423SLionel Sambuc             {
449433d6423SLionel Sambuc                 return_ACPI_STATUS (Status);
450433d6423SLionel Sambuc             }
451433d6423SLionel Sambuc         }
452433d6423SLionel Sambuc 
453433d6423SLionel Sambuc         if (ReadWrite == ACPI_READ)
454433d6423SLionel Sambuc         {
455433d6423SLionel Sambuc             /*
456433d6423SLionel Sambuc              * Copy the data from the source buffer.
457433d6423SLionel Sambuc              * Length is the field width in bytes.
458433d6423SLionel Sambuc              */
459433d6423SLionel Sambuc             ACPI_MEMCPY (Value,
460433d6423SLionel Sambuc                 (ObjDesc->BufferField.BufferObj)->Buffer.Pointer +
461433d6423SLionel Sambuc                     ObjDesc->BufferField.BaseByteOffset +
462433d6423SLionel Sambuc                     FieldDatumByteOffset,
463433d6423SLionel Sambuc                 ObjDesc->CommonField.AccessByteWidth);
464433d6423SLionel Sambuc         }
465433d6423SLionel Sambuc         else
466433d6423SLionel Sambuc         {
467433d6423SLionel Sambuc             /*
468433d6423SLionel Sambuc              * Copy the data to the target buffer.
469433d6423SLionel Sambuc              * Length is the field width in bytes.
470433d6423SLionel Sambuc              */
471433d6423SLionel Sambuc             ACPI_MEMCPY ((ObjDesc->BufferField.BufferObj)->Buffer.Pointer +
472433d6423SLionel Sambuc                 ObjDesc->BufferField.BaseByteOffset +
473433d6423SLionel Sambuc                 FieldDatumByteOffset,
474433d6423SLionel Sambuc                 Value, ObjDesc->CommonField.AccessByteWidth);
475433d6423SLionel Sambuc         }
476433d6423SLionel Sambuc 
477433d6423SLionel Sambuc         Status = AE_OK;
478433d6423SLionel Sambuc         break;
479433d6423SLionel Sambuc 
480433d6423SLionel Sambuc     case ACPI_TYPE_LOCAL_BANK_FIELD:
481433d6423SLionel Sambuc         /*
482433d6423SLionel Sambuc          * Ensure that the BankValue is not beyond the capacity of
483433d6423SLionel Sambuc          * the register
484433d6423SLionel Sambuc          */
485433d6423SLionel Sambuc         if (AcpiExRegisterOverflow (ObjDesc->BankField.BankObj,
486433d6423SLionel Sambuc                 (UINT64) ObjDesc->BankField.Value))
487433d6423SLionel Sambuc         {
488433d6423SLionel Sambuc             return_ACPI_STATUS (AE_AML_REGISTER_LIMIT);
489433d6423SLionel Sambuc         }
490433d6423SLionel Sambuc 
491433d6423SLionel Sambuc         /*
492433d6423SLionel Sambuc          * For BankFields, we must write the BankValue to the BankRegister
493433d6423SLionel Sambuc          * (itself a RegionField) before we can access the data.
494433d6423SLionel Sambuc          */
495433d6423SLionel Sambuc         Status = AcpiExInsertIntoField (ObjDesc->BankField.BankObj,
496433d6423SLionel Sambuc                     &ObjDesc->BankField.Value,
497433d6423SLionel Sambuc                     sizeof (ObjDesc->BankField.Value));
498433d6423SLionel Sambuc         if (ACPI_FAILURE (Status))
499433d6423SLionel Sambuc         {
500433d6423SLionel Sambuc             return_ACPI_STATUS (Status);
501433d6423SLionel Sambuc         }
502433d6423SLionel Sambuc 
503433d6423SLionel Sambuc         /*
504433d6423SLionel Sambuc          * Now that the Bank has been selected, fall through to the
505433d6423SLionel Sambuc          * RegionField case and write the datum to the Operation Region
506433d6423SLionel Sambuc          */
507433d6423SLionel Sambuc 
508433d6423SLionel Sambuc         /*lint -fallthrough */
509433d6423SLionel Sambuc 
510433d6423SLionel Sambuc     case ACPI_TYPE_LOCAL_REGION_FIELD:
511433d6423SLionel Sambuc         /*
512433d6423SLionel Sambuc          * For simple RegionFields, we just directly access the owning
513433d6423SLionel Sambuc          * Operation Region.
514433d6423SLionel Sambuc          */
515433d6423SLionel Sambuc         Status = AcpiExAccessRegion (ObjDesc, FieldDatumByteOffset, Value,
516433d6423SLionel Sambuc                     ReadWrite);
517433d6423SLionel Sambuc         break;
518433d6423SLionel Sambuc 
519433d6423SLionel Sambuc     case ACPI_TYPE_LOCAL_INDEX_FIELD:
520433d6423SLionel Sambuc         /*
521433d6423SLionel Sambuc          * Ensure that the IndexValue is not beyond the capacity of
522433d6423SLionel Sambuc          * the register
523433d6423SLionel Sambuc          */
524433d6423SLionel Sambuc         if (AcpiExRegisterOverflow (ObjDesc->IndexField.IndexObj,
525433d6423SLionel Sambuc                 (UINT64) ObjDesc->IndexField.Value))
526433d6423SLionel Sambuc         {
527433d6423SLionel Sambuc             return_ACPI_STATUS (AE_AML_REGISTER_LIMIT);
528433d6423SLionel Sambuc         }
529433d6423SLionel Sambuc 
530433d6423SLionel Sambuc         /* Write the index value to the IndexRegister (itself a RegionField) */
531433d6423SLionel Sambuc 
532433d6423SLionel Sambuc         FieldDatumByteOffset += ObjDesc->IndexField.Value;
533433d6423SLionel Sambuc 
534433d6423SLionel Sambuc         ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
535433d6423SLionel Sambuc             "Write to Index Register: Value %8.8X\n",
536433d6423SLionel Sambuc             FieldDatumByteOffset));
537433d6423SLionel Sambuc 
538433d6423SLionel Sambuc         Status = AcpiExInsertIntoField (ObjDesc->IndexField.IndexObj,
539433d6423SLionel Sambuc                     &FieldDatumByteOffset,
540433d6423SLionel Sambuc                     sizeof (FieldDatumByteOffset));
541433d6423SLionel Sambuc         if (ACPI_FAILURE (Status))
542433d6423SLionel Sambuc         {
543433d6423SLionel Sambuc             return_ACPI_STATUS (Status);
544433d6423SLionel Sambuc         }
545433d6423SLionel Sambuc 
546433d6423SLionel Sambuc         if (ReadWrite == ACPI_READ)
547433d6423SLionel Sambuc         {
548433d6423SLionel Sambuc             /* Read the datum from the DataRegister */
549433d6423SLionel Sambuc 
550433d6423SLionel Sambuc             ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
551433d6423SLionel Sambuc                 "Read from Data Register\n"));
552433d6423SLionel Sambuc 
553433d6423SLionel Sambuc             Status = AcpiExExtractFromField (ObjDesc->IndexField.DataObj,
554433d6423SLionel Sambuc                         Value, sizeof (UINT64));
555433d6423SLionel Sambuc         }
556433d6423SLionel Sambuc         else
557433d6423SLionel Sambuc         {
558433d6423SLionel Sambuc             /* Write the datum to the DataRegister */
559433d6423SLionel Sambuc 
560433d6423SLionel Sambuc             ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
561433d6423SLionel Sambuc                 "Write to Data Register: Value %8.8X%8.8X\n",
562433d6423SLionel Sambuc                 ACPI_FORMAT_UINT64 (*Value)));
563433d6423SLionel Sambuc 
564433d6423SLionel Sambuc             Status = AcpiExInsertIntoField (ObjDesc->IndexField.DataObj,
565433d6423SLionel Sambuc                         Value, sizeof (UINT64));
566433d6423SLionel Sambuc         }
567433d6423SLionel Sambuc         break;
568433d6423SLionel Sambuc 
569433d6423SLionel Sambuc     default:
570433d6423SLionel Sambuc 
571433d6423SLionel Sambuc         ACPI_ERROR ((AE_INFO, "Wrong object type in field I/O %u",
572433d6423SLionel Sambuc             ObjDesc->Common.Type));
573433d6423SLionel Sambuc         Status = AE_AML_INTERNAL;
574433d6423SLionel Sambuc         break;
575433d6423SLionel Sambuc     }
576433d6423SLionel Sambuc 
577433d6423SLionel Sambuc     if (ACPI_SUCCESS (Status))
578433d6423SLionel Sambuc     {
579433d6423SLionel Sambuc         if (ReadWrite == ACPI_READ)
580433d6423SLionel Sambuc         {
581433d6423SLionel Sambuc             ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
582433d6423SLionel Sambuc                 "Value Read %8.8X%8.8X, Width %u\n",
583433d6423SLionel Sambuc                 ACPI_FORMAT_UINT64 (*Value),
584433d6423SLionel Sambuc                 ObjDesc->CommonField.AccessByteWidth));
585433d6423SLionel Sambuc         }
586433d6423SLionel Sambuc         else
587433d6423SLionel Sambuc         {
588433d6423SLionel Sambuc             ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
589433d6423SLionel Sambuc                 "Value Written %8.8X%8.8X, Width %u\n",
590433d6423SLionel Sambuc                 ACPI_FORMAT_UINT64 (*Value),
591433d6423SLionel Sambuc                 ObjDesc->CommonField.AccessByteWidth));
592433d6423SLionel Sambuc         }
593433d6423SLionel Sambuc     }
594433d6423SLionel Sambuc 
595433d6423SLionel Sambuc     return_ACPI_STATUS (Status);
596433d6423SLionel Sambuc }
597433d6423SLionel Sambuc 
598433d6423SLionel Sambuc 
599433d6423SLionel Sambuc /*******************************************************************************
600433d6423SLionel Sambuc  *
601433d6423SLionel Sambuc  * FUNCTION:    AcpiExWriteWithUpdateRule
602433d6423SLionel Sambuc  *
603433d6423SLionel Sambuc  * PARAMETERS:  ObjDesc                 - Field to be written
604433d6423SLionel Sambuc  *              Mask                    - bitmask within field datum
605433d6423SLionel Sambuc  *              FieldValue              - Value to write
606433d6423SLionel Sambuc  *              FieldDatumByteOffset    - Offset of datum within field
607433d6423SLionel Sambuc  *
608433d6423SLionel Sambuc  * RETURN:      Status
609433d6423SLionel Sambuc  *
610433d6423SLionel Sambuc  * DESCRIPTION: Apply the field update rule to a field write
611433d6423SLionel Sambuc  *
612433d6423SLionel Sambuc  ******************************************************************************/
613433d6423SLionel Sambuc 
614433d6423SLionel Sambuc ACPI_STATUS
AcpiExWriteWithUpdateRule(ACPI_OPERAND_OBJECT * ObjDesc,UINT64 Mask,UINT64 FieldValue,UINT32 FieldDatumByteOffset)615433d6423SLionel Sambuc AcpiExWriteWithUpdateRule (
616433d6423SLionel Sambuc     ACPI_OPERAND_OBJECT     *ObjDesc,
617433d6423SLionel Sambuc     UINT64                  Mask,
618433d6423SLionel Sambuc     UINT64                  FieldValue,
619433d6423SLionel Sambuc     UINT32                  FieldDatumByteOffset)
620433d6423SLionel Sambuc {
621433d6423SLionel Sambuc     ACPI_STATUS             Status = AE_OK;
622433d6423SLionel Sambuc     UINT64                  MergedValue;
623433d6423SLionel Sambuc     UINT64                  CurrentValue;
624433d6423SLionel Sambuc 
625433d6423SLionel Sambuc 
626433d6423SLionel Sambuc     ACPI_FUNCTION_TRACE_U32 (ExWriteWithUpdateRule, Mask);
627433d6423SLionel Sambuc 
628433d6423SLionel Sambuc 
629433d6423SLionel Sambuc     /* Start with the new bits  */
630433d6423SLionel Sambuc 
631433d6423SLionel Sambuc     MergedValue = FieldValue;
632433d6423SLionel Sambuc 
633433d6423SLionel Sambuc     /* If the mask is all ones, we don't need to worry about the update rule */
634433d6423SLionel Sambuc 
635433d6423SLionel Sambuc     if (Mask != ACPI_UINT64_MAX)
636433d6423SLionel Sambuc     {
637433d6423SLionel Sambuc         /* Decode the update rule */
638433d6423SLionel Sambuc 
639433d6423SLionel Sambuc         switch (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)
640433d6423SLionel Sambuc         {
641433d6423SLionel Sambuc         case AML_FIELD_UPDATE_PRESERVE:
642433d6423SLionel Sambuc             /*
643433d6423SLionel Sambuc              * Check if update rule needs to be applied (not if mask is all
644433d6423SLionel Sambuc              * ones)  The left shift drops the bits we want to ignore.
645433d6423SLionel Sambuc              */
646433d6423SLionel Sambuc             if ((~Mask << (ACPI_MUL_8 (sizeof (Mask)) -
647433d6423SLionel Sambuc                            ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth))) != 0)
648433d6423SLionel Sambuc             {
649433d6423SLionel Sambuc                 /*
650433d6423SLionel Sambuc                  * Read the current contents of the byte/word/dword containing
651433d6423SLionel Sambuc                  * the field, and merge with the new field value.
652433d6423SLionel Sambuc                  */
653433d6423SLionel Sambuc                 Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
654433d6423SLionel Sambuc                             &CurrentValue, ACPI_READ);
655433d6423SLionel Sambuc                 if (ACPI_FAILURE (Status))
656433d6423SLionel Sambuc                 {
657433d6423SLionel Sambuc                     return_ACPI_STATUS (Status);
658433d6423SLionel Sambuc                 }
659433d6423SLionel Sambuc 
660433d6423SLionel Sambuc                 MergedValue |= (CurrentValue & ~Mask);
661433d6423SLionel Sambuc             }
662433d6423SLionel Sambuc             break;
663433d6423SLionel Sambuc 
664433d6423SLionel Sambuc         case AML_FIELD_UPDATE_WRITE_AS_ONES:
665433d6423SLionel Sambuc 
666433d6423SLionel Sambuc             /* Set positions outside the field to all ones */
667433d6423SLionel Sambuc 
668433d6423SLionel Sambuc             MergedValue |= ~Mask;
669433d6423SLionel Sambuc             break;
670433d6423SLionel Sambuc 
671433d6423SLionel Sambuc         case AML_FIELD_UPDATE_WRITE_AS_ZEROS:
672433d6423SLionel Sambuc 
673433d6423SLionel Sambuc             /* Set positions outside the field to all zeros */
674433d6423SLionel Sambuc 
675433d6423SLionel Sambuc             MergedValue &= Mask;
676433d6423SLionel Sambuc             break;
677433d6423SLionel Sambuc 
678433d6423SLionel Sambuc         default:
679433d6423SLionel Sambuc 
680433d6423SLionel Sambuc             ACPI_ERROR ((AE_INFO,
681433d6423SLionel Sambuc                 "Unknown UpdateRule value: 0x%X",
682433d6423SLionel Sambuc                 (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)));
683433d6423SLionel Sambuc             return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
684433d6423SLionel Sambuc         }
685433d6423SLionel Sambuc     }
686433d6423SLionel Sambuc 
687433d6423SLionel Sambuc     ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
688433d6423SLionel Sambuc         "Mask %8.8X%8.8X, DatumOffset %X, Width %X, Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
689433d6423SLionel Sambuc         ACPI_FORMAT_UINT64 (Mask),
690433d6423SLionel Sambuc         FieldDatumByteOffset,
691433d6423SLionel Sambuc         ObjDesc->CommonField.AccessByteWidth,
692433d6423SLionel Sambuc         ACPI_FORMAT_UINT64 (FieldValue),
693433d6423SLionel Sambuc         ACPI_FORMAT_UINT64 (MergedValue)));
694433d6423SLionel Sambuc 
695433d6423SLionel Sambuc     /* Write the merged value */
696433d6423SLionel Sambuc 
697433d6423SLionel Sambuc     Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
698433d6423SLionel Sambuc                 &MergedValue, ACPI_WRITE);
699433d6423SLionel Sambuc 
700433d6423SLionel Sambuc     return_ACPI_STATUS (Status);
701433d6423SLionel Sambuc }
702433d6423SLionel Sambuc 
703433d6423SLionel Sambuc 
704433d6423SLionel Sambuc /*******************************************************************************
705433d6423SLionel Sambuc  *
706433d6423SLionel Sambuc  * FUNCTION:    AcpiExExtractFromField
707433d6423SLionel Sambuc  *
708433d6423SLionel Sambuc  * PARAMETERS:  ObjDesc             - Field to be read
709433d6423SLionel Sambuc  *              Buffer              - Where to store the field data
710433d6423SLionel Sambuc  *              BufferLength        - Length of Buffer
711433d6423SLionel Sambuc  *
712433d6423SLionel Sambuc  * RETURN:      Status
713433d6423SLionel Sambuc  *
714433d6423SLionel Sambuc  * DESCRIPTION: Retrieve the current value of the given field
715433d6423SLionel Sambuc  *
716433d6423SLionel Sambuc  ******************************************************************************/
717433d6423SLionel Sambuc 
718433d6423SLionel Sambuc ACPI_STATUS
AcpiExExtractFromField(ACPI_OPERAND_OBJECT * ObjDesc,void * Buffer,UINT32 BufferLength)719433d6423SLionel Sambuc AcpiExExtractFromField (
720433d6423SLionel Sambuc     ACPI_OPERAND_OBJECT     *ObjDesc,
721433d6423SLionel Sambuc     void                    *Buffer,
722433d6423SLionel Sambuc     UINT32                  BufferLength)
723433d6423SLionel Sambuc {
724433d6423SLionel Sambuc     ACPI_STATUS             Status;
725433d6423SLionel Sambuc     UINT64                  RawDatum;
726433d6423SLionel Sambuc     UINT64                  MergedDatum;
727433d6423SLionel Sambuc     UINT32                  FieldOffset = 0;
728433d6423SLionel Sambuc     UINT32                  BufferOffset = 0;
729433d6423SLionel Sambuc     UINT32                  BufferTailBits;
730433d6423SLionel Sambuc     UINT32                  DatumCount;
731433d6423SLionel Sambuc     UINT32                  FieldDatumCount;
732*29492bb7SDavid van Moolenbroek     UINT32                  AccessBitWidth;
733433d6423SLionel Sambuc     UINT32                  i;
734433d6423SLionel Sambuc 
735433d6423SLionel Sambuc 
736433d6423SLionel Sambuc     ACPI_FUNCTION_TRACE (ExExtractFromField);
737433d6423SLionel Sambuc 
738433d6423SLionel Sambuc 
739433d6423SLionel Sambuc     /* Validate target buffer and clear it */
740433d6423SLionel Sambuc 
741433d6423SLionel Sambuc     if (BufferLength <
742433d6423SLionel Sambuc         ACPI_ROUND_BITS_UP_TO_BYTES (ObjDesc->CommonField.BitLength))
743433d6423SLionel Sambuc     {
744433d6423SLionel Sambuc         ACPI_ERROR ((AE_INFO,
745433d6423SLionel Sambuc             "Field size %u (bits) is too large for buffer (%u)",
746433d6423SLionel Sambuc             ObjDesc->CommonField.BitLength, BufferLength));
747433d6423SLionel Sambuc 
748433d6423SLionel Sambuc         return_ACPI_STATUS (AE_BUFFER_OVERFLOW);
749433d6423SLionel Sambuc     }
750*29492bb7SDavid van Moolenbroek 
751433d6423SLionel Sambuc     ACPI_MEMSET (Buffer, 0, BufferLength);
752*29492bb7SDavid van Moolenbroek     AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
753*29492bb7SDavid van Moolenbroek 
754*29492bb7SDavid van Moolenbroek     /* Handle the simple case here */
755*29492bb7SDavid van Moolenbroek 
756*29492bb7SDavid van Moolenbroek     if ((ObjDesc->CommonField.StartFieldBitOffset == 0) &&
757*29492bb7SDavid van Moolenbroek         (ObjDesc->CommonField.BitLength == AccessBitWidth))
758*29492bb7SDavid van Moolenbroek     {
759*29492bb7SDavid van Moolenbroek         if (BufferLength >= sizeof (UINT64))
760*29492bb7SDavid van Moolenbroek         {
761*29492bb7SDavid van Moolenbroek             Status = AcpiExFieldDatumIo (ObjDesc, 0, Buffer, ACPI_READ);
762*29492bb7SDavid van Moolenbroek         }
763*29492bb7SDavid van Moolenbroek         else
764*29492bb7SDavid van Moolenbroek         {
765*29492bb7SDavid van Moolenbroek             /* Use RawDatum (UINT64) to handle buffers < 64 bits */
766*29492bb7SDavid van Moolenbroek 
767*29492bb7SDavid van Moolenbroek             Status = AcpiExFieldDatumIo (ObjDesc, 0, &RawDatum, ACPI_READ);
768*29492bb7SDavid van Moolenbroek             ACPI_MEMCPY (Buffer, &RawDatum, BufferLength);
769*29492bb7SDavid van Moolenbroek         }
770*29492bb7SDavid van Moolenbroek 
771*29492bb7SDavid van Moolenbroek         return_ACPI_STATUS (Status);
772*29492bb7SDavid van Moolenbroek     }
773*29492bb7SDavid van Moolenbroek 
774*29492bb7SDavid van Moolenbroek /* TBD: Move to common setup code */
775*29492bb7SDavid van Moolenbroek 
776*29492bb7SDavid van Moolenbroek     /* Field algorithm is limited to sizeof(UINT64), truncate if needed */
777*29492bb7SDavid van Moolenbroek 
778*29492bb7SDavid van Moolenbroek     if (ObjDesc->CommonField.AccessByteWidth > sizeof (UINT64))
779*29492bb7SDavid van Moolenbroek     {
780*29492bb7SDavid van Moolenbroek         ObjDesc->CommonField.AccessByteWidth = sizeof (UINT64);
781*29492bb7SDavid van Moolenbroek         AccessBitWidth = sizeof (UINT64) * 8;
782*29492bb7SDavid van Moolenbroek     }
783433d6423SLionel Sambuc 
784433d6423SLionel Sambuc     /* Compute the number of datums (access width data items) */
785433d6423SLionel Sambuc 
786433d6423SLionel Sambuc     DatumCount = ACPI_ROUND_UP_TO (
787*29492bb7SDavid van Moolenbroek         ObjDesc->CommonField.BitLength, AccessBitWidth);
788*29492bb7SDavid van Moolenbroek 
789433d6423SLionel Sambuc     FieldDatumCount = ACPI_ROUND_UP_TO (
790433d6423SLionel Sambuc         ObjDesc->CommonField.BitLength +
791*29492bb7SDavid van Moolenbroek         ObjDesc->CommonField.StartFieldBitOffset, AccessBitWidth);
792433d6423SLionel Sambuc 
793433d6423SLionel Sambuc     /* Priming read from the field */
794433d6423SLionel Sambuc 
795433d6423SLionel Sambuc     Status = AcpiExFieldDatumIo (ObjDesc, FieldOffset, &RawDatum, ACPI_READ);
796433d6423SLionel Sambuc     if (ACPI_FAILURE (Status))
797433d6423SLionel Sambuc     {
798433d6423SLionel Sambuc         return_ACPI_STATUS (Status);
799433d6423SLionel Sambuc     }
800433d6423SLionel Sambuc     MergedDatum = RawDatum >> ObjDesc->CommonField.StartFieldBitOffset;
801433d6423SLionel Sambuc 
802433d6423SLionel Sambuc     /* Read the rest of the field */
803433d6423SLionel Sambuc 
804433d6423SLionel Sambuc     for (i = 1; i < FieldDatumCount; i++)
805433d6423SLionel Sambuc     {
806433d6423SLionel Sambuc         /* Get next input datum from the field */
807433d6423SLionel Sambuc 
808433d6423SLionel Sambuc         FieldOffset += ObjDesc->CommonField.AccessByteWidth;
809433d6423SLionel Sambuc         Status = AcpiExFieldDatumIo (ObjDesc, FieldOffset,
810433d6423SLionel Sambuc                     &RawDatum, ACPI_READ);
811433d6423SLionel Sambuc         if (ACPI_FAILURE (Status))
812433d6423SLionel Sambuc         {
813433d6423SLionel Sambuc             return_ACPI_STATUS (Status);
814433d6423SLionel Sambuc         }
815433d6423SLionel Sambuc 
816433d6423SLionel Sambuc         /*
817433d6423SLionel Sambuc          * Merge with previous datum if necessary.
818433d6423SLionel Sambuc          *
819433d6423SLionel Sambuc          * Note: Before the shift, check if the shift value will be larger than
820433d6423SLionel Sambuc          * the integer size. If so, there is no need to perform the operation.
821433d6423SLionel Sambuc          * This avoids the differences in behavior between different compilers
822433d6423SLionel Sambuc          * concerning shift values larger than the target data width.
823433d6423SLionel Sambuc          */
824*29492bb7SDavid van Moolenbroek         if (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset <
825*29492bb7SDavid van Moolenbroek             ACPI_INTEGER_BIT_SIZE)
826433d6423SLionel Sambuc         {
827433d6423SLionel Sambuc             MergedDatum |= RawDatum <<
828*29492bb7SDavid van Moolenbroek                 (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset);
829433d6423SLionel Sambuc         }
830433d6423SLionel Sambuc 
831433d6423SLionel Sambuc         if (i == DatumCount)
832433d6423SLionel Sambuc         {
833433d6423SLionel Sambuc             break;
834433d6423SLionel Sambuc         }
835433d6423SLionel Sambuc 
836433d6423SLionel Sambuc         /* Write merged datum to target buffer */
837433d6423SLionel Sambuc 
838433d6423SLionel Sambuc         ACPI_MEMCPY (((char *) Buffer) + BufferOffset, &MergedDatum,
839433d6423SLionel Sambuc             ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
840433d6423SLionel Sambuc                 BufferLength - BufferOffset));
841433d6423SLionel Sambuc 
842433d6423SLionel Sambuc         BufferOffset += ObjDesc->CommonField.AccessByteWidth;
843433d6423SLionel Sambuc         MergedDatum = RawDatum >> ObjDesc->CommonField.StartFieldBitOffset;
844433d6423SLionel Sambuc     }
845433d6423SLionel Sambuc 
846433d6423SLionel Sambuc     /* Mask off any extra bits in the last datum */
847433d6423SLionel Sambuc 
848*29492bb7SDavid van Moolenbroek     BufferTailBits = ObjDesc->CommonField.BitLength % AccessBitWidth;
849433d6423SLionel Sambuc     if (BufferTailBits)
850433d6423SLionel Sambuc     {
851433d6423SLionel Sambuc         MergedDatum &= ACPI_MASK_BITS_ABOVE (BufferTailBits);
852433d6423SLionel Sambuc     }
853433d6423SLionel Sambuc 
854433d6423SLionel Sambuc     /* Write the last datum to the buffer */
855433d6423SLionel Sambuc 
856433d6423SLionel Sambuc     ACPI_MEMCPY (((char *) Buffer) + BufferOffset, &MergedDatum,
857433d6423SLionel Sambuc         ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
858433d6423SLionel Sambuc             BufferLength - BufferOffset));
859433d6423SLionel Sambuc 
860433d6423SLionel Sambuc     return_ACPI_STATUS (AE_OK);
861433d6423SLionel Sambuc }
862433d6423SLionel Sambuc 
863433d6423SLionel Sambuc 
864433d6423SLionel Sambuc /*******************************************************************************
865433d6423SLionel Sambuc  *
866433d6423SLionel Sambuc  * FUNCTION:    AcpiExInsertIntoField
867433d6423SLionel Sambuc  *
868433d6423SLionel Sambuc  * PARAMETERS:  ObjDesc             - Field to be written
869433d6423SLionel Sambuc  *              Buffer              - Data to be written
870433d6423SLionel Sambuc  *              BufferLength        - Length of Buffer
871433d6423SLionel Sambuc  *
872433d6423SLionel Sambuc  * RETURN:      Status
873433d6423SLionel Sambuc  *
874433d6423SLionel Sambuc  * DESCRIPTION: Store the Buffer contents into the given field
875433d6423SLionel Sambuc  *
876433d6423SLionel Sambuc  ******************************************************************************/
877433d6423SLionel Sambuc 
878433d6423SLionel Sambuc ACPI_STATUS
AcpiExInsertIntoField(ACPI_OPERAND_OBJECT * ObjDesc,void * Buffer,UINT32 BufferLength)879433d6423SLionel Sambuc AcpiExInsertIntoField (
880433d6423SLionel Sambuc     ACPI_OPERAND_OBJECT     *ObjDesc,
881433d6423SLionel Sambuc     void                    *Buffer,
882433d6423SLionel Sambuc     UINT32                  BufferLength)
883433d6423SLionel Sambuc {
884*29492bb7SDavid van Moolenbroek     void                    *NewBuffer;
885433d6423SLionel Sambuc     ACPI_STATUS             Status;
886433d6423SLionel Sambuc     UINT64                  Mask;
887433d6423SLionel Sambuc     UINT64                  WidthMask;
888433d6423SLionel Sambuc     UINT64                  MergedDatum;
889433d6423SLionel Sambuc     UINT64                  RawDatum = 0;
890433d6423SLionel Sambuc     UINT32                  FieldOffset = 0;
891433d6423SLionel Sambuc     UINT32                  BufferOffset = 0;
892433d6423SLionel Sambuc     UINT32                  BufferTailBits;
893433d6423SLionel Sambuc     UINT32                  DatumCount;
894433d6423SLionel Sambuc     UINT32                  FieldDatumCount;
895*29492bb7SDavid van Moolenbroek     UINT32                  AccessBitWidth;
896433d6423SLionel Sambuc     UINT32                  RequiredLength;
897*29492bb7SDavid van Moolenbroek     UINT32                  i;
898433d6423SLionel Sambuc 
899433d6423SLionel Sambuc 
900433d6423SLionel Sambuc     ACPI_FUNCTION_TRACE (ExInsertIntoField);
901433d6423SLionel Sambuc 
902433d6423SLionel Sambuc 
903433d6423SLionel Sambuc     /* Validate input buffer */
904433d6423SLionel Sambuc 
905433d6423SLionel Sambuc     NewBuffer = NULL;
906433d6423SLionel Sambuc     RequiredLength = ACPI_ROUND_BITS_UP_TO_BYTES (
907433d6423SLionel Sambuc                         ObjDesc->CommonField.BitLength);
908433d6423SLionel Sambuc     /*
909433d6423SLionel Sambuc      * We must have a buffer that is at least as long as the field
910433d6423SLionel Sambuc      * we are writing to. This is because individual fields are
911433d6423SLionel Sambuc      * indivisible and partial writes are not supported -- as per
912433d6423SLionel Sambuc      * the ACPI specification.
913433d6423SLionel Sambuc      */
914433d6423SLionel Sambuc     if (BufferLength < RequiredLength)
915433d6423SLionel Sambuc     {
916433d6423SLionel Sambuc         /* We need to create a new buffer */
917433d6423SLionel Sambuc 
918433d6423SLionel Sambuc         NewBuffer = ACPI_ALLOCATE_ZEROED (RequiredLength);
919433d6423SLionel Sambuc         if (!NewBuffer)
920433d6423SLionel Sambuc         {
921433d6423SLionel Sambuc             return_ACPI_STATUS (AE_NO_MEMORY);
922433d6423SLionel Sambuc         }
923433d6423SLionel Sambuc 
924433d6423SLionel Sambuc         /*
925433d6423SLionel Sambuc          * Copy the original data to the new buffer, starting
926433d6423SLionel Sambuc          * at Byte zero. All unused (upper) bytes of the
927433d6423SLionel Sambuc          * buffer will be 0.
928433d6423SLionel Sambuc          */
929433d6423SLionel Sambuc         ACPI_MEMCPY ((char *) NewBuffer, (char *) Buffer, BufferLength);
930433d6423SLionel Sambuc         Buffer = NewBuffer;
931433d6423SLionel Sambuc         BufferLength = RequiredLength;
932433d6423SLionel Sambuc     }
933433d6423SLionel Sambuc 
934*29492bb7SDavid van Moolenbroek /* TBD: Move to common setup code */
935*29492bb7SDavid van Moolenbroek 
936*29492bb7SDavid van Moolenbroek     /* Algo is limited to sizeof(UINT64), so cut the AccessByteWidth */
937*29492bb7SDavid van Moolenbroek     if (ObjDesc->CommonField.AccessByteWidth > sizeof (UINT64))
938*29492bb7SDavid van Moolenbroek     {
939*29492bb7SDavid van Moolenbroek         ObjDesc->CommonField.AccessByteWidth = sizeof (UINT64);
940*29492bb7SDavid van Moolenbroek     }
941*29492bb7SDavid van Moolenbroek 
942*29492bb7SDavid van Moolenbroek     AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
943*29492bb7SDavid van Moolenbroek 
944433d6423SLionel Sambuc     /*
945433d6423SLionel Sambuc      * Create the bitmasks used for bit insertion.
946433d6423SLionel Sambuc      * Note: This if/else is used to bypass compiler differences with the
947433d6423SLionel Sambuc      * shift operator
948433d6423SLionel Sambuc      */
949*29492bb7SDavid van Moolenbroek     if (AccessBitWidth == ACPI_INTEGER_BIT_SIZE)
950433d6423SLionel Sambuc     {
951433d6423SLionel Sambuc         WidthMask = ACPI_UINT64_MAX;
952433d6423SLionel Sambuc     }
953433d6423SLionel Sambuc     else
954433d6423SLionel Sambuc     {
955*29492bb7SDavid van Moolenbroek         WidthMask = ACPI_MASK_BITS_ABOVE (AccessBitWidth);
956433d6423SLionel Sambuc     }
957433d6423SLionel Sambuc 
958433d6423SLionel Sambuc     Mask = WidthMask &
959433d6423SLionel Sambuc         ACPI_MASK_BITS_BELOW (ObjDesc->CommonField.StartFieldBitOffset);
960433d6423SLionel Sambuc 
961433d6423SLionel Sambuc     /* Compute the number of datums (access width data items) */
962433d6423SLionel Sambuc 
963433d6423SLionel Sambuc     DatumCount = ACPI_ROUND_UP_TO (ObjDesc->CommonField.BitLength,
964*29492bb7SDavid van Moolenbroek         AccessBitWidth);
965433d6423SLionel Sambuc 
966433d6423SLionel Sambuc     FieldDatumCount = ACPI_ROUND_UP_TO (ObjDesc->CommonField.BitLength +
967433d6423SLionel Sambuc         ObjDesc->CommonField.StartFieldBitOffset,
968*29492bb7SDavid van Moolenbroek         AccessBitWidth);
969433d6423SLionel Sambuc 
970433d6423SLionel Sambuc     /* Get initial Datum from the input buffer */
971433d6423SLionel Sambuc 
972433d6423SLionel Sambuc     ACPI_MEMCPY (&RawDatum, Buffer,
973433d6423SLionel Sambuc         ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
974433d6423SLionel Sambuc             BufferLength - BufferOffset));
975433d6423SLionel Sambuc 
976433d6423SLionel Sambuc     MergedDatum = RawDatum << ObjDesc->CommonField.StartFieldBitOffset;
977433d6423SLionel Sambuc 
978433d6423SLionel Sambuc     /* Write the entire field */
979433d6423SLionel Sambuc 
980433d6423SLionel Sambuc     for (i = 1; i < FieldDatumCount; i++)
981433d6423SLionel Sambuc     {
982433d6423SLionel Sambuc         /* Write merged datum to the target field */
983433d6423SLionel Sambuc 
984433d6423SLionel Sambuc         MergedDatum &= Mask;
985433d6423SLionel Sambuc         Status = AcpiExWriteWithUpdateRule (ObjDesc, Mask,
986433d6423SLionel Sambuc                     MergedDatum, FieldOffset);
987433d6423SLionel Sambuc         if (ACPI_FAILURE (Status))
988433d6423SLionel Sambuc         {
989433d6423SLionel Sambuc             goto Exit;
990433d6423SLionel Sambuc         }
991433d6423SLionel Sambuc 
992433d6423SLionel Sambuc         FieldOffset += ObjDesc->CommonField.AccessByteWidth;
993433d6423SLionel Sambuc 
994433d6423SLionel Sambuc         /*
995433d6423SLionel Sambuc          * Start new output datum by merging with previous input datum
996433d6423SLionel Sambuc          * if necessary.
997433d6423SLionel Sambuc          *
998433d6423SLionel Sambuc          * Note: Before the shift, check if the shift value will be larger than
999433d6423SLionel Sambuc          * the integer size. If so, there is no need to perform the operation.
1000433d6423SLionel Sambuc          * This avoids the differences in behavior between different compilers
1001433d6423SLionel Sambuc          * concerning shift values larger than the target data width.
1002433d6423SLionel Sambuc          */
1003*29492bb7SDavid van Moolenbroek         if ((AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset) <
1004*29492bb7SDavid van Moolenbroek             ACPI_INTEGER_BIT_SIZE)
1005433d6423SLionel Sambuc         {
1006433d6423SLionel Sambuc             MergedDatum = RawDatum >>
1007*29492bb7SDavid van Moolenbroek                 (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset);
1008433d6423SLionel Sambuc         }
1009433d6423SLionel Sambuc         else
1010433d6423SLionel Sambuc         {
1011433d6423SLionel Sambuc             MergedDatum = 0;
1012433d6423SLionel Sambuc         }
1013433d6423SLionel Sambuc 
1014433d6423SLionel Sambuc         Mask = WidthMask;
1015433d6423SLionel Sambuc 
1016433d6423SLionel Sambuc         if (i == DatumCount)
1017433d6423SLionel Sambuc         {
1018433d6423SLionel Sambuc             break;
1019433d6423SLionel Sambuc         }
1020433d6423SLionel Sambuc 
1021433d6423SLionel Sambuc         /* Get the next input datum from the buffer */
1022433d6423SLionel Sambuc 
1023433d6423SLionel Sambuc         BufferOffset += ObjDesc->CommonField.AccessByteWidth;
1024433d6423SLionel Sambuc         ACPI_MEMCPY (&RawDatum, ((char *) Buffer) + BufferOffset,
1025433d6423SLionel Sambuc             ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
1026433d6423SLionel Sambuc                  BufferLength - BufferOffset));
1027*29492bb7SDavid van Moolenbroek 
1028433d6423SLionel Sambuc         MergedDatum |= RawDatum << ObjDesc->CommonField.StartFieldBitOffset;
1029433d6423SLionel Sambuc     }
1030433d6423SLionel Sambuc 
1031433d6423SLionel Sambuc     /* Mask off any extra bits in the last datum */
1032433d6423SLionel Sambuc 
1033433d6423SLionel Sambuc     BufferTailBits = (ObjDesc->CommonField.BitLength +
1034*29492bb7SDavid van Moolenbroek         ObjDesc->CommonField.StartFieldBitOffset) % AccessBitWidth;
1035433d6423SLionel Sambuc     if (BufferTailBits)
1036433d6423SLionel Sambuc     {
1037433d6423SLionel Sambuc         Mask &= ACPI_MASK_BITS_ABOVE (BufferTailBits);
1038433d6423SLionel Sambuc     }
1039433d6423SLionel Sambuc 
1040433d6423SLionel Sambuc     /* Write the last datum to the field */
1041433d6423SLionel Sambuc 
1042433d6423SLionel Sambuc     MergedDatum &= Mask;
1043433d6423SLionel Sambuc     Status = AcpiExWriteWithUpdateRule (ObjDesc,
1044433d6423SLionel Sambuc                 Mask, MergedDatum, FieldOffset);
1045433d6423SLionel Sambuc 
1046433d6423SLionel Sambuc Exit:
1047433d6423SLionel Sambuc     /* Free temporary buffer if we used one */
1048433d6423SLionel Sambuc 
1049433d6423SLionel Sambuc     if (NewBuffer)
1050433d6423SLionel Sambuc     {
1051433d6423SLionel Sambuc         ACPI_FREE (NewBuffer);
1052433d6423SLionel Sambuc     }
1053433d6423SLionel Sambuc     return_ACPI_STATUS (Status);
1054433d6423SLionel Sambuc }
1055