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 "acpi.h" |
45 | #include "accommon.h" |
46 | #include "acinterp.h" |
47 | #include "amlcode.h" |
48 | #include "acevents.h" |
49 | #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 | ( |
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 | |