| // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 | 
 | /****************************************************************************** | 
 |  * | 
 |  * Module Name: exconcat - Concatenate-type AML operators | 
 |  * | 
 |  * Copyright (C) 2000 - 2018, Intel Corp. | 
 |  * | 
 |  *****************************************************************************/ | 
 |  | 
 | #include <acpi/acpi.h> | 
 | #include "accommon.h" | 
 | #include "acinterp.h" | 
 | #include "amlresrc.h" | 
 |  | 
 | #define _COMPONENT          ACPI_EXECUTER | 
 | ACPI_MODULE_NAME("exconcat") | 
 |  | 
 | /* Local Prototypes */ | 
 | static acpi_status | 
 | acpi_ex_convert_to_object_type_string(union acpi_operand_object *obj_desc, | 
 | 				      union acpi_operand_object **result_desc); | 
 |  | 
 | /******************************************************************************* | 
 |  * | 
 |  * FUNCTION:    acpi_ex_do_concatenate | 
 |  * | 
 |  * PARAMETERS:  operand0            - First source object | 
 |  *              operand1            - Second source object | 
 |  *              actual_return_desc  - Where to place the return object | 
 |  *              walk_state          - Current walk state | 
 |  * | 
 |  * RETURN:      Status | 
 |  * | 
 |  * DESCRIPTION: Concatenate two objects with the ACPI-defined conversion | 
 |  *              rules as necessary. | 
 |  * NOTE: | 
 |  * Per the ACPI spec (up to 6.1), Concatenate only supports Integer, | 
 |  * String, and Buffer objects. However, we support all objects here | 
 |  * as an extension. This improves the usefulness of both Concatenate | 
 |  * and the Printf/Fprintf macros. The extension returns a string | 
 |  * describing the object type for the other objects. | 
 |  * 02/2016. | 
 |  * | 
 |  ******************************************************************************/ | 
 |  | 
 | acpi_status | 
 | acpi_ex_do_concatenate(union acpi_operand_object *operand0, | 
 | 		       union acpi_operand_object *operand1, | 
 | 		       union acpi_operand_object **actual_return_desc, | 
 | 		       struct acpi_walk_state *walk_state) | 
 | { | 
 | 	union acpi_operand_object *local_operand0 = operand0; | 
 | 	union acpi_operand_object *local_operand1 = operand1; | 
 | 	union acpi_operand_object *temp_operand1 = NULL; | 
 | 	union acpi_operand_object *return_desc; | 
 | 	char *buffer; | 
 | 	acpi_object_type operand0_type; | 
 | 	acpi_object_type operand1_type; | 
 | 	acpi_status status; | 
 |  | 
 | 	ACPI_FUNCTION_TRACE(ex_do_concatenate); | 
 |  | 
 | 	/* Operand 0 preprocessing */ | 
 |  | 
 | 	switch (operand0->common.type) { | 
 | 	case ACPI_TYPE_INTEGER: | 
 | 	case ACPI_TYPE_STRING: | 
 | 	case ACPI_TYPE_BUFFER: | 
 |  | 
 | 		operand0_type = operand0->common.type; | 
 | 		break; | 
 |  | 
 | 	default: | 
 |  | 
 | 		/* For all other types, get the "object type" string */ | 
 |  | 
 | 		status = | 
 | 		    acpi_ex_convert_to_object_type_string(operand0, | 
 | 							  &local_operand0); | 
 | 		if (ACPI_FAILURE(status)) { | 
 | 			goto cleanup; | 
 | 		} | 
 |  | 
 | 		operand0_type = ACPI_TYPE_STRING; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	/* Operand 1 preprocessing */ | 
 |  | 
 | 	switch (operand1->common.type) { | 
 | 	case ACPI_TYPE_INTEGER: | 
 | 	case ACPI_TYPE_STRING: | 
 | 	case ACPI_TYPE_BUFFER: | 
 |  | 
 | 		operand1_type = operand1->common.type; | 
 | 		break; | 
 |  | 
 | 	default: | 
 |  | 
 | 		/* For all other types, get the "object type" string */ | 
 |  | 
 | 		status = | 
 | 		    acpi_ex_convert_to_object_type_string(operand1, | 
 | 							  &local_operand1); | 
 | 		if (ACPI_FAILURE(status)) { | 
 | 			goto cleanup; | 
 | 		} | 
 |  | 
 | 		operand1_type = ACPI_TYPE_STRING; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Convert the second operand if necessary. The first operand (0) | 
 | 	 * determines the type of the second operand (1) (See the Data Types | 
 | 	 * section of the ACPI specification). Both object types are | 
 | 	 * guaranteed to be either Integer/String/Buffer by the operand | 
 | 	 * resolution mechanism. | 
 | 	 */ | 
 | 	switch (operand0_type) { | 
 | 	case ACPI_TYPE_INTEGER: | 
 |  | 
 | 		status = | 
 | 		    acpi_ex_convert_to_integer(local_operand1, &temp_operand1, | 
 | 					       ACPI_IMPLICIT_CONVERSION); | 
 | 		break; | 
 |  | 
 | 	case ACPI_TYPE_BUFFER: | 
 |  | 
 | 		status = | 
 | 		    acpi_ex_convert_to_buffer(local_operand1, &temp_operand1); | 
 | 		break; | 
 |  | 
 | 	case ACPI_TYPE_STRING: | 
 |  | 
 | 		switch (operand1_type) { | 
 | 		case ACPI_TYPE_INTEGER: | 
 | 		case ACPI_TYPE_STRING: | 
 | 		case ACPI_TYPE_BUFFER: | 
 |  | 
 | 			/* Other types have already been converted to string */ | 
 |  | 
 | 			status = | 
 | 			    acpi_ex_convert_to_string(local_operand1, | 
 | 						      &temp_operand1, | 
 | 						      ACPI_IMPLICIT_CONVERT_HEX); | 
 | 			break; | 
 |  | 
 | 		default: | 
 |  | 
 | 			status = AE_OK; | 
 | 			break; | 
 | 		} | 
 | 		break; | 
 |  | 
 | 	default: | 
 |  | 
 | 		ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X", | 
 | 			    operand0->common.type)); | 
 | 		status = AE_AML_INTERNAL; | 
 | 	} | 
 |  | 
 | 	if (ACPI_FAILURE(status)) { | 
 | 		goto cleanup; | 
 | 	} | 
 |  | 
 | 	/* Take care with any newly created operand objects */ | 
 |  | 
 | 	if ((local_operand1 != operand1) && (local_operand1 != temp_operand1)) { | 
 | 		acpi_ut_remove_reference(local_operand1); | 
 | 	} | 
 |  | 
 | 	local_operand1 = temp_operand1; | 
 |  | 
 | 	/* | 
 | 	 * Both operands are now known to be the same object type | 
 | 	 * (Both are Integer, String, or Buffer), and we can now perform | 
 | 	 * the concatenation. | 
 | 	 * | 
 | 	 * There are three cases to handle, as per the ACPI spec: | 
 | 	 * | 
 | 	 * 1) Two Integers concatenated to produce a new Buffer | 
 | 	 * 2) Two Strings concatenated to produce a new String | 
 | 	 * 3) Two Buffers concatenated to produce a new Buffer | 
 | 	 */ | 
 | 	switch (operand0_type) { | 
 | 	case ACPI_TYPE_INTEGER: | 
 |  | 
 | 		/* Result of two Integers is a Buffer */ | 
 | 		/* Need enough buffer space for two integers */ | 
 |  | 
 | 		return_desc = acpi_ut_create_buffer_object((acpi_size) | 
 | 							   ACPI_MUL_2 | 
 | 							   (acpi_gbl_integer_byte_width)); | 
 | 		if (!return_desc) { | 
 | 			status = AE_NO_MEMORY; | 
 | 			goto cleanup; | 
 | 		} | 
 |  | 
 | 		buffer = (char *)return_desc->buffer.pointer; | 
 |  | 
 | 		/* Copy the first integer, LSB first */ | 
 |  | 
 | 		memcpy(buffer, &operand0->integer.value, | 
 | 		       acpi_gbl_integer_byte_width); | 
 |  | 
 | 		/* Copy the second integer (LSB first) after the first */ | 
 |  | 
 | 		memcpy(buffer + acpi_gbl_integer_byte_width, | 
 | 		       &local_operand1->integer.value, | 
 | 		       acpi_gbl_integer_byte_width); | 
 | 		break; | 
 |  | 
 | 	case ACPI_TYPE_STRING: | 
 |  | 
 | 		/* Result of two Strings is a String */ | 
 |  | 
 | 		return_desc = acpi_ut_create_string_object(((acpi_size) | 
 | 							    local_operand0-> | 
 | 							    string.length + | 
 | 							    local_operand1-> | 
 | 							    string.length)); | 
 | 		if (!return_desc) { | 
 | 			status = AE_NO_MEMORY; | 
 | 			goto cleanup; | 
 | 		} | 
 |  | 
 | 		buffer = return_desc->string.pointer; | 
 |  | 
 | 		/* Concatenate the strings */ | 
 |  | 
 | 		strcpy(buffer, local_operand0->string.pointer); | 
 | 		strcat(buffer, local_operand1->string.pointer); | 
 | 		break; | 
 |  | 
 | 	case ACPI_TYPE_BUFFER: | 
 |  | 
 | 		/* Result of two Buffers is a Buffer */ | 
 |  | 
 | 		return_desc = acpi_ut_create_buffer_object(((acpi_size) | 
 | 							    operand0->buffer. | 
 | 							    length + | 
 | 							    local_operand1-> | 
 | 							    buffer.length)); | 
 | 		if (!return_desc) { | 
 | 			status = AE_NO_MEMORY; | 
 | 			goto cleanup; | 
 | 		} | 
 |  | 
 | 		buffer = (char *)return_desc->buffer.pointer; | 
 |  | 
 | 		/* Concatenate the buffers */ | 
 |  | 
 | 		memcpy(buffer, operand0->buffer.pointer, | 
 | 		       operand0->buffer.length); | 
 | 		memcpy(buffer + operand0->buffer.length, | 
 | 		       local_operand1->buffer.pointer, | 
 | 		       local_operand1->buffer.length); | 
 | 		break; | 
 |  | 
 | 	default: | 
 |  | 
 | 		/* Invalid object type, should not happen here */ | 
 |  | 
 | 		ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X", | 
 | 			    operand0->common.type)); | 
 | 		status = AE_AML_INTERNAL; | 
 | 		goto cleanup; | 
 | 	} | 
 |  | 
 | 	*actual_return_desc = return_desc; | 
 |  | 
 | cleanup: | 
 | 	if (local_operand0 != operand0) { | 
 | 		acpi_ut_remove_reference(local_operand0); | 
 | 	} | 
 |  | 
 | 	if (local_operand1 != operand1) { | 
 | 		acpi_ut_remove_reference(local_operand1); | 
 | 	} | 
 |  | 
 | 	return_ACPI_STATUS(status); | 
 | } | 
 |  | 
 | /******************************************************************************* | 
 |  * | 
 |  * FUNCTION:    acpi_ex_convert_to_object_type_string | 
 |  * | 
 |  * PARAMETERS:  obj_desc            - Object to be converted | 
 |  *              return_desc         - Where to place the return object | 
 |  * | 
 |  * RETURN:      Status | 
 |  * | 
 |  * DESCRIPTION: Convert an object of arbitrary type to a string object that | 
 |  *              contains the namestring for the object. Used for the | 
 |  *              concatenate operator. | 
 |  * | 
 |  ******************************************************************************/ | 
 |  | 
 | static acpi_status | 
 | acpi_ex_convert_to_object_type_string(union acpi_operand_object *obj_desc, | 
 | 				      union acpi_operand_object **result_desc) | 
 | { | 
 | 	union acpi_operand_object *return_desc; | 
 | 	const char *type_string; | 
 |  | 
 | 	type_string = acpi_ut_get_type_name(obj_desc->common.type); | 
 |  | 
 | 	return_desc = acpi_ut_create_string_object(((acpi_size)strlen(type_string) + 9));	/* 9 For "[ Object]" */ | 
 | 	if (!return_desc) { | 
 | 		return (AE_NO_MEMORY); | 
 | 	} | 
 |  | 
 | 	strcpy(return_desc->string.pointer, "["); | 
 | 	strcat(return_desc->string.pointer, type_string); | 
 | 	strcat(return_desc->string.pointer, " Object]"); | 
 |  | 
 | 	*result_desc = return_desc; | 
 | 	return (AE_OK); | 
 | } | 
 |  | 
 | /******************************************************************************* | 
 |  * | 
 |  * FUNCTION:    acpi_ex_concat_template | 
 |  * | 
 |  * PARAMETERS:  operand0            - First source object | 
 |  *              operand1            - Second source object | 
 |  *              actual_return_desc  - Where to place the return object | 
 |  *              walk_state          - Current walk state | 
 |  * | 
 |  * RETURN:      Status | 
 |  * | 
 |  * DESCRIPTION: Concatenate two resource templates | 
 |  * | 
 |  ******************************************************************************/ | 
 |  | 
 | acpi_status | 
 | acpi_ex_concat_template(union acpi_operand_object *operand0, | 
 | 			union acpi_operand_object *operand1, | 
 | 			union acpi_operand_object **actual_return_desc, | 
 | 			struct acpi_walk_state *walk_state) | 
 | { | 
 | 	acpi_status status; | 
 | 	union acpi_operand_object *return_desc; | 
 | 	u8 *new_buf; | 
 | 	u8 *end_tag; | 
 | 	acpi_size length0; | 
 | 	acpi_size length1; | 
 | 	acpi_size new_length; | 
 |  | 
 | 	ACPI_FUNCTION_TRACE(ex_concat_template); | 
 |  | 
 | 	/* | 
 | 	 * Find the end_tag descriptor in each resource template. | 
 | 	 * Note1: returned pointers point TO the end_tag, not past it. | 
 | 	 * Note2: zero-length buffers are allowed; treated like one end_tag | 
 | 	 */ | 
 |  | 
 | 	/* Get the length of the first resource template */ | 
 |  | 
 | 	status = acpi_ut_get_resource_end_tag(operand0, &end_tag); | 
 | 	if (ACPI_FAILURE(status)) { | 
 | 		return_ACPI_STATUS(status); | 
 | 	} | 
 |  | 
 | 	length0 = ACPI_PTR_DIFF(end_tag, operand0->buffer.pointer); | 
 |  | 
 | 	/* Get the length of the second resource template */ | 
 |  | 
 | 	status = acpi_ut_get_resource_end_tag(operand1, &end_tag); | 
 | 	if (ACPI_FAILURE(status)) { | 
 | 		return_ACPI_STATUS(status); | 
 | 	} | 
 |  | 
 | 	length1 = ACPI_PTR_DIFF(end_tag, operand1->buffer.pointer); | 
 |  | 
 | 	/* Combine both lengths, minimum size will be 2 for end_tag */ | 
 |  | 
 | 	new_length = length0 + length1 + sizeof(struct aml_resource_end_tag); | 
 |  | 
 | 	/* Create a new buffer object for the result (with one end_tag) */ | 
 |  | 
 | 	return_desc = acpi_ut_create_buffer_object(new_length); | 
 | 	if (!return_desc) { | 
 | 		return_ACPI_STATUS(AE_NO_MEMORY); | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Copy the templates to the new buffer, 0 first, then 1 follows. One | 
 | 	 * end_tag descriptor is copied from Operand1. | 
 | 	 */ | 
 | 	new_buf = return_desc->buffer.pointer; | 
 | 	memcpy(new_buf, operand0->buffer.pointer, length0); | 
 | 	memcpy(new_buf + length0, operand1->buffer.pointer, length1); | 
 |  | 
 | 	/* Insert end_tag and set the checksum to zero, means "ignore checksum" */ | 
 |  | 
 | 	new_buf[new_length - 1] = 0; | 
 | 	new_buf[new_length - 2] = ACPI_RESOURCE_NAME_END_TAG | 1; | 
 |  | 
 | 	/* Return the completed resource template */ | 
 |  | 
 | 	*actual_return_desc = return_desc; | 
 | 	return_ACPI_STATUS(AE_OK); | 
 | } |