root/tools/power/acpi/tools/acpidump/apdump.c
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
 *
 * Module Name: apdump - Dump routines for ACPI tables (acpidump)
 *
 * Copyright (C) 2000 - 2025, Intel Corp.
 *
 *****************************************************************************/

#include "acpidump.h"

/* Local prototypes */

static int
ap_dump_table_buffer(struct acpi_table_header *table,
                     u32 instance, acpi_physical_address address);

/******************************************************************************
 *
 * FUNCTION:    ap_is_valid_header
 *
 * PARAMETERS:  table               - Pointer to table to be validated
 *
 * RETURN:      TRUE if the header appears to be valid. FALSE otherwise
 *
 * DESCRIPTION: Check for a valid ACPI table header
 *
 ******************************************************************************/

u8 ap_is_valid_header(struct acpi_table_header *table)
{

        if (!ACPI_VALIDATE_RSDP_SIG(table->signature)) {

                /* Make sure signature is all ASCII and a valid ACPI name */

                if (!acpi_ut_valid_nameseg(table->signature)) {
                        fprintf(stderr,
                                "Table signature (0x%8.8X) is invalid\n",
                                *(u32 *)table->signature);
                        return (FALSE);
                }

                /* Check for minimum table length */

                if (table->length < sizeof(struct acpi_table_header)) {
                        fprintf(stderr, "Table length (0x%8.8X) is invalid\n",
                                table->length);
                        return (FALSE);
                }
        }

        return (TRUE);
}

/******************************************************************************
 *
 * FUNCTION:    ap_is_valid_checksum
 *
 * PARAMETERS:  table               - Pointer to table to be validated
 *
 * RETURN:      TRUE if the checksum appears to be valid. FALSE otherwise.
 *
 * DESCRIPTION: Check for a valid ACPI table checksum.
 *
 ******************************************************************************/

u8 ap_is_valid_checksum(struct acpi_table_header *table)
{
        acpi_status status;
        struct acpi_table_rsdp *rsdp;

        if (ACPI_VALIDATE_RSDP_SIG(table->signature)) {
                /*
                 * Checksum for RSDP.
                 * Note: Other checksums are computed during the table dump.
                 */
                rsdp = ACPI_CAST_PTR(struct acpi_table_rsdp, table);
                status = acpi_tb_validate_rsdp(rsdp);
        } else {
                /* We don't have to check for a CDAT here, since CDAT is not in the RSDT/XSDT */

                status = acpi_ut_verify_checksum(table, table->length);
        }

        if (ACPI_FAILURE(status)) {
                fprintf(stderr, "%4.4s: Warning: wrong checksum in table\n",
                        table->signature);
                return (FALSE);
        }

        return (TRUE);
}

/******************************************************************************
 *
 * FUNCTION:    ap_get_table_length
 *
 * PARAMETERS:  table               - Pointer to the table
 *
 * RETURN:      Table length
 *
 * DESCRIPTION: Obtain table length according to table signature.
 *
 ******************************************************************************/

u32 ap_get_table_length(struct acpi_table_header *table)
{
        struct acpi_table_rsdp *rsdp;

        /* Check if table is valid */

        if (!ap_is_valid_header(table)) {
                return (0);
        }

        if (ACPI_VALIDATE_RSDP_SIG(table->signature)) {
                rsdp = ACPI_CAST_PTR(struct acpi_table_rsdp, table);
                return (acpi_tb_get_rsdp_length(rsdp));
        }

        /* Normal ACPI table */

        return (table->length);
}

/******************************************************************************
 *
 * FUNCTION:    ap_dump_table_buffer
 *
 * PARAMETERS:  table               - ACPI table to be dumped
 *              instance            - ACPI table instance no. to be dumped
 *              address             - Physical address of the table
 *
 * RETURN:      None
 *
 * DESCRIPTION: Dump an ACPI table in standard ASCII hex format, with a
 *              header that is compatible with the acpi_xtract utility.
 *
 ******************************************************************************/

static int
ap_dump_table_buffer(struct acpi_table_header *table,
                     u32 instance, acpi_physical_address address)
{
        u32 table_length;

        table_length = ap_get_table_length(table);

        /* Print only the header if requested */

        if (gbl_summary_mode) {
                acpi_tb_print_table_header(address, table);
                return (0);
        }

        /* Dump to binary file if requested */

        if (gbl_binary_mode) {
                return (ap_write_to_binary_file(table, instance));
        }

        /*
         * Dump the table with header for use with acpixtract utility.
         * Note: simplest to just always emit a 64-bit address. acpi_xtract
         * utility can handle this.
         */
        fprintf(gbl_output_file, "%4.4s @ 0x%8.8X%8.8X\n",
                table->signature, ACPI_FORMAT_UINT64(address));

        acpi_ut_dump_buffer_to_file(gbl_output_file,
                                    ACPI_CAST_PTR(u8, table), table_length,
                                    DB_BYTE_DISPLAY, 0);
        fprintf(gbl_output_file, "\n");
        return (0);
}

/******************************************************************************
 *
 * FUNCTION:    ap_dump_all_tables
 *
 * PARAMETERS:  None
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Get all tables from the RSDT/XSDT (or at least all of the
 *              tables that we can possibly get).
 *
 ******************************************************************************/

int ap_dump_all_tables(void)
{
        struct acpi_table_header *table;
        u32 instance = 0;
        acpi_physical_address address;
        acpi_status status;
        int table_status;
        u32 i;

        /* Get and dump all available ACPI tables */

        for (i = 0; i < AP_MAX_ACPI_FILES; i++) {
                status =
                    acpi_os_get_table_by_index(i, &table, &instance, &address);
                if (ACPI_FAILURE(status)) {

                        /* AE_LIMIT means that no more tables are available */

                        if (status == AE_LIMIT) {
                                return (0);
                        } else if (i == 0) {
                                fprintf(stderr,
                                        "Could not get ACPI tables, %s\n",
                                        acpi_format_exception(status));
                                return (-1);
                        } else {
                                fprintf(stderr,
                                        "Could not get ACPI table at index %u, %s\n",
                                        i, acpi_format_exception(status));
                                continue;
                        }
                }

                table_status = ap_dump_table_buffer(table, instance, address);
                ACPI_FREE(table);

                if (table_status) {
                        break;
                }
        }

        /* Something seriously bad happened if the loop terminates here */

        return (-1);
}

/******************************************************************************
 *
 * FUNCTION:    ap_dump_table_by_address
 *
 * PARAMETERS:  ascii_address       - Address for requested ACPI table
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Get an ACPI table via a physical address and dump it.
 *
 ******************************************************************************/

int ap_dump_table_by_address(char *ascii_address)
{
        acpi_physical_address address;
        struct acpi_table_header *table;
        acpi_status status;
        int table_status;
        u64 long_address;

        /* Convert argument to an integer physical address */

        status = acpi_ut_strtoul64(ascii_address, &long_address);
        if (ACPI_FAILURE(status)) {
                fprintf(stderr, "%s: Could not convert to a physical address\n",
                        ascii_address);
                return (-1);
        }

        address = (acpi_physical_address)long_address;
        status = acpi_os_get_table_by_address(address, &table);
        if (ACPI_FAILURE(status)) {
                fprintf(stderr, "Could not get table at 0x%8.8X%8.8X, %s\n",
                        ACPI_FORMAT_UINT64(address),
                        acpi_format_exception(status));
                return (-1);
        }

        table_status = ap_dump_table_buffer(table, 0, address);
        ACPI_FREE(table);
        return (table_status);
}

/******************************************************************************
 *
 * FUNCTION:    ap_dump_table_by_name
 *
 * PARAMETERS:  signature           - Requested ACPI table signature
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Get an ACPI table via a signature and dump it. Handles
 *              multiple tables with the same signature (SSDTs).
 *
 ******************************************************************************/

int ap_dump_table_by_name(char *signature)
{
        char local_signature[ACPI_NAMESEG_SIZE + 1];
        u32 instance;
        struct acpi_table_header *table;
        acpi_physical_address address;
        acpi_status status;
        int table_status;

        if (strlen(signature) != ACPI_NAMESEG_SIZE) {
                fprintf(stderr,
                        "Invalid table signature [%s]: must be exactly 4 characters\n",
                        signature);
                return (-1);
        }

        /* Table signatures are expected to be uppercase */

        strcpy(local_signature, signature);
        acpi_ut_strupr(local_signature);

        /* To be friendly, handle tables whose signatures do not match the name */

        if (ACPI_COMPARE_NAMESEG(local_signature, "FADT")) {
                strcpy(local_signature, ACPI_SIG_FADT);
        } else if (ACPI_COMPARE_NAMESEG(local_signature, "MADT")) {
                strcpy(local_signature, ACPI_SIG_MADT);
        }

        /* Dump all instances of this signature (to handle multiple SSDTs) */

        for (instance = 0; instance < AP_MAX_ACPI_FILES; instance++) {
                status = acpi_os_get_table_by_name(local_signature, instance,
                                                   &table, &address);
                if (ACPI_FAILURE(status)) {

                        /* AE_LIMIT means that no more tables are available */

                        if (status == AE_LIMIT) {
                                return (0);
                        }

                        fprintf(stderr,
                                "Could not get ACPI table with signature [%s], %s\n",
                                local_signature, acpi_format_exception(status));
                        return (-1);
                }

                table_status = ap_dump_table_buffer(table, instance, address);
                ACPI_FREE(table);

                if (table_status) {
                        break;
                }
        }

        /* Something seriously bad happened if the loop terminates here */

        return (-1);
}

/******************************************************************************
 *
 * FUNCTION:    ap_dump_table_from_file
 *
 * PARAMETERS:  pathname            - File containing the binary ACPI table
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Dump an ACPI table from a binary file
 *
 ******************************************************************************/

int ap_dump_table_from_file(char *pathname)
{
        struct acpi_table_header *table;
        u32 file_size = 0;
        int table_status = -1;

        /* Get the entire ACPI table from the file */

        table = ap_get_table_from_file(pathname, &file_size);
        if (!table) {
                return (-1);
        }

        if (!acpi_ut_valid_nameseg(table->signature)) {
                fprintf(stderr,
                        "No valid ACPI signature was found in input file %s\n",
                        pathname);
        }

        /* File must be at least as long as the table length */

        if (table->length > file_size) {
                fprintf(stderr,
                        "Table length (0x%X) is too large for input file (0x%X) %s\n",
                        table->length, file_size, pathname);
                goto exit;
        }

        if (gbl_verbose_mode) {
                fprintf(stderr,
                        "Input file:  %s contains table [%4.4s], 0x%X (%u) bytes\n",
                        pathname, table->signature, file_size, file_size);
        }

        table_status = ap_dump_table_buffer(table, 0, 0);

exit:
        ACPI_FREE(table);
        return (table_status);
}