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