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 - 2023, 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  static 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
acpi_ex_get_protocol_buffer_length(u32 protocol_id,u32 * return_length)72  acpi_ex_get_protocol_buffer_length(u32 protocol_id, u32 *return_length)
73  {
74  
75  	if ((protocol_id > ACPI_MAX_PROTOCOL_ID) ||
76  	    (acpi_protocol_lengths[protocol_id] == ACPI_INVALID_PROTOCOL_ID)) {
77  		ACPI_ERROR((AE_INFO,
78  			    "Invalid Field/AccessAs protocol ID: 0x%4.4X",
79  			    protocol_id));
80  
81  		return (AE_AML_PROTOCOL);
82  	}
83  
84  	*return_length = acpi_protocol_lengths[protocol_id];
85  	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
acpi_ex_read_data_from_field(struct acpi_walk_state * walk_state,union acpi_operand_object * obj_desc,union acpi_operand_object ** ret_buffer_desc)105  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  	acpi_status status;
110  	union acpi_operand_object *buffer_desc;
111  	void *buffer;
112  	u32 buffer_length;
113  
114  	ACPI_FUNCTION_TRACE_PTR(ex_read_data_from_field, obj_desc);
115  
116  	/* Parameter validation */
117  
118  	if (!obj_desc) {
119  		return_ACPI_STATUS(AE_AML_NO_OPERAND);
120  	}
121  	if (!ret_buffer_desc) {
122  		return_ACPI_STATUS(AE_BAD_PARAMETER);
123  	}
124  
125  	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  		if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
131  			status = acpi_ds_get_buffer_field_arguments(obj_desc);
132  			if (ACPI_FAILURE(status)) {
133  				return_ACPI_STATUS(status);
134  			}
135  		}
136  	} else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
137  		   (obj_desc->field.region_obj->region.space_id ==
138  		    ACPI_ADR_SPACE_SMBUS
139  		    || obj_desc->field.region_obj->region.space_id ==
140  		    ACPI_ADR_SPACE_GSBUS
141  		    || obj_desc->field.region_obj->region.space_id ==
142  		    ACPI_ADR_SPACE_IPMI
143  		    || obj_desc->field.region_obj->region.space_id ==
144  		    ACPI_ADR_SPACE_PLATFORM_RT
145  		    || obj_desc->field.region_obj->region.space_id ==
146  		    ACPI_ADR_SPACE_FIXED_HARDWARE)) {
147  
148  		/* SMBus, GSBus, IPMI serial */
149  
150  		status = acpi_ex_read_serial_bus(obj_desc, ret_buffer_desc);
151  		return_ACPI_STATUS(status);
152  	}
153  
154  	/*
155  	 * Allocate a buffer for the contents of the field.
156  	 *
157  	 * If the field is larger than the current integer width, create
158  	 * a BUFFER to hold it. Otherwise, use an INTEGER. This allows
159  	 * the use of arithmetic operators on the returned value if the
160  	 * field size is equal or smaller than an Integer.
161  	 *
162  	 * However, all buffer fields created by create_field operator needs to
163  	 * remain as a buffer to match other AML interpreter implementations.
164  	 *
165  	 * Note: Field.length is in bits.
166  	 */
167  	buffer_length =
168  	    (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.bit_length);
169  
170  	if (buffer_length > acpi_gbl_integer_byte_width ||
171  	    (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD &&
172  	     obj_desc->buffer_field.is_create_field)) {
173  
174  		/* Field is too large for an Integer, create a Buffer instead */
175  
176  		buffer_desc = acpi_ut_create_buffer_object(buffer_length);
177  		if (!buffer_desc) {
178  			return_ACPI_STATUS(AE_NO_MEMORY);
179  		}
180  		buffer = buffer_desc->buffer.pointer;
181  	} else {
182  		/* Field will fit within an Integer (normal case) */
183  
184  		buffer_desc = acpi_ut_create_integer_object((u64) 0);
185  		if (!buffer_desc) {
186  			return_ACPI_STATUS(AE_NO_MEMORY);
187  		}
188  
189  		buffer_length = acpi_gbl_integer_byte_width;
190  		buffer = &buffer_desc->integer.value;
191  	}
192  
193  	if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
194  	    (obj_desc->field.region_obj->region.space_id ==
195  	     ACPI_ADR_SPACE_GPIO)) {
196  
197  		/* General Purpose I/O */
198  
199  		status = acpi_ex_read_gpio(obj_desc, buffer);
200  		goto exit;
201  	} else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
202  		   (obj_desc->field.region_obj->region.space_id ==
203  		    ACPI_ADR_SPACE_PLATFORM_COMM)) {
204  		/*
205  		 * Reading from a PCC field unit does not require the handler because
206  		 * it only requires reading from the internal_pcc_buffer.
207  		 */
208  		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
209  				  "PCC FieldRead bits %u\n",
210  				  obj_desc->field.bit_length));
211  
212  		memcpy(buffer,
213  		       obj_desc->field.region_obj->field.internal_pcc_buffer +
214  		       obj_desc->field.base_byte_offset,
215  		       (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.
216  							      bit_length));
217  
218  		*ret_buffer_desc = buffer_desc;
219  		return AE_OK;
220  	}
221  
222  	ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
223  			  "FieldRead [TO]:   Obj %p, Type %X, Buf %p, ByteLen %X\n",
224  			  obj_desc, obj_desc->common.type, buffer,
225  			  buffer_length));
226  	ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
227  			  "FieldRead [FROM]: BitLen %X, BitOff %X, ByteOff %X\n",
228  			  obj_desc->common_field.bit_length,
229  			  obj_desc->common_field.start_field_bit_offset,
230  			  obj_desc->common_field.base_byte_offset));
231  
232  	/* Lock entire transaction if requested */
233  
234  	acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
235  
236  	/* Read from the field */
237  
238  	status = acpi_ex_extract_from_field(obj_desc, buffer, buffer_length);
239  	acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
240  
241  exit:
242  	if (ACPI_FAILURE(status)) {
243  		acpi_ut_remove_reference(buffer_desc);
244  	} else {
245  		*ret_buffer_desc = buffer_desc;
246  	}
247  
248  	return_ACPI_STATUS(status);
249  }
250  
251  /*******************************************************************************
252   *
253   * FUNCTION:    acpi_ex_write_data_to_field
254   *
255   * PARAMETERS:  source_desc         - Contains data to write
256   *              obj_desc            - The named field
257   *              result_desc         - Where the return value is returned, if any
258   *
259   * RETURN:      Status
260   *
261   * DESCRIPTION: Write to a named field
262   *
263   ******************************************************************************/
264  
265  acpi_status
acpi_ex_write_data_to_field(union acpi_operand_object * source_desc,union acpi_operand_object * obj_desc,union acpi_operand_object ** result_desc)266  acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
267  			    union acpi_operand_object *obj_desc,
268  			    union acpi_operand_object **result_desc)
269  {
270  	acpi_status status;
271  	u32 buffer_length;
272  	u32 data_length;
273  	void *buffer;
274  
275  	ACPI_FUNCTION_TRACE_PTR(ex_write_data_to_field, obj_desc);
276  
277  	/* Parameter validation */
278  
279  	if (!source_desc || !obj_desc) {
280  		return_ACPI_STATUS(AE_AML_NO_OPERAND);
281  	}
282  
283  	if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) {
284  		/*
285  		 * If the buffer_field arguments have not been previously evaluated,
286  		 * evaluate them now and save the results.
287  		 */
288  		if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
289  			status = acpi_ds_get_buffer_field_arguments(obj_desc);
290  			if (ACPI_FAILURE(status)) {
291  				return_ACPI_STATUS(status);
292  			}
293  		}
294  	} else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
295  		   (obj_desc->field.region_obj->region.space_id ==
296  		    ACPI_ADR_SPACE_GPIO)) {
297  
298  		/* General Purpose I/O */
299  
300  		status = acpi_ex_write_gpio(source_desc, obj_desc, result_desc);
301  		return_ACPI_STATUS(status);
302  	} else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
303  		   (obj_desc->field.region_obj->region.space_id ==
304  		    ACPI_ADR_SPACE_SMBUS
305  		    || obj_desc->field.region_obj->region.space_id ==
306  		    ACPI_ADR_SPACE_GSBUS
307  		    || obj_desc->field.region_obj->region.space_id ==
308  		    ACPI_ADR_SPACE_IPMI
309  		    || obj_desc->field.region_obj->region.space_id ==
310  		    ACPI_ADR_SPACE_PLATFORM_RT
311  		    || obj_desc->field.region_obj->region.space_id ==
312  		    ACPI_ADR_SPACE_FIXED_HARDWARE)) {
313  
314  		/* SMBus, GSBus, IPMI serial */
315  
316  		status =
317  		    acpi_ex_write_serial_bus(source_desc, obj_desc,
318  					     result_desc);
319  		return_ACPI_STATUS(status);
320  	} else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
321  		   (obj_desc->field.region_obj->region.space_id ==
322  		    ACPI_ADR_SPACE_PLATFORM_COMM)) {
323  		/*
324  		 * According to the spec a write to the COMD field will invoke the
325  		 * region handler. Otherwise, write to the pcc_internal buffer. This
326  		 * implementation will use the offsets specified rather than the name
327  		 * of the field. This is considered safer because some firmware tools
328  		 * are known to obfiscate named objects.
329  		 */
330  		data_length =
331  		    (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.
332  							   bit_length);
333  		memcpy(obj_desc->field.region_obj->field.internal_pcc_buffer +
334  		       obj_desc->field.base_byte_offset,
335  		       source_desc->buffer.pointer, data_length);
336  
337  		if (MASTER_SUBSPACE_COMMAND(obj_desc->field.base_byte_offset)) {
338  
339  			/* Perform the write */
340  
341  			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
342  					  "PCC COMD field has been written. Invoking PCC handler now.\n"));
343  
344  			status =
345  			    acpi_ex_access_region(obj_desc, 0,
346  						  (u64 *)obj_desc->field.
347  						  region_obj->field.
348  						  internal_pcc_buffer,
349  						  ACPI_WRITE);
350  			return_ACPI_STATUS(status);
351  		}
352  		return (AE_OK);
353  	}
354  
355  	/* Get a pointer to the data to be written */
356  
357  	switch (source_desc->common.type) {
358  	case ACPI_TYPE_INTEGER:
359  
360  		buffer = &source_desc->integer.value;
361  		buffer_length = sizeof(source_desc->integer.value);
362  		break;
363  
364  	case ACPI_TYPE_BUFFER:
365  
366  		buffer = source_desc->buffer.pointer;
367  		buffer_length = source_desc->buffer.length;
368  		break;
369  
370  	case ACPI_TYPE_STRING:
371  
372  		buffer = source_desc->string.pointer;
373  		buffer_length = source_desc->string.length;
374  		break;
375  
376  	default:
377  		return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
378  	}
379  
380  	ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
381  			  "FieldWrite [FROM]: Obj %p (%s:%X), Buf %p, ByteLen %X\n",
382  			  source_desc,
383  			  acpi_ut_get_type_name(source_desc->common.type),
384  			  source_desc->common.type, buffer, buffer_length));
385  
386  	ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
387  			  "FieldWrite [TO]:   Obj %p (%s:%X), BitLen %X, BitOff %X, ByteOff %X\n",
388  			  obj_desc,
389  			  acpi_ut_get_type_name(obj_desc->common.type),
390  			  obj_desc->common.type,
391  			  obj_desc->common_field.bit_length,
392  			  obj_desc->common_field.start_field_bit_offset,
393  			  obj_desc->common_field.base_byte_offset));
394  
395  	/* Lock entire transaction if requested */
396  
397  	acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
398  
399  	/* Write to the field */
400  
401  	status = acpi_ex_insert_into_field(obj_desc, buffer, buffer_length);
402  	acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
403  	return_ACPI_STATUS(status);
404  }
405