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