xref: /freebsd/sys/contrib/dev/acpica/components/executer/exfldio.c (revision 273c26a3c3bea87a241d6879abd4f991db180bf0)
1 /******************************************************************************
2  *
3  * Module Name: exfldio - Aml Field I/O
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2016, Intel Corp.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions, and the following disclaimer,
16  *    without modification.
17  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18  *    substantially similar to the "NO WARRANTY" disclaimer below
19  *    ("Disclaimer") and any redistribution must be conditioned upon
20  *    including a substantially similar Disclaimer requirement for further
21  *    binary redistribution.
22  * 3. Neither the names of the above-listed copyright holders nor the names
23  *    of any contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * Alternatively, this software may be distributed under the terms of the
27  * GNU General Public License ("GPL") version 2 as published by the Free
28  * Software Foundation.
29  *
30  * NO WARRANTY
31  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41  * POSSIBILITY OF SUCH DAMAGES.
42  */
43 
44 #include <contrib/dev/acpica/include/acpi.h>
45 #include <contrib/dev/acpica/include/accommon.h>
46 #include <contrib/dev/acpica/include/acinterp.h>
47 #include <contrib/dev/acpica/include/amlcode.h>
48 #include <contrib/dev/acpica/include/acevents.h>
49 #include <contrib/dev/acpica/include/acdispat.h>
50 
51 
52 #define _COMPONENT          ACPI_EXECUTER
53         ACPI_MODULE_NAME    ("exfldio")
54 
55 /* Local prototypes */
56 
57 static ACPI_STATUS
58 AcpiExFieldDatumIo (
59     ACPI_OPERAND_OBJECT     *ObjDesc,
60     UINT32                  FieldDatumByteOffset,
61     UINT64                  *Value,
62     UINT32                  ReadWrite);
63 
64 static BOOLEAN
65 AcpiExRegisterOverflow (
66     ACPI_OPERAND_OBJECT     *ObjDesc,
67     UINT64                  Value);
68 
69 static ACPI_STATUS
70 AcpiExSetupRegion (
71     ACPI_OPERAND_OBJECT     *ObjDesc,
72     UINT32                  FieldDatumByteOffset);
73 
74 
75 /*******************************************************************************
76  *
77  * FUNCTION:    AcpiExSetupRegion
78  *
79  * PARAMETERS:  ObjDesc                 - Field to be read or written
80  *              FieldDatumByteOffset    - Byte offset of this datum within the
81  *                                        parent field
82  *
83  * RETURN:      Status
84  *
85  * DESCRIPTION: Common processing for AcpiExExtractFromField and
86  *              AcpiExInsertIntoField. Initialize the Region if necessary and
87  *              validate the request.
88  *
89  ******************************************************************************/
90 
91 static ACPI_STATUS
92 AcpiExSetupRegion (
93     ACPI_OPERAND_OBJECT     *ObjDesc,
94     UINT32                  FieldDatumByteOffset)
95 {
96     ACPI_STATUS             Status = AE_OK;
97     ACPI_OPERAND_OBJECT     *RgnDesc;
98     UINT8                   SpaceId;
99 
100 
101     ACPI_FUNCTION_TRACE_U32 (ExSetupRegion, FieldDatumByteOffset);
102 
103 
104     RgnDesc = ObjDesc->CommonField.RegionObj;
105 
106     /* We must have a valid region */
107 
108     if (RgnDesc->Common.Type != ACPI_TYPE_REGION)
109     {
110         ACPI_ERROR ((AE_INFO, "Needed Region, found type 0x%X (%s)",
111             RgnDesc->Common.Type,
112             AcpiUtGetObjectTypeName (RgnDesc)));
113 
114         return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
115     }
116 
117     SpaceId = RgnDesc->Region.SpaceId;
118 
119     /* Validate the Space ID */
120 
121     if (!AcpiIsValidSpaceId (SpaceId))
122     {
123         ACPI_ERROR ((AE_INFO,
124             "Invalid/unknown Address Space ID: 0x%2.2X", SpaceId));
125         return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID);
126     }
127 
128     /*
129      * If the Region Address and Length have not been previously evaluated,
130      * evaluate them now and save the results.
131      */
132     if (!(RgnDesc->Common.Flags & AOPOBJ_DATA_VALID))
133     {
134         Status = AcpiDsGetRegionArguments (RgnDesc);
135         if (ACPI_FAILURE (Status))
136         {
137             return_ACPI_STATUS (Status);
138         }
139     }
140 
141     /*
142      * Exit now for SMBus, GSBus or IPMI address space, it has a non-linear
143      * address space and the request cannot be directly validated
144      */
145     if (SpaceId == ACPI_ADR_SPACE_SMBUS ||
146         SpaceId == ACPI_ADR_SPACE_GSBUS ||
147         SpaceId == ACPI_ADR_SPACE_IPMI)
148     {
149         /* SMBus or IPMI has a non-linear address space */
150 
151         return_ACPI_STATUS (AE_OK);
152     }
153 
154 #ifdef ACPI_UNDER_DEVELOPMENT
155     /*
156      * If the Field access is AnyAcc, we can now compute the optimal
157      * access (because we know know the length of the parent region)
158      */
159     if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
160     {
161         if (ACPI_FAILURE (Status))
162         {
163             return_ACPI_STATUS (Status);
164         }
165     }
166 #endif
167 
168     /*
169      * Validate the request. The entire request from the byte offset for a
170      * length of one field datum (access width) must fit within the region.
171      * (Region length is specified in bytes)
172      */
173     if (RgnDesc->Region.Length <
174         (ObjDesc->CommonField.BaseByteOffset + FieldDatumByteOffset +
175         ObjDesc->CommonField.AccessByteWidth))
176     {
177         if (AcpiGbl_EnableInterpreterSlack)
178         {
179             /*
180              * Slack mode only:  We will go ahead and allow access to this
181              * field if it is within the region length rounded up to the next
182              * access width boundary. ACPI_SIZE cast for 64-bit compile.
183              */
184             if (ACPI_ROUND_UP (RgnDesc->Region.Length,
185                     ObjDesc->CommonField.AccessByteWidth) >=
186                 ((ACPI_SIZE) ObjDesc->CommonField.BaseByteOffset +
187                     ObjDesc->CommonField.AccessByteWidth +
188                     FieldDatumByteOffset))
189             {
190                 return_ACPI_STATUS (AE_OK);
191             }
192         }
193 
194         if (RgnDesc->Region.Length < ObjDesc->CommonField.AccessByteWidth)
195         {
196             /*
197              * This is the case where the AccessType (AccWord, etc.) is wider
198              * than the region itself. For example, a region of length one
199              * byte, and a field with Dword access specified.
200              */
201             ACPI_ERROR ((AE_INFO,
202                 "Field [%4.4s] access width (%u bytes) "
203                 "too large for region [%4.4s] (length %u)",
204                 AcpiUtGetNodeName (ObjDesc->CommonField.Node),
205                 ObjDesc->CommonField.AccessByteWidth,
206                 AcpiUtGetNodeName (RgnDesc->Region.Node),
207                 RgnDesc->Region.Length));
208         }
209 
210         /*
211          * Offset rounded up to next multiple of field width
212          * exceeds region length, indicate an error
213          */
214         ACPI_ERROR ((AE_INFO,
215             "Field [%4.4s] Base+Offset+Width %u+%u+%u "
216             "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 
248 ACPI_STATUS
249 AcpiExAccessRegion (
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 %8.8X%8.8X\n",
296         AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
297         RgnDesc->Region.SpaceId,
298         ObjDesc->CommonField.AccessByteWidth,
299         ObjDesc->CommonField.BaseByteOffset,
300         FieldDatumByteOffset,
301         ACPI_FORMAT_UINT64 (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 
348 static BOOLEAN
349 AcpiExRegisterOverflow (
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 
401 static ACPI_STATUS
402 AcpiExFieldDatumIo (
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             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             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 (
519             ObjDesc, FieldDatumByteOffset, Value, 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, sizeof (FieldDatumByteOffset));
543         if (ACPI_FAILURE (Status))
544         {
545             return_ACPI_STATUS (Status);
546         }
547 
548         if (ReadWrite == ACPI_READ)
549         {
550             /* Read the datum from the DataRegister */
551 
552             ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
553                 "Read from Data Register\n"));
554 
555             Status = AcpiExExtractFromField (
556                 ObjDesc->IndexField.DataObj, Value, sizeof (UINT64));
557         }
558         else
559         {
560             /* Write the datum to the DataRegister */
561 
562             ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
563                 "Write to Data Register: Value %8.8X%8.8X\n",
564                 ACPI_FORMAT_UINT64 (*Value)));
565 
566             Status = AcpiExInsertIntoField (
567                 ObjDesc->IndexField.DataObj, Value, sizeof (UINT64));
568         }
569         break;
570 
571     default:
572 
573         ACPI_ERROR ((AE_INFO, "Wrong object type in field I/O %u",
574             ObjDesc->Common.Type));
575         Status = AE_AML_INTERNAL;
576         break;
577     }
578 
579     if (ACPI_SUCCESS (Status))
580     {
581         if (ReadWrite == ACPI_READ)
582         {
583             ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
584                 "Value Read %8.8X%8.8X, Width %u\n",
585                 ACPI_FORMAT_UINT64 (*Value),
586                 ObjDesc->CommonField.AccessByteWidth));
587         }
588         else
589         {
590             ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
591                 "Value Written %8.8X%8.8X, Width %u\n",
592                 ACPI_FORMAT_UINT64 (*Value),
593                 ObjDesc->CommonField.AccessByteWidth));
594         }
595     }
596 
597     return_ACPI_STATUS (Status);
598 }
599 
600 
601 /*******************************************************************************
602  *
603  * FUNCTION:    AcpiExWriteWithUpdateRule
604  *
605  * PARAMETERS:  ObjDesc                 - Field to be written
606  *              Mask                    - bitmask within field datum
607  *              FieldValue              - Value to write
608  *              FieldDatumByteOffset    - Offset of datum within field
609  *
610  * RETURN:      Status
611  *
612  * DESCRIPTION: Apply the field update rule to a field write
613  *
614  ******************************************************************************/
615 
616 ACPI_STATUS
617 AcpiExWriteWithUpdateRule (
618     ACPI_OPERAND_OBJECT     *ObjDesc,
619     UINT64                  Mask,
620     UINT64                  FieldValue,
621     UINT32                  FieldDatumByteOffset)
622 {
623     ACPI_STATUS             Status = AE_OK;
624     UINT64                  MergedValue;
625     UINT64                  CurrentValue;
626 
627 
628     ACPI_FUNCTION_TRACE_U32 (ExWriteWithUpdateRule, Mask);
629 
630 
631     /* Start with the new bits  */
632 
633     MergedValue = FieldValue;
634 
635     /* If the mask is all ones, we don't need to worry about the update rule */
636 
637     if (Mask != ACPI_UINT64_MAX)
638     {
639         /* Decode the update rule */
640 
641         switch (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)
642         {
643         case AML_FIELD_UPDATE_PRESERVE:
644             /*
645              * Check if update rule needs to be applied (not if mask is all
646              * ones)  The left shift drops the bits we want to ignore.
647              */
648             if ((~Mask << (ACPI_MUL_8 (sizeof (Mask)) -
649                    ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth))) != 0)
650             {
651                 /*
652                  * Read the current contents of the byte/word/dword containing
653                  * the field, and merge with the new field value.
654                  */
655                 Status = AcpiExFieldDatumIo (
656                     ObjDesc, FieldDatumByteOffset, &CurrentValue, ACPI_READ);
657                 if (ACPI_FAILURE (Status))
658                 {
659                     return_ACPI_STATUS (Status);
660                 }
661 
662                 MergedValue |= (CurrentValue & ~Mask);
663             }
664             break;
665 
666         case AML_FIELD_UPDATE_WRITE_AS_ONES:
667 
668             /* Set positions outside the field to all ones */
669 
670             MergedValue |= ~Mask;
671             break;
672 
673         case AML_FIELD_UPDATE_WRITE_AS_ZEROS:
674 
675             /* Set positions outside the field to all zeros */
676 
677             MergedValue &= Mask;
678             break;
679 
680         default:
681 
682             ACPI_ERROR ((AE_INFO,
683                 "Unknown UpdateRule value: 0x%X",
684                 (ObjDesc->CommonField.FieldFlags &
685                     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, "
692         "Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
693         ACPI_FORMAT_UINT64 (Mask),
694         FieldDatumByteOffset,
695         ObjDesc->CommonField.AccessByteWidth,
696         ACPI_FORMAT_UINT64 (FieldValue),
697         ACPI_FORMAT_UINT64 (MergedValue)));
698 
699     /* Write the merged value */
700 
701     Status = AcpiExFieldDatumIo (
702         ObjDesc, FieldDatumByteOffset, &MergedValue, ACPI_WRITE);
703 
704     return_ACPI_STATUS (Status);
705 }
706 
707 
708 /*******************************************************************************
709  *
710  * FUNCTION:    AcpiExExtractFromField
711  *
712  * PARAMETERS:  ObjDesc             - Field to be read
713  *              Buffer              - Where to store the field data
714  *              BufferLength        - Length of Buffer
715  *
716  * RETURN:      Status
717  *
718  * DESCRIPTION: Retrieve the current value of the given field
719  *
720  ******************************************************************************/
721 
722 ACPI_STATUS
723 AcpiExExtractFromField (
724     ACPI_OPERAND_OBJECT     *ObjDesc,
725     void                    *Buffer,
726     UINT32                  BufferLength)
727 {
728     ACPI_STATUS             Status;
729     UINT64                  RawDatum;
730     UINT64                  MergedDatum;
731     UINT32                  FieldOffset = 0;
732     UINT32                  BufferOffset = 0;
733     UINT32                  BufferTailBits;
734     UINT32                  DatumCount;
735     UINT32                  FieldDatumCount;
736     UINT32                  AccessBitWidth;
737     UINT32                  i;
738 
739 
740     ACPI_FUNCTION_TRACE (ExExtractFromField);
741 
742 
743     /* Validate target buffer and clear it */
744 
745     if (BufferLength <
746         ACPI_ROUND_BITS_UP_TO_BYTES (ObjDesc->CommonField.BitLength))
747     {
748         ACPI_ERROR ((AE_INFO,
749             "Field size %u (bits) is too large for buffer (%u)",
750             ObjDesc->CommonField.BitLength, BufferLength));
751 
752         return_ACPI_STATUS (AE_BUFFER_OVERFLOW);
753     }
754 
755     memset (Buffer, 0, BufferLength);
756     AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
757 
758     /* Handle the simple case here */
759 
760     if ((ObjDesc->CommonField.StartFieldBitOffset == 0) &&
761         (ObjDesc->CommonField.BitLength == AccessBitWidth))
762     {
763         if (BufferLength >= sizeof (UINT64))
764         {
765             Status = AcpiExFieldDatumIo (ObjDesc, 0, Buffer, ACPI_READ);
766         }
767         else
768         {
769             /* Use RawDatum (UINT64) to handle buffers < 64 bits */
770 
771             Status = AcpiExFieldDatumIo (ObjDesc, 0, &RawDatum, ACPI_READ);
772             memcpy (Buffer, &RawDatum, BufferLength);
773         }
774 
775         return_ACPI_STATUS (Status);
776     }
777 
778 /* TBD: Move to common setup code */
779 
780     /* Field algorithm is limited to sizeof(UINT64), truncate if needed */
781 
782     if (ObjDesc->CommonField.AccessByteWidth > sizeof (UINT64))
783     {
784         ObjDesc->CommonField.AccessByteWidth = sizeof (UINT64);
785         AccessBitWidth = sizeof (UINT64) * 8;
786     }
787 
788     /* Compute the number of datums (access width data items) */
789 
790     DatumCount = ACPI_ROUND_UP_TO (
791         ObjDesc->CommonField.BitLength, AccessBitWidth);
792 
793     FieldDatumCount = ACPI_ROUND_UP_TO (
794         ObjDesc->CommonField.BitLength +
795         ObjDesc->CommonField.StartFieldBitOffset, AccessBitWidth);
796 
797     /* Priming read from the field */
798 
799     Status = AcpiExFieldDatumIo (ObjDesc, FieldOffset, &RawDatum, ACPI_READ);
800     if (ACPI_FAILURE (Status))
801     {
802         return_ACPI_STATUS (Status);
803     }
804     MergedDatum = RawDatum >> ObjDesc->CommonField.StartFieldBitOffset;
805 
806     /* Read the rest of the field */
807 
808     for (i = 1; i < FieldDatumCount; i++)
809     {
810         /* Get next input datum from the field */
811 
812         FieldOffset += ObjDesc->CommonField.AccessByteWidth;
813         Status = AcpiExFieldDatumIo (
814             ObjDesc, FieldOffset, &RawDatum, ACPI_READ);
815         if (ACPI_FAILURE (Status))
816         {
817             return_ACPI_STATUS (Status);
818         }
819 
820         /*
821          * Merge with previous datum if necessary.
822          *
823          * Note: Before the shift, check if the shift value will be larger than
824          * the integer size. If so, there is no need to perform the operation.
825          * This avoids the differences in behavior between different compilers
826          * concerning shift values larger than the target data width.
827          */
828         if (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset <
829             ACPI_INTEGER_BIT_SIZE)
830         {
831             MergedDatum |= RawDatum <<
832                 (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset);
833         }
834 
835         if (i == DatumCount)
836         {
837             break;
838         }
839 
840         /* Write merged datum to target buffer */
841 
842         memcpy (((char *) Buffer) + BufferOffset, &MergedDatum,
843             ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
844                 BufferLength - BufferOffset));
845 
846         BufferOffset += ObjDesc->CommonField.AccessByteWidth;
847         MergedDatum = RawDatum >> ObjDesc->CommonField.StartFieldBitOffset;
848     }
849 
850     /* Mask off any extra bits in the last datum */
851 
852     BufferTailBits = ObjDesc->CommonField.BitLength % AccessBitWidth;
853     if (BufferTailBits)
854     {
855         MergedDatum &= ACPI_MASK_BITS_ABOVE (BufferTailBits);
856     }
857 
858     /* Write the last datum to the buffer */
859 
860     memcpy (((char *) Buffer) + BufferOffset, &MergedDatum,
861         ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
862             BufferLength - BufferOffset));
863 
864     return_ACPI_STATUS (AE_OK);
865 }
866 
867 
868 /*******************************************************************************
869  *
870  * FUNCTION:    AcpiExInsertIntoField
871  *
872  * PARAMETERS:  ObjDesc             - Field to be written
873  *              Buffer              - Data to be written
874  *              BufferLength        - Length of Buffer
875  *
876  * RETURN:      Status
877  *
878  * DESCRIPTION: Store the Buffer contents into the given field
879  *
880  ******************************************************************************/
881 
882 ACPI_STATUS
883 AcpiExInsertIntoField (
884     ACPI_OPERAND_OBJECT     *ObjDesc,
885     void                    *Buffer,
886     UINT32                  BufferLength)
887 {
888     void                    *NewBuffer;
889     ACPI_STATUS             Status;
890     UINT64                  Mask;
891     UINT64                  WidthMask;
892     UINT64                  MergedDatum;
893     UINT64                  RawDatum = 0;
894     UINT32                  FieldOffset = 0;
895     UINT32                  BufferOffset = 0;
896     UINT32                  BufferTailBits;
897     UINT32                  DatumCount;
898     UINT32                  FieldDatumCount;
899     UINT32                  AccessBitWidth;
900     UINT32                  RequiredLength;
901     UINT32                  i;
902 
903 
904     ACPI_FUNCTION_TRACE (ExInsertIntoField);
905 
906 
907     /* Validate input buffer */
908 
909     NewBuffer = NULL;
910     RequiredLength = ACPI_ROUND_BITS_UP_TO_BYTES (
911         ObjDesc->CommonField.BitLength);
912 
913     /*
914      * We must have a buffer that is at least as long as the field
915      * we are writing to. This is because individual fields are
916      * indivisible and partial writes are not supported -- as per
917      * the ACPI specification.
918      */
919     if (BufferLength < RequiredLength)
920     {
921         /* We need to create a new buffer */
922 
923         NewBuffer = ACPI_ALLOCATE_ZEROED (RequiredLength);
924         if (!NewBuffer)
925         {
926             return_ACPI_STATUS (AE_NO_MEMORY);
927         }
928 
929         /*
930          * Copy the original data to the new buffer, starting
931          * at Byte zero. All unused (upper) bytes of the
932          * buffer will be 0.
933          */
934         memcpy ((char *) NewBuffer, (char *) Buffer, BufferLength);
935         Buffer = NewBuffer;
936         BufferLength = RequiredLength;
937     }
938 
939 /* TBD: Move to common setup code */
940 
941     /* Algo is limited to sizeof(UINT64), so cut the AccessByteWidth */
942     if (ObjDesc->CommonField.AccessByteWidth > sizeof (UINT64))
943     {
944         ObjDesc->CommonField.AccessByteWidth = sizeof (UINT64);
945     }
946 
947     AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
948 
949     /*
950      * Create the bitmasks used for bit insertion.
951      * Note: This if/else is used to bypass compiler differences with the
952      * shift operator
953      */
954     if (AccessBitWidth == ACPI_INTEGER_BIT_SIZE)
955     {
956         WidthMask = ACPI_UINT64_MAX;
957     }
958     else
959     {
960         WidthMask = ACPI_MASK_BITS_ABOVE (AccessBitWidth);
961     }
962 
963     Mask = WidthMask &
964         ACPI_MASK_BITS_BELOW (ObjDesc->CommonField.StartFieldBitOffset);
965 
966     /* Compute the number of datums (access width data items) */
967 
968     DatumCount = ACPI_ROUND_UP_TO (ObjDesc->CommonField.BitLength,
969         AccessBitWidth);
970 
971     FieldDatumCount = ACPI_ROUND_UP_TO (ObjDesc->CommonField.BitLength +
972         ObjDesc->CommonField.StartFieldBitOffset,
973         AccessBitWidth);
974 
975     /* Get initial Datum from the input buffer */
976 
977     memcpy (&RawDatum, Buffer,
978         ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
979             BufferLength - BufferOffset));
980 
981     MergedDatum = RawDatum << ObjDesc->CommonField.StartFieldBitOffset;
982 
983     /* Write the entire field */
984 
985     for (i = 1; i < FieldDatumCount; i++)
986     {
987         /* Write merged datum to the target field */
988 
989         MergedDatum &= Mask;
990         Status = AcpiExWriteWithUpdateRule (
991             ObjDesc, Mask, MergedDatum, FieldOffset);
992         if (ACPI_FAILURE (Status))
993         {
994             goto Exit;
995         }
996 
997         FieldOffset += ObjDesc->CommonField.AccessByteWidth;
998 
999         /*
1000          * Start new output datum by merging with previous input datum
1001          * if necessary.
1002          *
1003          * Note: Before the shift, check if the shift value will be larger than
1004          * the integer size. If so, there is no need to perform the operation.
1005          * This avoids the differences in behavior between different compilers
1006          * concerning shift values larger than the target data width.
1007          */
1008         if ((AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset) <
1009             ACPI_INTEGER_BIT_SIZE)
1010         {
1011             MergedDatum = RawDatum >>
1012                 (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset);
1013         }
1014         else
1015         {
1016             MergedDatum = 0;
1017         }
1018 
1019         Mask = WidthMask;
1020 
1021         if (i == DatumCount)
1022         {
1023             break;
1024         }
1025 
1026         /* Get the next input datum from the buffer */
1027 
1028         BufferOffset += ObjDesc->CommonField.AccessByteWidth;
1029         memcpy (&RawDatum, ((char *) Buffer) + BufferOffset,
1030             ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
1031                  BufferLength - BufferOffset));
1032 
1033         MergedDatum |= RawDatum << ObjDesc->CommonField.StartFieldBitOffset;
1034     }
1035 
1036     /* Mask off any extra bits in the last datum */
1037 
1038     BufferTailBits = (ObjDesc->CommonField.BitLength +
1039         ObjDesc->CommonField.StartFieldBitOffset) % AccessBitWidth;
1040     if (BufferTailBits)
1041     {
1042         Mask &= ACPI_MASK_BITS_ABOVE (BufferTailBits);
1043     }
1044 
1045     /* Write the last datum to the field */
1046 
1047     MergedDatum &= Mask;
1048     Status = AcpiExWriteWithUpdateRule (
1049         ObjDesc, Mask, MergedDatum, FieldOffset);
1050 
1051 Exit:
1052     /* Free temporary buffer if we used one */
1053 
1054     if (NewBuffer)
1055     {
1056         ACPI_FREE (NewBuffer);
1057     }
1058     return_ACPI_STATUS (Status);
1059 }
1060