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