root/drivers/acpi/acpica/exconcat.c
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
 *
 * Module Name: exconcat - Concatenate-type AML operators
 *
 * Copyright (C) 2000 - 2025, 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);
}