Branch data Line data Source code
1 : : // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 : : /******************************************************************************
3 : : *
4 : : * Module Name: exfield - AML execution - field_unit read/write
5 : : *
6 : : * Copyright (C) 2000 - 2020, Intel Corp.
7 : : *
8 : : *****************************************************************************/
9 : :
10 : : #include <acpi/acpi.h>
11 : : #include "accommon.h"
12 : : #include "acdispat.h"
13 : : #include "acinterp.h"
14 : : #include "amlcode.h"
15 : :
16 : : #define _COMPONENT ACPI_EXECUTER
17 : : ACPI_MODULE_NAME("exfield")
18 : :
19 : : /*
20 : : * This table maps the various Attrib protocols to the byte transfer
21 : : * length. Used for the generic serial bus.
22 : : */
23 : : #define ACPI_INVALID_PROTOCOL_ID 0x80
24 : : #define ACPI_MAX_PROTOCOL_ID 0x0F
25 : : const u8 acpi_protocol_lengths[] = {
26 : : ACPI_INVALID_PROTOCOL_ID, /* 0 - reserved */
27 : : ACPI_INVALID_PROTOCOL_ID, /* 1 - reserved */
28 : : 0x00, /* 2 - ATTRIB_QUICK */
29 : : ACPI_INVALID_PROTOCOL_ID, /* 3 - reserved */
30 : : 0x01, /* 4 - ATTRIB_SEND_RECEIVE */
31 : : ACPI_INVALID_PROTOCOL_ID, /* 5 - reserved */
32 : : 0x01, /* 6 - ATTRIB_BYTE */
33 : : ACPI_INVALID_PROTOCOL_ID, /* 7 - reserved */
34 : : 0x02, /* 8 - ATTRIB_WORD */
35 : : ACPI_INVALID_PROTOCOL_ID, /* 9 - reserved */
36 : : 0xFF, /* A - ATTRIB_BLOCK */
37 : : 0xFF, /* B - ATTRIB_BYTES */
38 : : 0x02, /* C - ATTRIB_PROCESS_CALL */
39 : : 0xFF, /* D - ATTRIB_BLOCK_PROCESS_CALL */
40 : : 0xFF, /* E - ATTRIB_RAW_BYTES */
41 : : 0xFF /* F - ATTRIB_RAW_PROCESS_BYTES */
42 : : };
43 : :
44 : : #define PCC_MASTER_SUBSPACE 3
45 : :
46 : : /*
47 : : * The following macros determine a given offset is a COMD field.
48 : : * According to the specification, generic subspaces (types 0-2) contains a
49 : : * 2-byte COMD field at offset 4 and master subspaces (type 3) contains a 4-byte
50 : : * COMD field starting at offset 12.
51 : : */
52 : : #define GENERIC_SUBSPACE_COMMAND(a) (4 == a || a == 5)
53 : : #define MASTER_SUBSPACE_COMMAND(a) (12 <= a && a <= 15)
54 : :
55 : : /*******************************************************************************
56 : : *
57 : : * FUNCTION: acpi_ex_get_protocol_buffer_length
58 : : *
59 : : * PARAMETERS: protocol_id - The type of the protocol indicated by region
60 : : * field access attributes
61 : : * return_length - Where the protocol byte transfer length is
62 : : * returned
63 : : *
64 : : * RETURN: Status and decoded byte transfer length
65 : : *
66 : : * DESCRIPTION: This routine returns the length of the generic_serial_bus
67 : : * protocol bytes
68 : : *
69 : : ******************************************************************************/
70 : :
71 : : acpi_status
72 : 0 : acpi_ex_get_protocol_buffer_length(u32 protocol_id, u32 *return_length)
73 : : {
74 : :
75 [ # # ]: 0 : if ((protocol_id > ACPI_MAX_PROTOCOL_ID) ||
76 [ # # ]: 0 : (acpi_protocol_lengths[protocol_id] == ACPI_INVALID_PROTOCOL_ID)) {
77 : 0 : ACPI_ERROR((AE_INFO,
78 : : "Invalid Field/AccessAs protocol ID: 0x%4.4X",
79 : : protocol_id));
80 : :
81 : 0 : return (AE_AML_PROTOCOL);
82 : : }
83 : :
84 : 0 : *return_length = acpi_protocol_lengths[protocol_id];
85 : 0 : return (AE_OK);
86 : : }
87 : :
88 : : /*******************************************************************************
89 : : *
90 : : * FUNCTION: acpi_ex_read_data_from_field
91 : : *
92 : : * PARAMETERS: walk_state - Current execution state
93 : : * obj_desc - The named field
94 : : * ret_buffer_desc - Where the return data object is stored
95 : : *
96 : : * RETURN: Status
97 : : *
98 : : * DESCRIPTION: Read from a named field. Returns either an Integer or a
99 : : * Buffer, depending on the size of the field and whether if a
100 : : * field is created by the create_field() operator.
101 : : *
102 : : ******************************************************************************/
103 : :
104 : : acpi_status
105 : 4134 : acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
106 : : union acpi_operand_object *obj_desc,
107 : : union acpi_operand_object **ret_buffer_desc)
108 : : {
109 : 4134 : acpi_status status;
110 : 4134 : union acpi_operand_object *buffer_desc;
111 : 4134 : void *buffer;
112 : 4134 : u32 buffer_length;
113 : :
114 : 4134 : ACPI_FUNCTION_TRACE_PTR(ex_read_data_from_field, obj_desc);
115 : :
116 : : /* Parameter validation */
117 : :
118 [ + - ]: 4134 : if (!obj_desc) {
119 : : return_ACPI_STATUS(AE_AML_NO_OPERAND);
120 : : }
121 [ + - ]: 4134 : if (!ret_buffer_desc) {
122 : : return_ACPI_STATUS(AE_BAD_PARAMETER);
123 : : }
124 : :
125 [ + + ]: 4134 : if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) {
126 : : /*
127 : : * If the buffer_field arguments have not been previously evaluated,
128 : : * evaluate them now and save the results.
129 : : */
130 [ - + ]: 78 : if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
131 : 0 : status = acpi_ds_get_buffer_field_arguments(obj_desc);
132 [ # # ]: 0 : if (ACPI_FAILURE(status)) {
133 : : return_ACPI_STATUS(status);
134 : : }
135 : : }
136 [ + - ]: 4056 : } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
137 : 4056 : (obj_desc->field.region_obj->region.space_id ==
138 : : ACPI_ADR_SPACE_SMBUS
139 [ + - ]: 4056 : || obj_desc->field.region_obj->region.space_id ==
140 : : ACPI_ADR_SPACE_GSBUS
141 [ - + ]: 4056 : || obj_desc->field.region_obj->region.space_id ==
142 : : ACPI_ADR_SPACE_IPMI)) {
143 : :
144 : : /* SMBus, GSBus, IPMI serial */
145 : :
146 : 0 : status = acpi_ex_read_serial_bus(obj_desc, ret_buffer_desc);
147 : 0 : return_ACPI_STATUS(status);
148 : : }
149 : :
150 : : /*
151 : : * Allocate a buffer for the contents of the field.
152 : : *
153 : : * If the field is larger than the current integer width, create
154 : : * a BUFFER to hold it. Otherwise, use an INTEGER. This allows
155 : : * the use of arithmetic operators on the returned value if the
156 : : * field size is equal or smaller than an Integer.
157 : : *
158 : : * However, all buffer fields created by create_field operator needs to
159 : : * remain as a buffer to match other AML interpreter implementations.
160 : : *
161 : : * Note: Field.length is in bits.
162 : : */
163 : 4134 : buffer_length =
164 : 4134 : (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.bit_length);
165 : :
166 [ + - ]: 4134 : if (buffer_length > acpi_gbl_integer_byte_width ||
167 [ + + ]: 4134 : (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD &&
168 [ - + ]: 78 : obj_desc->buffer_field.is_create_field)) {
169 : :
170 : : /* Field is too large for an Integer, create a Buffer instead */
171 : :
172 : 0 : buffer_desc = acpi_ut_create_buffer_object(buffer_length);
173 [ # # ]: 0 : if (!buffer_desc) {
174 : : return_ACPI_STATUS(AE_NO_MEMORY);
175 : : }
176 : 0 : buffer = buffer_desc->buffer.pointer;
177 : : } else {
178 : : /* Field will fit within an Integer (normal case) */
179 : :
180 : 4134 : buffer_desc = acpi_ut_create_integer_object((u64) 0);
181 [ + - ]: 4134 : if (!buffer_desc) {
182 : : return_ACPI_STATUS(AE_NO_MEMORY);
183 : : }
184 : :
185 : 4134 : buffer_length = acpi_gbl_integer_byte_width;
186 : 4134 : buffer = &buffer_desc->integer.value;
187 : : }
188 : :
189 [ + + ]: 4134 : if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
190 [ - + ]: 4056 : (obj_desc->field.region_obj->region.space_id ==
191 : : ACPI_ADR_SPACE_GPIO)) {
192 : :
193 : : /* General Purpose I/O */
194 : :
195 : 0 : status = acpi_ex_read_gpio(obj_desc, buffer);
196 : 0 : goto exit;
197 [ + + ]: 4134 : } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
198 [ - + ]: 4056 : (obj_desc->field.region_obj->region.space_id ==
199 : : ACPI_ADR_SPACE_PLATFORM_COMM)) {
200 : : /*
201 : : * Reading from a PCC field unit does not require the handler because
202 : : * it only requires reading from the internal_pcc_buffer.
203 : : */
204 : : ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
205 : : "PCC FieldRead bits %u\n",
206 : 0 : obj_desc->field.bit_length));
207 : :
208 : 0 : memcpy(buffer,
209 : 0 : obj_desc->field.region_obj->field.internal_pcc_buffer +
210 : 0 : obj_desc->field.base_byte_offset,
211 : 0 : (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.
212 : : bit_length));
213 : :
214 : 0 : *ret_buffer_desc = buffer_desc;
215 : 0 : return AE_OK;
216 : : }
217 : :
218 : : ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
219 : : "FieldRead [TO]: Obj %p, Type %X, Buf %p, ByteLen %X\n",
220 : : obj_desc, obj_desc->common.type, buffer,
221 : 4134 : buffer_length));
222 : : ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
223 : : "FieldRead [FROM]: BitLen %X, BitOff %X, ByteOff %X\n",
224 : : obj_desc->common_field.bit_length,
225 : : obj_desc->common_field.start_field_bit_offset,
226 : 4134 : obj_desc->common_field.base_byte_offset));
227 : :
228 : : /* Lock entire transaction if requested */
229 : :
230 : 4134 : acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
231 : :
232 : : /* Read from the field */
233 : :
234 : 4134 : status = acpi_ex_extract_from_field(obj_desc, buffer, buffer_length);
235 : 4134 : acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
236 : :
237 : 4134 : exit:
238 [ - + ]: 4134 : if (ACPI_FAILURE(status)) {
239 : 0 : acpi_ut_remove_reference(buffer_desc);
240 : : } else {
241 : 4134 : *ret_buffer_desc = buffer_desc;
242 : : }
243 : :
244 : : return_ACPI_STATUS(status);
245 : : }
246 : :
247 : : /*******************************************************************************
248 : : *
249 : : * FUNCTION: acpi_ex_write_data_to_field
250 : : *
251 : : * PARAMETERS: source_desc - Contains data to write
252 : : * obj_desc - The named field
253 : : * result_desc - Where the return value is returned, if any
254 : : *
255 : : * RETURN: Status
256 : : *
257 : : * DESCRIPTION: Write to a named field
258 : : *
259 : : ******************************************************************************/
260 : :
261 : : acpi_status
262 : 1326 : acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
263 : : union acpi_operand_object *obj_desc,
264 : : union acpi_operand_object **result_desc)
265 : : {
266 : 1326 : acpi_status status;
267 : 1326 : u32 buffer_length;
268 : 1326 : u32 data_length;
269 : 1326 : void *buffer;
270 : :
271 : 1326 : ACPI_FUNCTION_TRACE_PTR(ex_write_data_to_field, obj_desc);
272 : :
273 : : /* Parameter validation */
274 : :
275 [ + - ]: 1326 : if (!source_desc || !obj_desc) {
276 : : return_ACPI_STATUS(AE_AML_NO_OPERAND);
277 : : }
278 : :
279 [ + + ]: 1326 : if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) {
280 : : /*
281 : : * If the buffer_field arguments have not been previously evaluated,
282 : : * evaluate them now and save the results.
283 : : */
284 [ - + ]: 702 : if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
285 : 0 : status = acpi_ds_get_buffer_field_arguments(obj_desc);
286 [ # # ]: 0 : if (ACPI_FAILURE(status)) {
287 : : return_ACPI_STATUS(status);
288 : : }
289 : : }
290 [ + - ]: 624 : } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
291 [ - + ]: 624 : (obj_desc->field.region_obj->region.space_id ==
292 : : ACPI_ADR_SPACE_GPIO)) {
293 : :
294 : : /* General Purpose I/O */
295 : :
296 : 0 : status = acpi_ex_write_gpio(source_desc, obj_desc, result_desc);
297 : 0 : return_ACPI_STATUS(status);
298 [ + - ]: 624 : } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
299 : 624 : (obj_desc->field.region_obj->region.space_id ==
300 : : ACPI_ADR_SPACE_SMBUS
301 [ + - ]: 624 : || obj_desc->field.region_obj->region.space_id ==
302 : : ACPI_ADR_SPACE_GSBUS
303 [ - + ]: 624 : || obj_desc->field.region_obj->region.space_id ==
304 : : ACPI_ADR_SPACE_IPMI)) {
305 : :
306 : : /* SMBus, GSBus, IPMI serial */
307 : :
308 : 0 : status =
309 : 0 : acpi_ex_write_serial_bus(source_desc, obj_desc,
310 : : result_desc);
311 : 0 : return_ACPI_STATUS(status);
312 [ + - ]: 624 : } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
313 [ - + ]: 624 : (obj_desc->field.region_obj->region.space_id ==
314 : : ACPI_ADR_SPACE_PLATFORM_COMM)) {
315 : : /*
316 : : * According to the spec a write to the COMD field will invoke the
317 : : * region handler. Otherwise, write to the pcc_internal buffer. This
318 : : * implementation will use the offsets specified rather than the name
319 : : * of the field. This is considered safer because some firmware tools
320 : : * are known to obfiscate named objects.
321 : : */
322 : 0 : data_length =
323 : 0 : (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.
324 : : bit_length);
325 : 0 : memcpy(obj_desc->field.region_obj->field.internal_pcc_buffer +
326 : 0 : obj_desc->field.base_byte_offset,
327 : 0 : source_desc->buffer.pointer, data_length);
328 : :
329 [ # # ]: 0 : if ((obj_desc->field.region_obj->region.address ==
330 : : PCC_MASTER_SUBSPACE
331 [ # # ]: 0 : && MASTER_SUBSPACE_COMMAND(obj_desc->field.
332 : : base_byte_offset))
333 [ # # ]: 0 : || GENERIC_SUBSPACE_COMMAND(obj_desc->field.
334 : : base_byte_offset)) {
335 : :
336 : : /* Perform the write */
337 : :
338 : : ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
339 : 0 : "PCC COMD field has been written. Invoking PCC handler now.\n"));
340 : :
341 : 0 : status =
342 : 0 : acpi_ex_access_region(obj_desc, 0,
343 : : (u64 *)obj_desc->field.
344 : 0 : region_obj->field.
345 : : internal_pcc_buffer,
346 : : ACPI_WRITE);
347 : 0 : return_ACPI_STATUS(status);
348 : : }
349 : : return (AE_OK);
350 : : }
351 : :
352 : : /* Get a pointer to the data to be written */
353 : :
354 [ + - - - ]: 1326 : switch (source_desc->common.type) {
355 : 1326 : case ACPI_TYPE_INTEGER:
356 : :
357 : 1326 : buffer = &source_desc->integer.value;
358 : 1326 : buffer_length = sizeof(source_desc->integer.value);
359 : 1326 : break;
360 : :
361 : 0 : case ACPI_TYPE_BUFFER:
362 : :
363 : 0 : buffer = source_desc->buffer.pointer;
364 : 0 : buffer_length = source_desc->buffer.length;
365 : 0 : break;
366 : :
367 : 0 : case ACPI_TYPE_STRING:
368 : :
369 : 0 : buffer = source_desc->string.pointer;
370 : 0 : buffer_length = source_desc->string.length;
371 : 0 : break;
372 : :
373 : : default:
374 : : return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
375 : : }
376 : :
377 : : ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
378 : : "FieldWrite [FROM]: Obj %p (%s:%X), Buf %p, ByteLen %X\n",
379 : : source_desc,
380 : : acpi_ut_get_type_name(source_desc->common.type),
381 : 1326 : source_desc->common.type, buffer, buffer_length));
382 : :
383 : : ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
384 : : "FieldWrite [TO]: Obj %p (%s:%X), BitLen %X, BitOff %X, ByteOff %X\n",
385 : : obj_desc,
386 : : acpi_ut_get_type_name(obj_desc->common.type),
387 : : obj_desc->common.type,
388 : : obj_desc->common_field.bit_length,
389 : : obj_desc->common_field.start_field_bit_offset,
390 : 1326 : obj_desc->common_field.base_byte_offset));
391 : :
392 : : /* Lock entire transaction if requested */
393 : :
394 : 1326 : acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
395 : :
396 : : /* Write to the field */
397 : :
398 : 1326 : status = acpi_ex_insert_into_field(obj_desc, buffer, buffer_length);
399 : 1326 : acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
400 : 1326 : return_ACPI_STATUS(status);
401 : : }
|