root/drivers/acpi/acpica/exconvrt.c
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
 *
 * Module Name: exconvrt - Object conversion routines
 *
 * Copyright (C) 2000 - 2025, Intel Corp.
 *
 *****************************************************************************/

#include <acpi/acpi.h>
#include "accommon.h"
#include "acinterp.h"
#include "amlcode.h"

#define _COMPONENT          ACPI_EXECUTER
ACPI_MODULE_NAME("exconvrt")

/* Local prototypes */
static u32
acpi_ex_convert_to_ascii(u64 integer,
                         u16 base, u8 *string, u8 max_length, u8 leading_zeros);

/*******************************************************************************
 *
 * FUNCTION:    acpi_ex_convert_to_integer
 *
 * PARAMETERS:  obj_desc            - Object to be converted. Must be an
 *                                    Integer, Buffer, or String
 *              result_desc         - Where the new Integer object is returned
 *              implicit_conversion - Used for string conversion
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Convert an ACPI Object to an integer.
 *
 ******************************************************************************/

acpi_status
acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc,
                           union acpi_operand_object **result_desc,
                           u32 implicit_conversion)
{
        union acpi_operand_object *return_desc;
        u8 *pointer;
        u64 result;
        u32 i;
        u32 count;

        ACPI_FUNCTION_TRACE_PTR(ex_convert_to_integer, obj_desc);

        switch (obj_desc->common.type) {
        case ACPI_TYPE_INTEGER:

                /* No conversion necessary */

                *result_desc = obj_desc;
                return_ACPI_STATUS(AE_OK);

        case ACPI_TYPE_BUFFER:
        case ACPI_TYPE_STRING:

                /* Note: Takes advantage of common buffer/string fields */

                pointer = obj_desc->buffer.pointer;
                count = obj_desc->buffer.length;
                break;

        default:

                return_ACPI_STATUS(AE_TYPE);
        }

        /*
         * Convert the buffer/string to an integer. Note that both buffers and
         * strings are treated as raw data - we don't convert ascii to hex for
         * strings.
         *
         * There are two terminating conditions for the loop:
         * 1) The size of an integer has been reached, or
         * 2) The end of the buffer or string has been reached
         */
        result = 0;

        /* String conversion is different than Buffer conversion */

        switch (obj_desc->common.type) {
        case ACPI_TYPE_STRING:
                /*
                 * Convert string to an integer - for most cases, the string must be
                 * hexadecimal as per the ACPI specification. The only exception (as
                 * of ACPI 3.0) is that the to_integer() operator allows both decimal
                 * and hexadecimal strings (hex prefixed with "0x").
                 *
                 * Explicit conversion is used only by to_integer.
                 * All other string-to-integer conversions are implicit conversions.
                 */
                if (implicit_conversion) {
                        result =
                            acpi_ut_implicit_strtoul64(ACPI_CAST_PTR
                                                       (char, pointer));
                } else {
                        result =
                            acpi_ut_explicit_strtoul64(ACPI_CAST_PTR
                                                       (char, pointer));
                }
                break;

        case ACPI_TYPE_BUFFER:

                /* Check for zero-length buffer */

                if (!count) {
                        return_ACPI_STATUS(AE_AML_BUFFER_LIMIT);
                }

                /* Transfer no more than an integer's worth of data */

                if (count > acpi_gbl_integer_byte_width) {
                        count = acpi_gbl_integer_byte_width;
                }

                /*
                 * Convert buffer to an integer - we simply grab enough raw data
                 * from the buffer to fill an integer
                 */
                for (i = 0; i < count; i++) {
                        /*
                         * Get next byte and shift it into the Result.
                         * Little endian is used, meaning that the first byte of the buffer
                         * is the LSB of the integer
                         */
                        result |= (((u64) pointer[i]) << (i * 8));
                }
                break;

        default:

                /* No other types can get here */

                break;
        }

        /* Create a new integer */

        return_desc = acpi_ut_create_integer_object(result);
        if (!return_desc) {
                return_ACPI_STATUS(AE_NO_MEMORY);
        }

        ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Converted value: %8.8X%8.8X\n",
                          ACPI_FORMAT_UINT64(result)));

        /* Save the Result */

        (void)acpi_ex_truncate_for32bit_table(return_desc);
        *result_desc = return_desc;
        return_ACPI_STATUS(AE_OK);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ex_convert_to_buffer
 *
 * PARAMETERS:  obj_desc        - Object to be converted. Must be an
 *                                Integer, Buffer, or String
 *              result_desc     - Where the new buffer object is returned
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Convert an ACPI Object to a Buffer
 *
 ******************************************************************************/

acpi_status
acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc,
                          union acpi_operand_object **result_desc)
{
        union acpi_operand_object *return_desc;
        u8 *new_buf;

        ACPI_FUNCTION_TRACE_PTR(ex_convert_to_buffer, obj_desc);

        switch (obj_desc->common.type) {
        case ACPI_TYPE_BUFFER:

                /* No conversion necessary */

                *result_desc = obj_desc;
                return_ACPI_STATUS(AE_OK);

        case ACPI_TYPE_INTEGER:
                /*
                 * Create a new Buffer object.
                 * Need enough space for one integer
                 */
                return_desc =
                    acpi_ut_create_buffer_object(acpi_gbl_integer_byte_width);
                if (!return_desc) {
                        return_ACPI_STATUS(AE_NO_MEMORY);
                }

                /* Copy the integer to the buffer, LSB first */

                new_buf = return_desc->buffer.pointer;
                memcpy(new_buf, &obj_desc->integer.value,
                       acpi_gbl_integer_byte_width);
                break;

        case ACPI_TYPE_STRING:
                /*
                 * Create a new Buffer object
                 * Size will be the string length
                 *
                 * NOTE: Add one to the string length to include the null terminator.
                 * The ACPI spec is unclear on this subject, but there is existing
                 * ASL/AML code that depends on the null being transferred to the new
                 * buffer.
                 */
                return_desc = acpi_ut_create_buffer_object((acpi_size)
                                                           obj_desc->string.
                                                           length + 1);
                if (!return_desc) {
                        return_ACPI_STATUS(AE_NO_MEMORY);
                }

                /* Copy the string to the buffer */

                new_buf = return_desc->buffer.pointer;
                memcpy((char *)new_buf, (char *)obj_desc->string.pointer,
                       obj_desc->string.length);
                break;

        default:

                return_ACPI_STATUS(AE_TYPE);
        }

        /* Mark buffer initialized */

        return_desc->common.flags |= AOPOBJ_DATA_VALID;
        *result_desc = return_desc;
        return_ACPI_STATUS(AE_OK);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ex_convert_to_ascii
 *
 * PARAMETERS:  integer         - Value to be converted
 *              base            - ACPI_STRING_DECIMAL or ACPI_STRING_HEX
 *              string          - Where the string is returned
 *              data_width      - Size of data item to be converted, in bytes
 *              leading_zeros   - Allow leading zeros
 *
 * RETURN:      Actual string length
 *
 * DESCRIPTION: Convert an ACPI Integer to a hex or decimal string
 *
 ******************************************************************************/

static u32
acpi_ex_convert_to_ascii(u64 integer,
                         u16 base, u8 *string, u8 data_width, u8 leading_zeros)
{
        u64 digit;
        u32 i;
        u32 j;
        u32 k = 0;
        u32 hex_length;
        u32 decimal_length;
        u32 remainder;
        u8 supress_zeros = !leading_zeros;
        u8 hex_char;

        ACPI_FUNCTION_ENTRY();

        switch (base) {
        case 10:

                /* Setup max length for the decimal number */

                switch (data_width) {
                case 1:

                        decimal_length = ACPI_MAX8_DECIMAL_DIGITS;
                        break;

                case 4:

                        decimal_length = ACPI_MAX32_DECIMAL_DIGITS;
                        break;

                case 8:
                default:

                        decimal_length = ACPI_MAX64_DECIMAL_DIGITS;
                        break;
                }

                remainder = 0;

                for (i = decimal_length; i > 0; i--) {

                        /* Divide by nth factor of 10 */

                        digit = integer;
                        for (j = 0; j < i; j++) {
                                (void)acpi_ut_short_divide(digit, 10, &digit,
                                                           &remainder);
                        }

                        /* Handle leading zeros */

                        if (remainder != 0) {
                                supress_zeros = FALSE;
                        }

                        if (!supress_zeros) {
                                string[k] = (u8) (ACPI_ASCII_ZERO + remainder);
                                k++;
                        }
                }
                break;

        case 16:

                /* hex_length: 2 ascii hex chars per data byte */

                hex_length = (data_width * 2);
                for (i = 0, j = (hex_length - 1); i < hex_length; i++, j--) {

                        /* Get one hex digit, most significant digits first */

                        hex_char = (u8)
                            acpi_ut_hex_to_ascii_char(integer, ACPI_MUL_4(j));

                        /* Supress leading zeros until the first non-zero character */

                        if (hex_char == ACPI_ASCII_ZERO && supress_zeros) {
                                continue;
                        }

                        supress_zeros = FALSE;
                        string[k] = hex_char;
                        k++;
                }
                break;

        default:
                return (0);
        }

        /*
         * Since leading zeros are suppressed, we must check for the case where
         * the integer equals 0
         *
         * Finally, null terminate the string and return the length
         */
        if (!k) {
                string[0] = ACPI_ASCII_ZERO;
                k = 1;
        }

        string[k] = 0;
        return ((u32) k);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ex_convert_to_string
 *
 * PARAMETERS:  obj_desc        - Object to be converted. Must be an
 *                                Integer, Buffer, or String
 *              result_desc     - Where the string object is returned
 *              type            - String flags (base and conversion type)
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Convert an ACPI Object to a string. Supports both implicit
 *              and explicit conversions and related rules.
 *
 ******************************************************************************/

acpi_status
acpi_ex_convert_to_string(union acpi_operand_object * obj_desc,
                          union acpi_operand_object ** result_desc, u32 type)
{
        union acpi_operand_object *return_desc;
        u8 *new_buf;
        u32 i;
        u32 string_length = 0;
        u16 base = 16;
        u8 separator = ',';
        u8 leading_zeros;

        ACPI_FUNCTION_TRACE_PTR(ex_convert_to_string, obj_desc);

        switch (obj_desc->common.type) {
        case ACPI_TYPE_STRING:

                /* No conversion necessary */

                *result_desc = obj_desc;
                return_ACPI_STATUS(AE_OK);

        case ACPI_TYPE_INTEGER:

                switch (type) {
                case ACPI_EXPLICIT_CONVERT_DECIMAL:
                        /*
                         * From to_decimal_string, integer source.
                         *
                         * Make room for the maximum decimal number size
                         */
                        string_length = ACPI_MAX_DECIMAL_DIGITS;
                        leading_zeros = FALSE;
                        base = 10;
                        break;

                case ACPI_EXPLICIT_CONVERT_HEX:
                        /*
                         * From to_hex_string.
                         *
                         * Supress leading zeros and append "0x"
                         */
                        string_length =
                            ACPI_MUL_2(acpi_gbl_integer_byte_width) + 2;
                        leading_zeros = FALSE;
                        break;
                default:

                        /* Two hex string characters for each integer byte */

                        string_length = ACPI_MUL_2(acpi_gbl_integer_byte_width);
                        leading_zeros = TRUE;
                        break;
                }

                /*
                 * Create a new String
                 * Need enough space for one ASCII integer (plus null terminator)
                 */
                return_desc =
                    acpi_ut_create_string_object((acpi_size)string_length);
                if (!return_desc) {
                        return_ACPI_STATUS(AE_NO_MEMORY);
                }

                new_buf = return_desc->buffer.pointer;
                if (type == ACPI_EXPLICIT_CONVERT_HEX) {

                        /* Append "0x" prefix for explicit hex conversion */

                        *new_buf++ = '0';
                        *new_buf++ = 'x';
                }

                /* Convert integer to string */

                string_length =
                    acpi_ex_convert_to_ascii(obj_desc->integer.value, base,
                                             new_buf,
                                             acpi_gbl_integer_byte_width,
                                             leading_zeros);

                /* Null terminate at the correct place */

                return_desc->string.length = string_length;
                if (type == ACPI_EXPLICIT_CONVERT_HEX) {

                        /* Take "0x" prefix into account */

                        return_desc->string.length += 2;
                }

                new_buf[string_length] = 0;
                break;

        case ACPI_TYPE_BUFFER:

                /* Setup string length, base, and separator */

                switch (type) {
                case ACPI_EXPLICIT_CONVERT_DECIMAL:     /* Used by to_decimal_string */
                        /*
                         * Explicit conversion from the to_decimal_string ASL operator.
                         *
                         * From ACPI: "If the input is a buffer, it is converted to a
                         * a string of decimal values separated by commas."
                         */
                        leading_zeros = FALSE;
                        base = 10;

                        /*
                         * Calculate the final string length. Individual string values
                         * are variable length (include separator for each)
                         */
                        for (i = 0; i < obj_desc->buffer.length; i++) {
                                if (obj_desc->buffer.pointer[i] >= 100) {
                                        string_length += 4;
                                } else if (obj_desc->buffer.pointer[i] >= 10) {
                                        string_length += 3;
                                } else {
                                        string_length += 2;
                                }
                        }
                        break;

                case ACPI_IMPLICIT_CONVERT_HEX:
                        /*
                         * Implicit buffer-to-string conversion
                         *
                         * From the ACPI spec:
                         * "The entire contents of the buffer are converted to a string of
                         * two-character hexadecimal numbers, each separated by a space."
                         *
                         * Each hex number is prefixed with 0x (11/2018)
                         */
                        leading_zeros = TRUE;
                        separator = ' ';
                        string_length = (obj_desc->buffer.length * 5);
                        break;

                case ACPI_EXPLICIT_CONVERT_HEX:
                        /*
                         * Explicit conversion from the to_hex_string ASL operator.
                         *
                         * From ACPI: "If Data is a buffer, it is converted to a string of
                         * hexadecimal values separated by commas."
                         *
                         * Each hex number is prefixed with 0x (11/2018)
                         */
                        leading_zeros = TRUE;
                        separator = ',';
                        string_length = (obj_desc->buffer.length * 5);
                        break;

                default:
                        return_ACPI_STATUS(AE_BAD_PARAMETER);
                }

                /*
                 * Create a new string object and string buffer
                 * (-1 because of extra separator included in string_length from above)
                 * Allow creation of zero-length strings from zero-length buffers.
                 */
                if (string_length) {
                        string_length--;
                }

                return_desc =
                    acpi_ut_create_string_object((acpi_size)string_length);
                if (!return_desc) {
                        return_ACPI_STATUS(AE_NO_MEMORY);
                }

                new_buf = return_desc->buffer.pointer;

                /*
                 * Convert buffer bytes to hex or decimal values
                 * (separated by commas or spaces)
                 */
                for (i = 0; i < obj_desc->buffer.length; i++) {
                        if (base == 16) {

                                /* Emit 0x prefix for explicit/implicit hex conversion */

                                *new_buf++ = '0';
                                *new_buf++ = 'x';
                        }

                        new_buf += acpi_ex_convert_to_ascii((u64) obj_desc->
                                                            buffer.pointer[i],
                                                            base, new_buf, 1,
                                                            leading_zeros);

                        /* Each digit is separated by either a comma or space */

                        *new_buf++ = separator;
                }

                /*
                 * Null terminate the string
                 * (overwrites final comma/space from above)
                 */
                if (obj_desc->buffer.length) {
                        new_buf--;
                }
                *new_buf = 0;
                break;

        default:

                return_ACPI_STATUS(AE_TYPE);
        }

        *result_desc = return_desc;
        return_ACPI_STATUS(AE_OK);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ex_convert_to_target_type
 *
 * PARAMETERS:  destination_type    - Current type of the destination
 *              source_desc         - Source object to be converted.
 *              result_desc         - Where the converted object is returned
 *              walk_state          - Current method state
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Implements "implicit conversion" rules for storing an object.
 *
 ******************************************************************************/

acpi_status
acpi_ex_convert_to_target_type(acpi_object_type destination_type,
                               union acpi_operand_object *source_desc,
                               union acpi_operand_object **result_desc,
                               struct acpi_walk_state *walk_state)
{
        acpi_status status = AE_OK;

        ACPI_FUNCTION_TRACE(ex_convert_to_target_type);

        /* Default behavior */

        *result_desc = source_desc;

        /*
         * If required by the target,
         * perform implicit conversion on the source before we store it.
         */
        switch (GET_CURRENT_ARG_TYPE(walk_state->op_info->runtime_args)) {
        case ARGI_SIMPLE_TARGET:
        case ARGI_FIXED_TARGET:
        case ARGI_INTEGER_REF:  /* Handles Increment, Decrement cases */

                switch (destination_type) {
                case ACPI_TYPE_LOCAL_REGION_FIELD:
                        /*
                         * Named field can always handle conversions
                         */
                        break;

                default:

                        /* No conversion allowed for these types */

                        if (destination_type != source_desc->common.type) {
                                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
                                                  "Explicit operator, will store (%s) over existing type (%s)\n",
                                                  acpi_ut_get_object_type_name
                                                  (source_desc),
                                                  acpi_ut_get_type_name
                                                  (destination_type)));
                                status = AE_TYPE;
                        }
                }
                break;

        case ARGI_TARGETREF:
        case ARGI_STORE_TARGET:

                switch (destination_type) {
                case ACPI_TYPE_INTEGER:
                case ACPI_TYPE_BUFFER_FIELD:
                case ACPI_TYPE_LOCAL_BANK_FIELD:
                case ACPI_TYPE_LOCAL_INDEX_FIELD:
                        /*
                         * These types require an Integer operand. We can convert
                         * a Buffer or a String to an Integer if necessary.
                         */
                        status =
                            acpi_ex_convert_to_integer(source_desc, result_desc,
                                                       ACPI_IMPLICIT_CONVERSION);
                        break;

                case ACPI_TYPE_STRING:
                        /*
                         * The operand must be a String. We can convert an
                         * Integer or Buffer if necessary
                         */
                        status =
                            acpi_ex_convert_to_string(source_desc, result_desc,
                                                      ACPI_IMPLICIT_CONVERT_HEX);
                        break;

                case ACPI_TYPE_BUFFER:
                        /*
                         * The operand must be a Buffer. We can convert an
                         * Integer or String if necessary
                         */
                        status =
                            acpi_ex_convert_to_buffer(source_desc, result_desc);
                        break;

                default:

                        ACPI_ERROR((AE_INFO,
                                    "Bad destination type during conversion: 0x%X",
                                    destination_type));
                        status = AE_AML_INTERNAL;
                        break;
                }
                break;

        case ARGI_REFERENCE:
                /*
                 * create_xxxx_field cases - we are storing the field object into the name
                 */
                break;

        default:

                ACPI_ERROR((AE_INFO,
                            "Unknown Target type ID 0x%X AmlOpcode 0x%X DestType %s",
                            GET_CURRENT_ARG_TYPE(walk_state->op_info->
                                                 runtime_args),
                            walk_state->opcode,
                            acpi_ut_get_type_name(destination_type)));
                status = AE_AML_INTERNAL;
        }

        /*
         * Source-to-Target conversion semantics:
         *
         * If conversion to the target type cannot be performed, then simply
         * overwrite the target with the new object and type.
         */
        if (status == AE_TYPE) {
                status = AE_OK;
        }

        return_ACPI_STATUS(status);
}