exfldio.c revision 281687
1/******************************************************************************
2 *
3 * Module Name: exfldio - Aml Field I/O
4 *
5 *****************************************************************************/
6
7/*
8 * Copyright (C) 2000 - 2015, Intel Corp.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions, and the following disclaimer,
16 *    without modification.
17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18 *    substantially similar to the "NO WARRANTY" disclaimer below
19 *    ("Disclaimer") and any redistribution must be conditioned upon
20 *    including a substantially similar Disclaimer requirement for further
21 *    binary redistribution.
22 * 3. Neither the names of the above-listed copyright holders nor the names
23 *    of any contributors may be used to endorse or promote products derived
24 *    from this software without specific prior written permission.
25 *
26 * Alternatively, this software may be distributed under the terms of the
27 * GNU General Public License ("GPL") version 2 as published by the Free
28 * Software Foundation.
29 *
30 * NO WARRANTY
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41 * POSSIBILITY OF SUCH DAMAGES.
42 */
43
44#include <contrib/dev/acpica/include/acpi.h>
45#include <contrib/dev/acpica/include/accommon.h>
46#include <contrib/dev/acpica/include/acinterp.h>
47#include <contrib/dev/acpica/include/amlcode.h>
48#include <contrib/dev/acpica/include/acevents.h>
49#include <contrib/dev/acpica/include/acdispat.h>
50
51
52#define _COMPONENT          ACPI_EXECUTER
53        ACPI_MODULE_NAME    ("exfldio")
54
55/* Local prototypes */
56
57static ACPI_STATUS
58AcpiExFieldDatumIo (
59    ACPI_OPERAND_OBJECT     *ObjDesc,
60    UINT32                  FieldDatumByteOffset,
61    UINT64                  *Value,
62    UINT32                  ReadWrite);
63
64static BOOLEAN
65AcpiExRegisterOverflow (
66    ACPI_OPERAND_OBJECT     *ObjDesc,
67    UINT64                  Value);
68
69static ACPI_STATUS
70AcpiExSetupRegion (
71    ACPI_OPERAND_OBJECT     *ObjDesc,
72    UINT32                  FieldDatumByteOffset);
73
74
75/*******************************************************************************
76 *
77 * FUNCTION:    AcpiExSetupRegion
78 *
79 * PARAMETERS:  ObjDesc                 - Field to be read or written
80 *              FieldDatumByteOffset    - Byte offset of this datum within the
81 *                                        parent field
82 *
83 * RETURN:      Status
84 *
85 * DESCRIPTION: Common processing for AcpiExExtractFromField and
86 *              AcpiExInsertIntoField. Initialize the Region if necessary and
87 *              validate the request.
88 *
89 ******************************************************************************/
90
91static ACPI_STATUS
92AcpiExSetupRegion (
93    ACPI_OPERAND_OBJECT     *ObjDesc,
94    UINT32                  FieldDatumByteOffset)
95{
96    ACPI_STATUS             Status = AE_OK;
97    ACPI_OPERAND_OBJECT     *RgnDesc;
98    UINT8                   SpaceId;
99
100
101    ACPI_FUNCTION_TRACE_U32 (ExSetupRegion, FieldDatumByteOffset);
102
103
104    RgnDesc = ObjDesc->CommonField.RegionObj;
105
106    /* We must have a valid region */
107
108    if (RgnDesc->Common.Type != ACPI_TYPE_REGION)
109    {
110        ACPI_ERROR ((AE_INFO, "Needed Region, found type 0x%X (%s)",
111            RgnDesc->Common.Type,
112            AcpiUtGetObjectTypeName (RgnDesc)));
113
114        return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
115    }
116
117    SpaceId = RgnDesc->Region.SpaceId;
118
119    /* Validate the Space ID */
120
121    if (!AcpiIsValidSpaceId (SpaceId))
122    {
123        ACPI_ERROR ((AE_INFO, "Invalid/unknown Address Space ID: 0x%2.2X", SpaceId));
124        return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID);
125    }
126
127    /*
128     * If the Region Address and Length have not been previously evaluated,
129     * evaluate them now and save the results.
130     */
131    if (!(RgnDesc->Common.Flags & AOPOBJ_DATA_VALID))
132    {
133        Status = AcpiDsGetRegionArguments (RgnDesc);
134        if (ACPI_FAILURE (Status))
135        {
136            return_ACPI_STATUS (Status);
137        }
138    }
139
140    /*
141     * Exit now for SMBus, GSBus or IPMI address space, it has a non-linear
142     * address space and the request cannot be directly validated
143     */
144    if (SpaceId == ACPI_ADR_SPACE_SMBUS ||
145        SpaceId == ACPI_ADR_SPACE_GSBUS ||
146        SpaceId == ACPI_ADR_SPACE_IPMI)
147    {
148        /* SMBus or IPMI has a non-linear address space */
149
150        return_ACPI_STATUS (AE_OK);
151    }
152
153#ifdef ACPI_UNDER_DEVELOPMENT
154    /*
155     * If the Field access is AnyAcc, we can now compute the optimal
156     * access (because we know know the length of the parent region)
157     */
158    if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
159    {
160        if (ACPI_FAILURE (Status))
161        {
162            return_ACPI_STATUS (Status);
163        }
164    }
165#endif
166
167    /*
168     * Validate the request. The entire request from the byte offset for a
169     * length of one field datum (access width) must fit within the region.
170     * (Region length is specified in bytes)
171     */
172    if (RgnDesc->Region.Length <
173            (ObjDesc->CommonField.BaseByteOffset + FieldDatumByteOffset +
174            ObjDesc->CommonField.AccessByteWidth))
175    {
176        if (AcpiGbl_EnableInterpreterSlack)
177        {
178            /*
179             * Slack mode only:  We will go ahead and allow access to this
180             * field if it is within the region length rounded up to the next
181             * access width boundary. ACPI_SIZE cast for 64-bit compile.
182             */
183            if (ACPI_ROUND_UP (RgnDesc->Region.Length,
184                    ObjDesc->CommonField.AccessByteWidth) >=
185                ((ACPI_SIZE) ObjDesc->CommonField.BaseByteOffset +
186                    ObjDesc->CommonField.AccessByteWidth +
187                    FieldDatumByteOffset))
188            {
189                return_ACPI_STATUS (AE_OK);
190            }
191        }
192
193        if (RgnDesc->Region.Length < ObjDesc->CommonField.AccessByteWidth)
194        {
195            /*
196             * This is the case where the AccessType (AccWord, etc.) is wider
197             * than the region itself. For example, a region of length one
198             * byte, and a field with Dword access specified.
199             */
200            ACPI_ERROR ((AE_INFO,
201                "Field [%4.4s] access width (%u bytes) too large for region [%4.4s] (length %u)",
202                AcpiUtGetNodeName (ObjDesc->CommonField.Node),
203                ObjDesc->CommonField.AccessByteWidth,
204                AcpiUtGetNodeName (RgnDesc->Region.Node),
205                RgnDesc->Region.Length));
206        }
207
208        /*
209         * Offset rounded up to next multiple of field width
210         * exceeds region length, indicate an error
211         */
212        ACPI_ERROR ((AE_INFO,
213            "Field [%4.4s] Base+Offset+Width %u+%u+%u is beyond end of region [%4.4s] (length %u)",
214            AcpiUtGetNodeName (ObjDesc->CommonField.Node),
215            ObjDesc->CommonField.BaseByteOffset,
216            FieldDatumByteOffset, ObjDesc->CommonField.AccessByteWidth,
217            AcpiUtGetNodeName (RgnDesc->Region.Node),
218            RgnDesc->Region.Length));
219
220        return_ACPI_STATUS (AE_AML_REGION_LIMIT);
221    }
222
223    return_ACPI_STATUS (AE_OK);
224}
225
226
227/*******************************************************************************
228 *
229 * FUNCTION:    AcpiExAccessRegion
230 *
231 * PARAMETERS:  ObjDesc                 - Field to be read
232 *              FieldDatumByteOffset    - Byte offset of this datum within the
233 *                                        parent field
234 *              Value                   - Where to store value (must at least
235 *                                        64 bits)
236 *              Function                - Read or Write flag plus other region-
237 *                                        dependent flags
238 *
239 * RETURN:      Status
240 *
241 * DESCRIPTION: Read or Write a single field datum to an Operation Region.
242 *
243 ******************************************************************************/
244
245ACPI_STATUS
246AcpiExAccessRegion (
247    ACPI_OPERAND_OBJECT     *ObjDesc,
248    UINT32                  FieldDatumByteOffset,
249    UINT64                  *Value,
250    UINT32                  Function)
251{
252    ACPI_STATUS             Status;
253    ACPI_OPERAND_OBJECT     *RgnDesc;
254    UINT32                  RegionOffset;
255
256
257    ACPI_FUNCTION_TRACE (ExAccessRegion);
258
259
260    /*
261     * Ensure that the region operands are fully evaluated and verify
262     * the validity of the request
263     */
264    Status = AcpiExSetupRegion (ObjDesc, FieldDatumByteOffset);
265    if (ACPI_FAILURE (Status))
266    {
267        return_ACPI_STATUS (Status);
268    }
269
270    /*
271     * The physical address of this field datum is:
272     *
273     * 1) The base of the region, plus
274     * 2) The base offset of the field, plus
275     * 3) The current offset into the field
276     */
277    RgnDesc = ObjDesc->CommonField.RegionObj;
278    RegionOffset =
279        ObjDesc->CommonField.BaseByteOffset +
280        FieldDatumByteOffset;
281
282    if ((Function & ACPI_IO_MASK) == ACPI_READ)
283    {
284        ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "[READ]"));
285    }
286    else
287    {
288        ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "[WRITE]"));
289    }
290
291    ACPI_DEBUG_PRINT_RAW ((ACPI_DB_BFIELD,
292        " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %8.8X%8.8X\n",
293        AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
294        RgnDesc->Region.SpaceId,
295        ObjDesc->CommonField.AccessByteWidth,
296        ObjDesc->CommonField.BaseByteOffset,
297        FieldDatumByteOffset,
298        ACPI_FORMAT_UINT64 (RgnDesc->Region.Address + RegionOffset)));
299
300    /* Invoke the appropriate AddressSpace/OpRegion handler */
301
302    Status = AcpiEvAddressSpaceDispatch (RgnDesc, ObjDesc,
303                Function, RegionOffset,
304                ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth), Value);
305
306    if (ACPI_FAILURE (Status))
307    {
308        if (Status == AE_NOT_IMPLEMENTED)
309        {
310            ACPI_ERROR ((AE_INFO,
311                "Region %s (ID=%u) not implemented",
312                AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
313                RgnDesc->Region.SpaceId));
314        }
315        else if (Status == AE_NOT_EXIST)
316        {
317            ACPI_ERROR ((AE_INFO,
318                "Region %s (ID=%u) has no handler",
319                AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
320                RgnDesc->Region.SpaceId));
321        }
322    }
323
324    return_ACPI_STATUS (Status);
325}
326
327
328/*******************************************************************************
329 *
330 * FUNCTION:    AcpiExRegisterOverflow
331 *
332 * PARAMETERS:  ObjDesc                 - Register(Field) to be written
333 *              Value                   - Value to be stored
334 *
335 * RETURN:      TRUE if value overflows the field, FALSE otherwise
336 *
337 * DESCRIPTION: Check if a value is out of range of the field being written.
338 *              Used to check if the values written to Index and Bank registers
339 *              are out of range. Normally, the value is simply truncated
340 *              to fit the field, but this case is most likely a serious
341 *              coding error in the ASL.
342 *
343 ******************************************************************************/
344
345static BOOLEAN
346AcpiExRegisterOverflow (
347    ACPI_OPERAND_OBJECT     *ObjDesc,
348    UINT64                  Value)
349{
350
351    if (ObjDesc->CommonField.BitLength >= ACPI_INTEGER_BIT_SIZE)
352    {
353        /*
354         * The field is large enough to hold the maximum integer, so we can
355         * never overflow it.
356         */
357        return (FALSE);
358    }
359
360    if (Value >= ((UINT64) 1 << ObjDesc->CommonField.BitLength))
361    {
362        /*
363         * The Value is larger than the maximum value that can fit into
364         * the register.
365         */
366        ACPI_ERROR ((AE_INFO,
367            "Index value 0x%8.8X%8.8X overflows field width 0x%X",
368            ACPI_FORMAT_UINT64 (Value),
369            ObjDesc->CommonField.BitLength));
370
371        return (TRUE);
372    }
373
374    /* The Value will fit into the field with no truncation */
375
376    return (FALSE);
377}
378
379
380/*******************************************************************************
381 *
382 * FUNCTION:    AcpiExFieldDatumIo
383 *
384 * PARAMETERS:  ObjDesc                 - Field to be read
385 *              FieldDatumByteOffset    - Byte offset of this datum within the
386 *                                        parent field
387 *              Value                   - Where to store value (must be 64 bits)
388 *              ReadWrite               - Read or Write flag
389 *
390 * RETURN:      Status
391 *
392 * DESCRIPTION: Read or Write a single datum of a field. The FieldType is
393 *              demultiplexed here to handle the different types of fields
394 *              (BufferField, RegionField, IndexField, BankField)
395 *
396 ******************************************************************************/
397
398static ACPI_STATUS
399AcpiExFieldDatumIo (
400    ACPI_OPERAND_OBJECT     *ObjDesc,
401    UINT32                  FieldDatumByteOffset,
402    UINT64                  *Value,
403    UINT32                  ReadWrite)
404{
405    ACPI_STATUS             Status;
406    UINT64                  LocalValue;
407
408
409    ACPI_FUNCTION_TRACE_U32 (ExFieldDatumIo, FieldDatumByteOffset);
410
411
412    if (ReadWrite == ACPI_READ)
413    {
414        if (!Value)
415        {
416            LocalValue = 0;
417
418            /* To support reads without saving return value */
419            Value = &LocalValue;
420        }
421
422        /* Clear the entire return buffer first, [Very Important!] */
423
424        *Value = 0;
425    }
426
427    /*
428     * The four types of fields are:
429     *
430     * BufferField - Read/write from/to a Buffer
431     * RegionField - Read/write from/to a Operation Region.
432     * BankField   - Write to a Bank Register, then read/write from/to an
433     *               OperationRegion
434     * IndexField  - Write to an Index Register, then read/write from/to a
435     *               Data Register
436     */
437    switch (ObjDesc->Common.Type)
438    {
439    case ACPI_TYPE_BUFFER_FIELD:
440        /*
441         * If the BufferField arguments have not been previously evaluated,
442         * evaluate them now and save the results.
443         */
444        if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
445        {
446            Status = AcpiDsGetBufferFieldArguments (ObjDesc);
447            if (ACPI_FAILURE (Status))
448            {
449                return_ACPI_STATUS (Status);
450            }
451        }
452
453        if (ReadWrite == ACPI_READ)
454        {
455            /*
456             * Copy the data from the source buffer.
457             * Length is the field width in bytes.
458             */
459            ACPI_MEMCPY (Value,
460                (ObjDesc->BufferField.BufferObj)->Buffer.Pointer +
461                    ObjDesc->BufferField.BaseByteOffset +
462                    FieldDatumByteOffset,
463                ObjDesc->CommonField.AccessByteWidth);
464        }
465        else
466        {
467            /*
468             * Copy the data to the target buffer.
469             * Length is the field width in bytes.
470             */
471            ACPI_MEMCPY ((ObjDesc->BufferField.BufferObj)->Buffer.Pointer +
472                ObjDesc->BufferField.BaseByteOffset +
473                FieldDatumByteOffset,
474                Value, ObjDesc->CommonField.AccessByteWidth);
475        }
476
477        Status = AE_OK;
478        break;
479
480    case ACPI_TYPE_LOCAL_BANK_FIELD:
481        /*
482         * Ensure that the BankValue is not beyond the capacity of
483         * the register
484         */
485        if (AcpiExRegisterOverflow (ObjDesc->BankField.BankObj,
486                (UINT64) ObjDesc->BankField.Value))
487        {
488            return_ACPI_STATUS (AE_AML_REGISTER_LIMIT);
489        }
490
491        /*
492         * For BankFields, we must write the BankValue to the BankRegister
493         * (itself a RegionField) before we can access the data.
494         */
495        Status = AcpiExInsertIntoField (ObjDesc->BankField.BankObj,
496                    &ObjDesc->BankField.Value,
497                    sizeof (ObjDesc->BankField.Value));
498        if (ACPI_FAILURE (Status))
499        {
500            return_ACPI_STATUS (Status);
501        }
502
503        /*
504         * Now that the Bank has been selected, fall through to the
505         * RegionField case and write the datum to the Operation Region
506         */
507
508        /*lint -fallthrough */
509
510    case ACPI_TYPE_LOCAL_REGION_FIELD:
511        /*
512         * For simple RegionFields, we just directly access the owning
513         * Operation Region.
514         */
515        Status = AcpiExAccessRegion (ObjDesc, FieldDatumByteOffset, Value,
516                    ReadWrite);
517        break;
518
519    case ACPI_TYPE_LOCAL_INDEX_FIELD:
520        /*
521         * Ensure that the IndexValue is not beyond the capacity of
522         * the register
523         */
524        if (AcpiExRegisterOverflow (ObjDesc->IndexField.IndexObj,
525                (UINT64) ObjDesc->IndexField.Value))
526        {
527            return_ACPI_STATUS (AE_AML_REGISTER_LIMIT);
528        }
529
530        /* Write the index value to the IndexRegister (itself a RegionField) */
531
532        FieldDatumByteOffset += ObjDesc->IndexField.Value;
533
534        ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
535            "Write to Index Register: Value %8.8X\n",
536            FieldDatumByteOffset));
537
538        Status = AcpiExInsertIntoField (ObjDesc->IndexField.IndexObj,
539                    &FieldDatumByteOffset,
540                    sizeof (FieldDatumByteOffset));
541        if (ACPI_FAILURE (Status))
542        {
543            return_ACPI_STATUS (Status);
544        }
545
546        if (ReadWrite == ACPI_READ)
547        {
548            /* Read the datum from the DataRegister */
549
550            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
551                "Read from Data Register\n"));
552
553            Status = AcpiExExtractFromField (ObjDesc->IndexField.DataObj,
554                        Value, sizeof (UINT64));
555        }
556        else
557        {
558            /* Write the datum to the DataRegister */
559
560            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
561                "Write to Data Register: Value %8.8X%8.8X\n",
562                ACPI_FORMAT_UINT64 (*Value)));
563
564            Status = AcpiExInsertIntoField (ObjDesc->IndexField.DataObj,
565                        Value, sizeof (UINT64));
566        }
567        break;
568
569    default:
570
571        ACPI_ERROR ((AE_INFO, "Wrong object type in field I/O %u",
572            ObjDesc->Common.Type));
573        Status = AE_AML_INTERNAL;
574        break;
575    }
576
577    if (ACPI_SUCCESS (Status))
578    {
579        if (ReadWrite == ACPI_READ)
580        {
581            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
582                "Value Read %8.8X%8.8X, Width %u\n",
583                ACPI_FORMAT_UINT64 (*Value),
584                ObjDesc->CommonField.AccessByteWidth));
585        }
586        else
587        {
588            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
589                "Value Written %8.8X%8.8X, Width %u\n",
590                ACPI_FORMAT_UINT64 (*Value),
591                ObjDesc->CommonField.AccessByteWidth));
592        }
593    }
594
595    return_ACPI_STATUS (Status);
596}
597
598
599/*******************************************************************************
600 *
601 * FUNCTION:    AcpiExWriteWithUpdateRule
602 *
603 * PARAMETERS:  ObjDesc                 - Field to be written
604 *              Mask                    - bitmask within field datum
605 *              FieldValue              - Value to write
606 *              FieldDatumByteOffset    - Offset of datum within field
607 *
608 * RETURN:      Status
609 *
610 * DESCRIPTION: Apply the field update rule to a field write
611 *
612 ******************************************************************************/
613
614ACPI_STATUS
615AcpiExWriteWithUpdateRule (
616    ACPI_OPERAND_OBJECT     *ObjDesc,
617    UINT64                  Mask,
618    UINT64                  FieldValue,
619    UINT32                  FieldDatumByteOffset)
620{
621    ACPI_STATUS             Status = AE_OK;
622    UINT64                  MergedValue;
623    UINT64                  CurrentValue;
624
625
626    ACPI_FUNCTION_TRACE_U32 (ExWriteWithUpdateRule, Mask);
627
628
629    /* Start with the new bits  */
630
631    MergedValue = FieldValue;
632
633    /* If the mask is all ones, we don't need to worry about the update rule */
634
635    if (Mask != ACPI_UINT64_MAX)
636    {
637        /* Decode the update rule */
638
639        switch (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)
640        {
641        case AML_FIELD_UPDATE_PRESERVE:
642            /*
643             * Check if update rule needs to be applied (not if mask is all
644             * ones)  The left shift drops the bits we want to ignore.
645             */
646            if ((~Mask << (ACPI_MUL_8 (sizeof (Mask)) -
647                           ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth))) != 0)
648            {
649                /*
650                 * Read the current contents of the byte/word/dword containing
651                 * the field, and merge with the new field value.
652                 */
653                Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
654                            &CurrentValue, ACPI_READ);
655                if (ACPI_FAILURE (Status))
656                {
657                    return_ACPI_STATUS (Status);
658                }
659
660                MergedValue |= (CurrentValue & ~Mask);
661            }
662            break;
663
664        case AML_FIELD_UPDATE_WRITE_AS_ONES:
665
666            /* Set positions outside the field to all ones */
667
668            MergedValue |= ~Mask;
669            break;
670
671        case AML_FIELD_UPDATE_WRITE_AS_ZEROS:
672
673            /* Set positions outside the field to all zeros */
674
675            MergedValue &= Mask;
676            break;
677
678        default:
679
680            ACPI_ERROR ((AE_INFO,
681                "Unknown UpdateRule value: 0x%X",
682                (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)));
683            return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
684        }
685    }
686
687    ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
688        "Mask %8.8X%8.8X, DatumOffset %X, Width %X, Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
689        ACPI_FORMAT_UINT64 (Mask),
690        FieldDatumByteOffset,
691        ObjDesc->CommonField.AccessByteWidth,
692        ACPI_FORMAT_UINT64 (FieldValue),
693        ACPI_FORMAT_UINT64 (MergedValue)));
694
695    /* Write the merged value */
696
697    Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
698                &MergedValue, ACPI_WRITE);
699
700    return_ACPI_STATUS (Status);
701}
702
703
704/*******************************************************************************
705 *
706 * FUNCTION:    AcpiExExtractFromField
707 *
708 * PARAMETERS:  ObjDesc             - Field to be read
709 *              Buffer              - Where to store the field data
710 *              BufferLength        - Length of Buffer
711 *
712 * RETURN:      Status
713 *
714 * DESCRIPTION: Retrieve the current value of the given field
715 *
716 ******************************************************************************/
717
718ACPI_STATUS
719AcpiExExtractFromField (
720    ACPI_OPERAND_OBJECT     *ObjDesc,
721    void                    *Buffer,
722    UINT32                  BufferLength)
723{
724    ACPI_STATUS             Status;
725    UINT64                  RawDatum;
726    UINT64                  MergedDatum;
727    UINT32                  FieldOffset = 0;
728    UINT32                  BufferOffset = 0;
729    UINT32                  BufferTailBits;
730    UINT32                  DatumCount;
731    UINT32                  FieldDatumCount;
732    UINT32                  AccessBitWidth;
733    UINT32                  i;
734
735
736    ACPI_FUNCTION_TRACE (ExExtractFromField);
737
738
739    /* Validate target buffer and clear it */
740
741    if (BufferLength <
742        ACPI_ROUND_BITS_UP_TO_BYTES (ObjDesc->CommonField.BitLength))
743    {
744        ACPI_ERROR ((AE_INFO,
745            "Field size %u (bits) is too large for buffer (%u)",
746            ObjDesc->CommonField.BitLength, BufferLength));
747
748        return_ACPI_STATUS (AE_BUFFER_OVERFLOW);
749    }
750
751    ACPI_MEMSET (Buffer, 0, BufferLength);
752    AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
753
754    /* Handle the simple case here */
755
756    if ((ObjDesc->CommonField.StartFieldBitOffset == 0) &&
757        (ObjDesc->CommonField.BitLength == AccessBitWidth))
758    {
759        if (BufferLength >= sizeof (UINT64))
760        {
761            Status = AcpiExFieldDatumIo (ObjDesc, 0, Buffer, ACPI_READ);
762        }
763        else
764        {
765            /* Use RawDatum (UINT64) to handle buffers < 64 bits */
766
767            Status = AcpiExFieldDatumIo (ObjDesc, 0, &RawDatum, ACPI_READ);
768            ACPI_MEMCPY (Buffer, &RawDatum, BufferLength);
769        }
770
771        return_ACPI_STATUS (Status);
772    }
773
774/* TBD: Move to common setup code */
775
776    /* Field algorithm is limited to sizeof(UINT64), truncate if needed */
777
778    if (ObjDesc->CommonField.AccessByteWidth > sizeof (UINT64))
779    {
780        ObjDesc->CommonField.AccessByteWidth = sizeof (UINT64);
781        AccessBitWidth = sizeof (UINT64) * 8;
782    }
783
784    /* Compute the number of datums (access width data items) */
785
786    DatumCount = ACPI_ROUND_UP_TO (
787        ObjDesc->CommonField.BitLength, AccessBitWidth);
788
789    FieldDatumCount = ACPI_ROUND_UP_TO (
790        ObjDesc->CommonField.BitLength +
791        ObjDesc->CommonField.StartFieldBitOffset, AccessBitWidth);
792
793    /* Priming read from the field */
794
795    Status = AcpiExFieldDatumIo (ObjDesc, FieldOffset, &RawDatum, ACPI_READ);
796    if (ACPI_FAILURE (Status))
797    {
798        return_ACPI_STATUS (Status);
799    }
800    MergedDatum = RawDatum >> ObjDesc->CommonField.StartFieldBitOffset;
801
802    /* Read the rest of the field */
803
804    for (i = 1; i < FieldDatumCount; i++)
805    {
806        /* Get next input datum from the field */
807
808        FieldOffset += ObjDesc->CommonField.AccessByteWidth;
809        Status = AcpiExFieldDatumIo (ObjDesc, FieldOffset,
810                    &RawDatum, ACPI_READ);
811        if (ACPI_FAILURE (Status))
812        {
813            return_ACPI_STATUS (Status);
814        }
815
816        /*
817         * Merge with previous datum if necessary.
818         *
819         * Note: Before the shift, check if the shift value will be larger than
820         * the integer size. If so, there is no need to perform the operation.
821         * This avoids the differences in behavior between different compilers
822         * concerning shift values larger than the target data width.
823         */
824        if (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset <
825            ACPI_INTEGER_BIT_SIZE)
826        {
827            MergedDatum |= RawDatum <<
828                (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset);
829        }
830
831        if (i == DatumCount)
832        {
833            break;
834        }
835
836        /* Write merged datum to target buffer */
837
838        ACPI_MEMCPY (((char *) Buffer) + BufferOffset, &MergedDatum,
839            ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
840                BufferLength - BufferOffset));
841
842        BufferOffset += ObjDesc->CommonField.AccessByteWidth;
843        MergedDatum = RawDatum >> ObjDesc->CommonField.StartFieldBitOffset;
844    }
845
846    /* Mask off any extra bits in the last datum */
847
848    BufferTailBits = ObjDesc->CommonField.BitLength % AccessBitWidth;
849    if (BufferTailBits)
850    {
851        MergedDatum &= ACPI_MASK_BITS_ABOVE (BufferTailBits);
852    }
853
854    /* Write the last datum to the buffer */
855
856    ACPI_MEMCPY (((char *) Buffer) + BufferOffset, &MergedDatum,
857        ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
858            BufferLength - BufferOffset));
859
860    return_ACPI_STATUS (AE_OK);
861}
862
863
864/*******************************************************************************
865 *
866 * FUNCTION:    AcpiExInsertIntoField
867 *
868 * PARAMETERS:  ObjDesc             - Field to be written
869 *              Buffer              - Data to be written
870 *              BufferLength        - Length of Buffer
871 *
872 * RETURN:      Status
873 *
874 * DESCRIPTION: Store the Buffer contents into the given field
875 *
876 ******************************************************************************/
877
878ACPI_STATUS
879AcpiExInsertIntoField (
880    ACPI_OPERAND_OBJECT     *ObjDesc,
881    void                    *Buffer,
882    UINT32                  BufferLength)
883{
884    void                    *NewBuffer;
885    ACPI_STATUS             Status;
886    UINT64                  Mask;
887    UINT64                  WidthMask;
888    UINT64                  MergedDatum;
889    UINT64                  RawDatum = 0;
890    UINT32                  FieldOffset = 0;
891    UINT32                  BufferOffset = 0;
892    UINT32                  BufferTailBits;
893    UINT32                  DatumCount;
894    UINT32                  FieldDatumCount;
895    UINT32                  AccessBitWidth;
896    UINT32                  RequiredLength;
897    UINT32                  i;
898
899
900    ACPI_FUNCTION_TRACE (ExInsertIntoField);
901
902
903    /* Validate input buffer */
904
905    NewBuffer = NULL;
906    RequiredLength = ACPI_ROUND_BITS_UP_TO_BYTES (
907                        ObjDesc->CommonField.BitLength);
908    /*
909     * We must have a buffer that is at least as long as the field
910     * we are writing to. This is because individual fields are
911     * indivisible and partial writes are not supported -- as per
912     * the ACPI specification.
913     */
914    if (BufferLength < RequiredLength)
915    {
916        /* We need to create a new buffer */
917
918        NewBuffer = ACPI_ALLOCATE_ZEROED (RequiredLength);
919        if (!NewBuffer)
920        {
921            return_ACPI_STATUS (AE_NO_MEMORY);
922        }
923
924        /*
925         * Copy the original data to the new buffer, starting
926         * at Byte zero. All unused (upper) bytes of the
927         * buffer will be 0.
928         */
929        ACPI_MEMCPY ((char *) NewBuffer, (char *) Buffer, BufferLength);
930        Buffer = NewBuffer;
931        BufferLength = RequiredLength;
932    }
933
934/* TBD: Move to common setup code */
935
936    /* Algo is limited to sizeof(UINT64), so cut the AccessByteWidth */
937    if (ObjDesc->CommonField.AccessByteWidth > sizeof (UINT64))
938    {
939        ObjDesc->CommonField.AccessByteWidth = sizeof (UINT64);
940    }
941
942    AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
943
944    /*
945     * Create the bitmasks used for bit insertion.
946     * Note: This if/else is used to bypass compiler differences with the
947     * shift operator
948     */
949    if (AccessBitWidth == ACPI_INTEGER_BIT_SIZE)
950    {
951        WidthMask = ACPI_UINT64_MAX;
952    }
953    else
954    {
955        WidthMask = ACPI_MASK_BITS_ABOVE (AccessBitWidth);
956    }
957
958    Mask = WidthMask &
959        ACPI_MASK_BITS_BELOW (ObjDesc->CommonField.StartFieldBitOffset);
960
961    /* Compute the number of datums (access width data items) */
962
963    DatumCount = ACPI_ROUND_UP_TO (ObjDesc->CommonField.BitLength,
964        AccessBitWidth);
965
966    FieldDatumCount = ACPI_ROUND_UP_TO (ObjDesc->CommonField.BitLength +
967        ObjDesc->CommonField.StartFieldBitOffset,
968        AccessBitWidth);
969
970    /* Get initial Datum from the input buffer */
971
972    ACPI_MEMCPY (&RawDatum, Buffer,
973        ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
974            BufferLength - BufferOffset));
975
976    MergedDatum = RawDatum << ObjDesc->CommonField.StartFieldBitOffset;
977
978    /* Write the entire field */
979
980    for (i = 1; i < FieldDatumCount; i++)
981    {
982        /* Write merged datum to the target field */
983
984        MergedDatum &= Mask;
985        Status = AcpiExWriteWithUpdateRule (ObjDesc, Mask,
986                    MergedDatum, FieldOffset);
987        if (ACPI_FAILURE (Status))
988        {
989            goto Exit;
990        }
991
992        FieldOffset += ObjDesc->CommonField.AccessByteWidth;
993
994        /*
995         * Start new output datum by merging with previous input datum
996         * if necessary.
997         *
998         * Note: Before the shift, check if the shift value will be larger than
999         * the integer size. If so, there is no need to perform the operation.
1000         * This avoids the differences in behavior between different compilers
1001         * concerning shift values larger than the target data width.
1002         */
1003        if ((AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset) <
1004            ACPI_INTEGER_BIT_SIZE)
1005        {
1006            MergedDatum = RawDatum >>
1007                (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset);
1008        }
1009        else
1010        {
1011            MergedDatum = 0;
1012        }
1013
1014        Mask = WidthMask;
1015
1016        if (i == DatumCount)
1017        {
1018            break;
1019        }
1020
1021        /* Get the next input datum from the buffer */
1022
1023        BufferOffset += ObjDesc->CommonField.AccessByteWidth;
1024        ACPI_MEMCPY (&RawDatum, ((char *) Buffer) + BufferOffset,
1025            ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
1026                 BufferLength - BufferOffset));
1027
1028        MergedDatum |= RawDatum << ObjDesc->CommonField.StartFieldBitOffset;
1029    }
1030
1031    /* Mask off any extra bits in the last datum */
1032
1033    BufferTailBits = (ObjDesc->CommonField.BitLength +
1034        ObjDesc->CommonField.StartFieldBitOffset) % AccessBitWidth;
1035    if (BufferTailBits)
1036    {
1037        Mask &= ACPI_MASK_BITS_ABOVE (BufferTailBits);
1038    }
1039
1040    /* Write the last datum to the field */
1041
1042    MergedDatum &= Mask;
1043    Status = AcpiExWriteWithUpdateRule (ObjDesc,
1044                Mask, MergedDatum, FieldOffset);
1045
1046Exit:
1047    /* Free temporary buffer if we used one */
1048
1049    if (NewBuffer)
1050    {
1051        ACPI_FREE (NewBuffer);
1052    }
1053    return_ACPI_STATUS (Status);
1054}
1055