brintos

brintos / linux-shallow public Read only

0
0
Text · 12.5 KiB · 61ff361 Raw
405 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/******************************************************************************3 *4 * Module Name: exfield - AML execution - field_unit read/write5 *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_EXECUTER17ACPI_MODULE_NAME("exfield")18 19/*20 * This table maps the various Attrib protocols to the byte transfer21 * length. Used for the generic serial bus.22 */23#define ACPI_INVALID_PROTOCOL_ID        0x8024#define ACPI_MAX_PROTOCOL_ID            0x0F25static 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     345 46/*47 * The following macros determine a given offset is a COMD field.48 * According to the specification, generic subspaces (types 0-2) contains a49 * 2-byte COMD field at offset 4 and master subspaces (type 3) contains a 4-byte50 * 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_length58 *59 * PARAMETERS:  protocol_id     - The type of the protocol indicated by region60 *                                field access attributes61 *              return_length   - Where the protocol byte transfer length is62 *                                returned63 *64 * RETURN:      Status and decoded byte transfer length65 *66 * DESCRIPTION: This routine returns the length of the generic_serial_bus67 *              protocol bytes68 *69 ******************************************************************************/70 71acpi_status72acpi_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_field91 *92 * PARAMETERS:  walk_state          - Current execution state93 *              obj_desc            - The named field94 *              ret_buffer_desc     - Where the return data object is stored95 *96 * RETURN:      Status97 *98 * DESCRIPTION: Read from a named field. Returns either an Integer or a99 *              Buffer, depending on the size of the field and whether if a100 *              field is created by the create_field() operator.101 *102 ******************************************************************************/103 104acpi_status105acpi_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_SMBUS139		    || obj_desc->field.region_obj->region.space_id ==140		    ACPI_ADR_SPACE_GSBUS141		    || obj_desc->field.region_obj->region.space_id ==142		    ACPI_ADR_SPACE_IPMI143		    || obj_desc->field.region_obj->region.space_id ==144		    ACPI_ADR_SPACE_PLATFORM_RT145		    || 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, create158	 * a BUFFER to hold it. Otherwise, use an INTEGER. This allows159	 * the use of arithmetic operators on the returned value if the160	 * field size is equal or smaller than an Integer.161	 *162	 * However, all buffer fields created by create_field operator needs to163	 * 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 because206		 * 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 241exit: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_field254 *255 * PARAMETERS:  source_desc         - Contains data to write256 *              obj_desc            - The named field257 *              result_desc         - Where the return value is returned, if any258 *259 * RETURN:      Status260 *261 * DESCRIPTION: Write to a named field262 *263 ******************************************************************************/264 265acpi_status266acpi_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_SMBUS305		    || obj_desc->field.region_obj->region.space_id ==306		    ACPI_ADR_SPACE_GSBUS307		    || obj_desc->field.region_obj->region.space_id ==308		    ACPI_ADR_SPACE_IPMI309		    || obj_desc->field.region_obj->region.space_id ==310		    ACPI_ADR_SPACE_PLATFORM_RT311		    || 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 the325		 * region handler. Otherwise, write to the pcc_internal buffer. This326		 * implementation will use the offsets specified rather than the name327		 * of the field. This is considered safer because some firmware tools328		 * 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