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