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    case ACPI_TYPE_LOCAL_BANK_FIELD:
484        /*
485         * Ensure that the BankValue is not beyond the capacity of
486         * the register
487         */
488        if (AcpiExRegisterOverflow (ObjDesc->BankField.BankObj,
489                (UINT64) ObjDesc->BankField.Value))
490        {
491            return_ACPI_STATUS (AE_AML_REGISTER_LIMIT);
492        }
493
494        /*
495         * For BankFields, we must write the BankValue to the BankRegister
496         * (itself a RegionField) before we can access the data.
497         */
498        Status = AcpiExInsertIntoField (ObjDesc->BankField.BankObj,
499                    &ObjDesc->BankField.Value,
500                    sizeof (ObjDesc->BankField.Value));
501        if (ACPI_FAILURE (Status))
502        {
503            return_ACPI_STATUS (Status);
504        }
505
506        /*
507         * Now that the Bank has been selected, fall through to the
508         * RegionField case and write the datum to the Operation Region
509         */
510
511        /*lint -fallthrough */
512
513    case ACPI_TYPE_LOCAL_REGION_FIELD:
514        /*
515         * For simple RegionFields, we just directly access the owning
516         * Operation Region.
517         */
518        Status = AcpiExAccessRegion (ObjDesc, FieldDatumByteOffset, Value,
519                    ReadWrite);
520        break;
521
522    case ACPI_TYPE_LOCAL_INDEX_FIELD:
523        /*
524         * Ensure that the IndexValue is not beyond the capacity of
525         * the register
526         */
527        if (AcpiExRegisterOverflow (ObjDesc->IndexField.IndexObj,
528                (UINT64) ObjDesc->IndexField.Value))
529        {
530            return_ACPI_STATUS (AE_AML_REGISTER_LIMIT);
531        }
532
533        /* Write the index value to the IndexRegister (itself a RegionField) */
534
535        FieldDatumByteOffset += ObjDesc->IndexField.Value;
536
537        ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
538            "Write to Index Register: Value %8.8X\n",
539            FieldDatumByteOffset));
540
541        Status = AcpiExInsertIntoField (ObjDesc->IndexField.IndexObj,
542                    &FieldDatumByteOffset,
543                    sizeof (FieldDatumByteOffset));
544        if (ACPI_FAILURE (Status))
545        {
546            return_ACPI_STATUS (Status);
547        }
548
549        if (ReadWrite == ACPI_READ)
550        {
551            /* Read the datum from the DataRegister */
552
553            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
554                "Read from Data Register\n"));
555
556            Status = AcpiExExtractFromField (ObjDesc->IndexField.DataObj,
557                        Value, sizeof (UINT64));
558        }
559        else
560        {
561            /* Write the datum to the DataRegister */
562
563            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
564                "Write to Data Register: Value %8.8X%8.8X\n",
565                ACPI_FORMAT_UINT64 (*Value)));
566
567            Status = AcpiExInsertIntoField (ObjDesc->IndexField.DataObj,
568                        Value, sizeof (UINT64));
569        }
570        break;
571
572    default:
573
574        ACPI_ERROR ((AE_INFO, "Wrong object type in field I/O %u",
575            ObjDesc->Common.Type));
576        Status = AE_AML_INTERNAL;
577        break;
578    }
579
580    if (ACPI_SUCCESS (Status))
581    {
582        if (ReadWrite == ACPI_READ)
583        {
584            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
585                "Value Read %8.8X%8.8X, Width %u\n",
586                ACPI_FORMAT_UINT64 (*Value),
587                ObjDesc->CommonField.AccessByteWidth));
588        }
589        else
590        {
591            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
592                "Value Written %8.8X%8.8X, Width %u\n",
593                ACPI_FORMAT_UINT64 (*Value),
594                ObjDesc->CommonField.AccessByteWidth));
595        }
596    }
597
598    return_ACPI_STATUS (Status);
599}
600
601
602/*******************************************************************************
603 *
604 * FUNCTION:    AcpiExWriteWithUpdateRule
605 *
606 * PARAMETERS:  ObjDesc                 - Field to be written
607 *              Mask                    - bitmask within field datum
608 *              FieldValue              - Value to write
609 *              FieldDatumByteOffset    - Offset of datum within field
610 *
611 * RETURN:      Status
612 *
613 * DESCRIPTION: Apply the field update rule to a field write
614 *
615 ******************************************************************************/
616
617ACPI_STATUS
618AcpiExWriteWithUpdateRule (
619    ACPI_OPERAND_OBJECT     *ObjDesc,
620    UINT64                  Mask,
621    UINT64                  FieldValue,
622    UINT32                  FieldDatumByteOffset)
623{
624    ACPI_STATUS             Status = AE_OK;
625    UINT64                  MergedValue;
626    UINT64                  CurrentValue;
627
628
629    ACPI_FUNCTION_TRACE_U32 (ExWriteWithUpdateRule, Mask);
630
631
632    /* Start with the new bits  */
633
634    MergedValue = FieldValue;
635
636    /* If the mask is all ones, we don't need to worry about the update rule */
637
638    if (Mask != ACPI_UINT64_MAX)
639    {
640        /* Decode the update rule */
641
642        switch (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)
643        {
644        case AML_FIELD_UPDATE_PRESERVE:
645            /*
646             * Check if update rule needs to be applied (not if mask is all
647             * ones)  The left shift drops the bits we want to ignore.
648             */
649            if ((~Mask << (ACPI_MUL_8 (sizeof (Mask)) -
650                           ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth))) != 0)
651            {
652                /*
653                 * Read the current contents of the byte/word/dword containing
654                 * the field, and merge with the new field value.
655                 */
656                Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
657                            &CurrentValue, ACPI_READ);
658                if (ACPI_FAILURE (Status))
659                {
660                    return_ACPI_STATUS (Status);
661                }
662
663                MergedValue |= (CurrentValue & ~Mask);
664            }
665            break;
666
667        case AML_FIELD_UPDATE_WRITE_AS_ONES:
668
669            /* Set positions outside the field to all ones */
670
671            MergedValue |= ~Mask;
672            break;
673
674        case AML_FIELD_UPDATE_WRITE_AS_ZEROS:
675
676            /* Set positions outside the field to all zeros */
677
678            MergedValue &= Mask;
679            break;
680
681        default:
682
683            ACPI_ERROR ((AE_INFO,
684                "Unknown UpdateRule value: 0x%X",
685                (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)));
686            return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
687        }
688    }
689
690    ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
691        "Mask %8.8X%8.8X, DatumOffset %X, Width %X, Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
692        ACPI_FORMAT_UINT64 (Mask),
693        FieldDatumByteOffset,
694        ObjDesc->CommonField.AccessByteWidth,
695        ACPI_FORMAT_UINT64 (FieldValue),
696        ACPI_FORMAT_UINT64 (MergedValue)));
697
698    /* Write the merged value */
699
700    Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
701                &MergedValue, ACPI_WRITE);
702
703    return_ACPI_STATUS (Status);
704}
705
706
707/*******************************************************************************
708 *
709 * FUNCTION:    AcpiExExtractFromField
710 *
711 * PARAMETERS:  ObjDesc             - Field to be read
712 *              Buffer              - Where to store the field data
713 *              BufferLength        - Length of Buffer
714 *
715 * RETURN:      Status
716 *
717 * DESCRIPTION: Retrieve the current value of the given field
718 *
719 ******************************************************************************/
720
721ACPI_STATUS
722AcpiExExtractFromField (
723    ACPI_OPERAND_OBJECT     *ObjDesc,
724    void                    *Buffer,
725    UINT32                  BufferLength)
726{
727    ACPI_STATUS             Status;
728    UINT64                  RawDatum;
729    UINT64                  MergedDatum;
730    UINT32                  FieldOffset = 0;
731    UINT32                  BufferOffset = 0;
732    UINT32                  BufferTailBits;
733    UINT32                  DatumCount;
734    UINT32                  FieldDatumCount;
735    UINT32                  AccessBitWidth;
736    UINT32                  i;
737
738
739    ACPI_FUNCTION_TRACE (ExExtractFromField);
740
741
742    /* Validate target buffer and clear it */
743
744    if (BufferLength <
745        ACPI_ROUND_BITS_UP_TO_BYTES (ObjDesc->CommonField.BitLength))
746    {
747        ACPI_ERROR ((AE_INFO,
748            "Field size %u (bits) is too large for buffer (%u)",
749            ObjDesc->CommonField.BitLength, BufferLength));
750
751        return_ACPI_STATUS (AE_BUFFER_OVERFLOW);
752    }
753
754    ACPI_MEMSET (Buffer, 0, BufferLength);
755    AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
756
757    /* Handle the simple case here */
758
759    if ((ObjDesc->CommonField.StartFieldBitOffset == 0) &&
760        (ObjDesc->CommonField.BitLength == AccessBitWidth))
761    {
762        if (BufferLength >= sizeof (UINT64))
763        {
764            Status = AcpiExFieldDatumIo (ObjDesc, 0, Buffer, ACPI_READ);
765        }
766        else
767        {
768            /* Use RawDatum (UINT64) to handle buffers < 64 bits */
769
770            Status = AcpiExFieldDatumIo (ObjDesc, 0, &RawDatum, ACPI_READ);
771            ACPI_MEMCPY (Buffer, &RawDatum, BufferLength);
772        }
773
774        return_ACPI_STATUS (Status);
775    }
776
777/* TBD: Move to common setup code */
778
779    /* Field algorithm is limited to sizeof(UINT64), truncate if needed */
780
781    if (ObjDesc->CommonField.AccessByteWidth > sizeof (UINT64))
782    {
783        ObjDesc->CommonField.AccessByteWidth = sizeof (UINT64);
784        AccessBitWidth = sizeof (UINT64) * 8;
785    }
786
787    /* Compute the number of datums (access width data items) */
788
789    DatumCount = ACPI_ROUND_UP_TO (
790        ObjDesc->CommonField.BitLength, AccessBitWidth);
791
792    FieldDatumCount = ACPI_ROUND_UP_TO (
793        ObjDesc->CommonField.BitLength +
794        ObjDesc->CommonField.StartFieldBitOffset, AccessBitWidth);
795
796    /* Priming read from the field */
797
798    Status = AcpiExFieldDatumIo (ObjDesc, FieldOffset, &RawDatum, ACPI_READ);
799    if (ACPI_FAILURE (Status))
800    {
801        return_ACPI_STATUS (Status);
802    }
803    MergedDatum = RawDatum >> ObjDesc->CommonField.StartFieldBitOffset;
804
805    /* Read the rest of the field */
806
807    for (i = 1; i < FieldDatumCount; i++)
808    {
809        /* Get next input datum from the field */
810
811        FieldOffset += ObjDesc->CommonField.AccessByteWidth;
812        Status = AcpiExFieldDatumIo (ObjDesc, FieldOffset,
813                    &RawDatum, ACPI_READ);
814        if (ACPI_FAILURE (Status))
815        {
816            return_ACPI_STATUS (Status);
817        }
818
819        /*
820         * Merge with previous datum if necessary.
821         *
822         * Note: Before the shift, check if the shift value will be larger than
823         * the integer size. If so, there is no need to perform the operation.
824         * This avoids the differences in behavior between different compilers
825         * concerning shift values larger than the target data width.
826         */
827        if (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset <
828            ACPI_INTEGER_BIT_SIZE)
829        {
830            MergedDatum |= RawDatum <<
831                (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset);
832        }
833
834        if (i == DatumCount)
835        {
836            break;
837        }
838
839        /* Write merged datum to target buffer */
840
841        ACPI_MEMCPY (((char *) Buffer) + BufferOffset, &MergedDatum,
842            ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
843                BufferLength - BufferOffset));
844
845        BufferOffset += ObjDesc->CommonField.AccessByteWidth;
846        MergedDatum = RawDatum >> ObjDesc->CommonField.StartFieldBitOffset;
847    }
848
849    /* Mask off any extra bits in the last datum */
850
851    BufferTailBits = ObjDesc->CommonField.BitLength % AccessBitWidth;
852    if (BufferTailBits)
853    {
854        MergedDatum &= ACPI_MASK_BITS_ABOVE (BufferTailBits);
855    }
856
857    /* Write the last datum to the buffer */
858
859    ACPI_MEMCPY (((char *) Buffer) + BufferOffset, &MergedDatum,
860        ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
861            BufferLength - BufferOffset));
862
863    return_ACPI_STATUS (AE_OK);
864}
865
866
867/*******************************************************************************
868 *
869 * FUNCTION:    AcpiExInsertIntoField
870 *
871 * PARAMETERS:  ObjDesc             - Field to be written
872 *              Buffer              - Data to be written
873 *              BufferLength        - Length of Buffer
874 *
875 * RETURN:      Status
876 *
877 * DESCRIPTION: Store the Buffer contents into the given field
878 *
879 ******************************************************************************/
880
881ACPI_STATUS
882AcpiExInsertIntoField (
883    ACPI_OPERAND_OBJECT     *ObjDesc,
884    void                    *Buffer,
885    UINT32                  BufferLength)
886{
887    void                    *NewBuffer;
888    ACPI_STATUS             Status;
889    UINT64                  Mask;
890    UINT64                  WidthMask;
891    UINT64                  MergedDatum;
892    UINT64                  RawDatum = 0;
893    UINT32                  FieldOffset = 0;
894    UINT32                  BufferOffset = 0;
895    UINT32                  BufferTailBits;
896    UINT32                  DatumCount;
897    UINT32                  FieldDatumCount;
898    UINT32                  AccessBitWidth;
899    UINT32                  RequiredLength;
900    UINT32                  i;
901
902
903    ACPI_FUNCTION_TRACE (ExInsertIntoField);
904
905
906    /* Validate input buffer */
907
908    NewBuffer = NULL;
909    RequiredLength = ACPI_ROUND_BITS_UP_TO_BYTES (
910                        ObjDesc->CommonField.BitLength);
911    /*
912     * We must have a buffer that is at least as long as the field
913     * we are writing to. This is because individual fields are
914     * indivisible and partial writes are not supported -- as per
915     * the ACPI specification.
916     */
917    if (BufferLength < RequiredLength)
918    {
919        /* We need to create a new buffer */
920
921        NewBuffer = ACPI_ALLOCATE_ZEROED (RequiredLength);
922        if (!NewBuffer)
923        {
924            return_ACPI_STATUS (AE_NO_MEMORY);
925        }
926
927        /*
928         * Copy the original data to the new buffer, starting
929         * at Byte zero. All unused (upper) bytes of the
930         * buffer will be 0.
931         */
932        ACPI_MEMCPY ((char *) NewBuffer, (char *) Buffer, BufferLength);
933        Buffer = NewBuffer;
934        BufferLength = RequiredLength;
935    }
936
937/* TBD: Move to common setup code */
938
939    /* Algo is limited to sizeof(UINT64), so cut the AccessByteWidth */
940    if (ObjDesc->CommonField.AccessByteWidth > sizeof (UINT64))
941    {
942        ObjDesc->CommonField.AccessByteWidth = sizeof (UINT64);
943    }
944
945    AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
946
947    /*
948     * Create the bitmasks used for bit insertion.
949     * Note: This if/else is used to bypass compiler differences with the
950     * shift operator
951     */
952    if (AccessBitWidth == ACPI_INTEGER_BIT_SIZE)
953    {
954        WidthMask = ACPI_UINT64_MAX;
955    }
956    else
957    {
958        WidthMask = ACPI_MASK_BITS_ABOVE (AccessBitWidth);
959    }
960
961    Mask = WidthMask &
962        ACPI_MASK_BITS_BELOW (ObjDesc->CommonField.StartFieldBitOffset);
963
964    /* Compute the number of datums (access width data items) */
965
966    DatumCount = ACPI_ROUND_UP_TO (ObjDesc->CommonField.BitLength,
967        AccessBitWidth);
968
969    FieldDatumCount = ACPI_ROUND_UP_TO (ObjDesc->CommonField.BitLength +
970        ObjDesc->CommonField.StartFieldBitOffset,
971        AccessBitWidth);
972
973    /* Get initial Datum from the input buffer */
974
975    ACPI_MEMCPY (&RawDatum, Buffer,
976        ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
977            BufferLength - BufferOffset));
978
979    MergedDatum = RawDatum << ObjDesc->CommonField.StartFieldBitOffset;
980
981    /* Write the entire field */
982
983    for (i = 1; i < FieldDatumCount; i++)
984    {
985        /* Write merged datum to the target field */
986
987        MergedDatum &= Mask;
988        Status = AcpiExWriteWithUpdateRule (ObjDesc, Mask,
989                    MergedDatum, FieldOffset);
990        if (ACPI_FAILURE (Status))
991        {
992            goto Exit;
993        }
994
995        FieldOffset += ObjDesc->CommonField.AccessByteWidth;
996
997        /*
998         * Start new output datum by merging with previous input datum
999         * if necessary.
1000         *
1001         * Note: Before the shift, check if the shift value will be larger than
1002         * the integer size. If so, there is no need to perform the operation.
1003         * This avoids the differences in behavior between different compilers
1004         * concerning shift values larger than the target data width.
1005         */
1006        if ((AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset) <
1007            ACPI_INTEGER_BIT_SIZE)
1008        {
1009            MergedDatum = RawDatum >>
1010                (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset);
1011        }
1012        else
1013        {
1014            MergedDatum = 0;
1015        }
1016
1017        Mask = WidthMask;
1018
1019        if (i == DatumCount)
1020        {
1021            break;
1022        }
1023
1024        /* Get the next input datum from the buffer */
1025
1026        BufferOffset += ObjDesc->CommonField.AccessByteWidth;
1027        ACPI_MEMCPY (&RawDatum, ((char *) Buffer) + BufferOffset,
1028            ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
1029                 BufferLength - BufferOffset));
1030
1031        MergedDatum |= RawDatum << ObjDesc->CommonField.StartFieldBitOffset;
1032    }
1033
1034    /* Mask off any extra bits in the last datum */
1035
1036    BufferTailBits = (ObjDesc->CommonField.BitLength +
1037        ObjDesc->CommonField.StartFieldBitOffset) % AccessBitWidth;
1038    if (BufferTailBits)
1039    {
1040        Mask &= ACPI_MASK_BITS_ABOVE (BufferTailBits);
1041    }
1042
1043    /* Write the last datum to the field */
1044
1045    MergedDatum &= Mask;
1046    Status = AcpiExWriteWithUpdateRule (ObjDesc,
1047                Mask, MergedDatum, FieldOffset);
1048
1049Exit:
1050    /* Free temporary buffer if we used one */
1051
1052    if (NewBuffer)
1053    {
1054        ACPI_FREE (NewBuffer);
1055    }
1056    return_ACPI_STATUS (Status);
1057}
1058