406 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			406 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
 | 
						|
/******************************************************************************
 | 
						|
 *
 | 
						|
 * Module Name: exconcat - Concatenate-type AML operators
 | 
						|
 *
 | 
						|
 * Copyright (C) 2000 - 2022, 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);
 | 
						|
}
 |