/******************************************************************************
 *
 * Module Name: aetables - ACPI table setup/install for acpiexec utility
 *
 *****************************************************************************/

/*
 * Copyright (C) 2000 - 2019, Intel Corp.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification.
 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
 *    substantially similar to the "NO WARRANTY" disclaimer below
 *    ("Disclaimer") and any redistribution must be conditioned upon
 *    including a substantially similar Disclaimer requirement for further
 *    binary redistribution.
 * 3. Neither the names of the above-listed copyright holders nor the names
 *    of any contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2 as published by the Free
 * Software Foundation.
 *
 * NO WARRANTY
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 */

#include "aecommon.h"
#include "aetables.h"

#define _COMPONENT          ACPI_TOOLS
        ACPI_MODULE_NAME    ("aetables")

/* Local prototypes */

static void
AeInitializeTableHeader (
    ACPI_TABLE_HEADER       *Header,
    char                    *Signature,
    UINT32                  Length);

void
AeTableOverride (
    ACPI_TABLE_HEADER       *ExistingTable,
    ACPI_TABLE_HEADER       **NewTable);

/* User table (DSDT) */

static ACPI_TABLE_HEADER        *DsdtToInstallOverride;

/* Non-AML tables that are constructed locally and installed */

static ACPI_TABLE_RSDP          LocalRSDP;
static ACPI_TABLE_FACS          LocalFACS;
static ACPI_TABLE_HEADER        LocalTEST;
static ACPI_TABLE_HEADER        LocalBADTABLE;

/*
 * We need a local FADT so that the hardware subcomponent will function,
 * even though the underlying OSD HW access functions don't do anything.
 */
static ACPI_TABLE_FADT          LocalFADT;

/*
 * Use XSDT so that both 32- and 64-bit versions of this utility will
 * function automatically.
 */
static ACPI_TABLE_XSDT          *LocalXSDT;

#define BASE_XSDT_TABLES        9
#define BASE_XSDT_SIZE          ((BASE_XSDT_TABLES) * sizeof (UINT64))

#define ACPI_MAX_INIT_TABLES    (32)


/******************************************************************************
 *
 * FUNCTION:    AeTableOverride
 *
 * DESCRIPTION: Local implementation of AcpiOsTableOverride.
 *              Exercise the override mechanism
 *
 *****************************************************************************/

void
AeTableOverride (
    ACPI_TABLE_HEADER       *ExistingTable,
    ACPI_TABLE_HEADER       **NewTable)
{

    if (!AcpiGbl_LoadTestTables)
    {
        *NewTable = NULL;
        return;
    }

    /* This code exercises the table override mechanism in the core */

    if (ACPI_COMPARE_NAMESEG (ExistingTable->Signature, ACPI_SIG_DSDT))
    {
        *NewTable = DsdtToInstallOverride;
    }

    /* This code tests override of dynamically loaded tables */

    else if (ACPI_COMPARE_NAMESEG (ExistingTable->Signature, "OEM9"))
    {
        *NewTable = ACPI_CAST_PTR (ACPI_TABLE_HEADER, Ssdt3Code);
    }
}


/******************************************************************************
 *
 * FUNCTION:    AeInitializeTableHeader
 *
 * PARAMETERS:  Header          - A valid standard ACPI table header
 *              Signature       - Signature to insert
 *              Length          - Length of the table
 *
 * RETURN:      None. Header is modified.
 *
 * DESCRIPTION: Initialize the table header for a local ACPI table.
 *
 *****************************************************************************/

static void
AeInitializeTableHeader (
    ACPI_TABLE_HEADER       *Header,
    char                    *Signature,
    UINT32                  Length)
{
    UINT16		    Tmp16;
    UINT32		    Tmp32;

    ACPI_COPY_NAMESEG (Header->Signature, Signature);
    ACPI_MOVE_32_TO_32(&Header->Length, &Length);

    Tmp16 = 0x1001;
    ACPI_MOVE_16_TO_16(&Header->OemRevision, &Tmp16);
    memcpy (Header->OemId, "Intel ", ACPI_OEM_ID_SIZE);
    memcpy (Header->OemTableId, "AcpiExec", ACPI_OEM_TABLE_ID_SIZE);
    ACPI_COPY_NAMESEG (Header->AslCompilerId, "INTL");
    Tmp32 = ACPI_CA_VERSION;
    ACPI_MOVE_32_TO_32(&Header->AslCompilerRevision, &Tmp32);

    /* Set the checksum, must set to zero first */

    Header->Checksum = 0;
    Header->Checksum = (UINT8) -AcpiTbChecksum (
        (void *) Header, Length);
}


/******************************************************************************
 *
 * FUNCTION:    AeBuildLocalTables
 *
 * PARAMETERS:  TableCount      - Number of tables on the command line
 *              ListHead        - List of actual tables from files
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Build a complete ACPI table chain, with a local RSDP, XSDT,
 *              FADT, and several other test tables.
 *
 *****************************************************************************/

ACPI_STATUS
AeBuildLocalTables (
    ACPI_NEW_TABLE_DESC     *ListHead)
{
    UINT32                  TableCount = 1;
    ACPI_PHYSICAL_ADDRESS   DsdtAddress = 0;
    UINT32                  XsdtSize;
    ACPI_NEW_TABLE_DESC     *NextTable;
    UINT32                  NextIndex;
    ACPI_TABLE_FADT         *ExternalFadt = NULL;
    UINT32		    Tmp32;


    /*
     * Update the table count. For the DSDT, it is not put into the XSDT.
     * For the FADT, this table is already accounted for since we usually
     * install a local FADT.
     */
    NextTable = ListHead;
    while (NextTable)
    {
        if (!ACPI_COMPARE_NAMESEG (NextTable->Table->Signature, ACPI_SIG_DSDT) &&
            !ACPI_COMPARE_NAMESEG (NextTable->Table->Signature, ACPI_SIG_FADT))
        {
            TableCount++;
        }

        NextTable = NextTable->Next;
    }

    XsdtSize = (((TableCount + 1) * sizeof (UINT64)) +
        sizeof (ACPI_TABLE_HEADER));
    if (AcpiGbl_LoadTestTables)
    {
        XsdtSize += BASE_XSDT_SIZE;
    }

    /* Build an XSDT */

    LocalXSDT = AcpiOsAllocate (XsdtSize);
    if (!LocalXSDT)
    {
        return (AE_NO_MEMORY);
    }

    memset (LocalXSDT, 0, XsdtSize);
    LocalXSDT->TableOffsetEntry[0] = ACPI_PTR_TO_PHYSADDR (&LocalFADT);
    NextIndex = 1;

    /*
     * Install the user tables. The DSDT must be installed in the FADT.
     * All other tables are installed directly into the XSDT.
     */
    NextTable = ListHead;
    while (NextTable)
    {
        /*
         * Incoming DSDT or FADT are special cases. All other tables are
         * just immediately installed into the XSDT.
         */
        if (ACPI_COMPARE_NAMESEG (NextTable->Table->Signature, ACPI_SIG_DSDT))
        {
            if (DsdtAddress)
            {
                printf ("Already found a DSDT, only one allowed\n");
                return (AE_ALREADY_EXISTS);
            }

            /* The incoming user table is a DSDT */

            DsdtAddress = ACPI_PTR_TO_PHYSADDR (NextTable->Table);
            DsdtToInstallOverride = NextTable->Table;
        }
        else if (ACPI_COMPARE_NAMESEG (NextTable->Table->Signature, ACPI_SIG_FADT))
        {
            ExternalFadt = ACPI_CAST_PTR (ACPI_TABLE_FADT, NextTable->Table);
            LocalXSDT->TableOffsetEntry[0] = ACPI_PTR_TO_PHYSADDR (NextTable->Table);
        }
        else
        {
            /* Install the table in the XSDT */

            LocalXSDT->TableOffsetEntry[NextIndex] =
                ACPI_PTR_TO_PHYSADDR (NextTable->Table);
            NextIndex++;
        }

        NextTable = NextTable->Next;
    }

    /* Install the optional extra local tables */

    if (AcpiGbl_LoadTestTables)
    {
        LocalXSDT->TableOffsetEntry[NextIndex++] = ACPI_PTR_TO_PHYSADDR (&LocalTEST);
        LocalXSDT->TableOffsetEntry[NextIndex++] = ACPI_PTR_TO_PHYSADDR (&LocalBADTABLE);

        /* Install two SSDTs to test multiple table support */

        LocalXSDT->TableOffsetEntry[NextIndex++] = ACPI_PTR_TO_PHYSADDR (&Ssdt1Code);
        LocalXSDT->TableOffsetEntry[NextIndex++] = ACPI_PTR_TO_PHYSADDR (&Ssdt2Code);

        /* Install the OEM1 table to test LoadTable */

        LocalXSDT->TableOffsetEntry[NextIndex++] = ACPI_PTR_TO_PHYSADDR (&Oem1Code);

        /* Install the OEMx table to test LoadTable */

        LocalXSDT->TableOffsetEntry[NextIndex++] = ACPI_PTR_TO_PHYSADDR (&OemxCode);

         /* Install the ECDT table to test _REG */

        LocalXSDT->TableOffsetEntry[NextIndex++] = ACPI_PTR_TO_PHYSADDR (&EcdtCode);

        /* Install two UEFIs to test multiple table support */

        LocalXSDT->TableOffsetEntry[NextIndex++] = ACPI_PTR_TO_PHYSADDR (&Uefi1Code);
        LocalXSDT->TableOffsetEntry[NextIndex++] = ACPI_PTR_TO_PHYSADDR (&Uefi2Code);
    }

    /* Build an RSDP. Contains a valid XSDT only, no RSDT */

    memset (&LocalRSDP, 0, sizeof (ACPI_TABLE_RSDP));
    ACPI_MAKE_RSDP_SIG (LocalRSDP.Signature);
    memcpy (LocalRSDP.OemId, "Intel", 6);

    LocalRSDP.Revision = 2;
    LocalRSDP.XsdtPhysicalAddress = ACPI_PTR_TO_PHYSADDR (LocalXSDT);
    LocalRSDP.Length = sizeof (ACPI_TABLE_RSDP);

    /* Set checksums for both XSDT and RSDP */

    AeInitializeTableHeader ((void *) LocalXSDT, ACPI_SIG_XSDT, XsdtSize);

    LocalRSDP.Checksum = 0;
    LocalRSDP.Checksum = (UINT8) -AcpiTbChecksum (
        (void *) &LocalRSDP, ACPI_RSDP_CHECKSUM_LENGTH);

    if (!DsdtAddress)
    {
        /* Use the local DSDT because incoming table(s) are all SSDT(s) */

        DsdtAddress = ACPI_PTR_TO_PHYSADDR (LocalDsdtCode);
        DsdtToInstallOverride = ACPI_CAST_PTR (ACPI_TABLE_HEADER, LocalDsdtCode);
    }

    /*
     * Build an FADT. There are three options for the FADT:
     * 1) Incoming external FADT specified on the command line
     * 2) A "hardware reduced" local FADT
     * 3) A fully featured local FADT
     */
    memset (&LocalFADT, 0, sizeof (ACPI_TABLE_FADT));

    if (ExternalFadt)
    {
        /*
         * Use the external FADT, but we must update the DSDT/FACS
         * addresses as well as the checksum
         */
        ExternalFadt->Dsdt = (UINT32) DsdtAddress;
        if (!AcpiGbl_ReducedHardware)
        {
            ExternalFadt->Facs = ACPI_PTR_TO_PHYSADDR (&LocalFACS);
        }

        /*
         * If there room in the FADT for the XDsdt and XFacs 64-bit
         * pointers, use them.
         */
        if (ExternalFadt->Header.Length > ACPI_PTR_DIFF (
            &ExternalFadt->XDsdt, ExternalFadt))
        {
            ExternalFadt->Dsdt = 0;
            ExternalFadt->Facs = 0;

            ExternalFadt->XDsdt = DsdtAddress;
            if (!AcpiGbl_ReducedHardware)
            {
                ExternalFadt->XFacs = ACPI_PTR_TO_PHYSADDR (&LocalFACS);
            }
        }

        /* Complete the external FADT with the checksum */

        ExternalFadt->Header.Checksum = 0;
        ExternalFadt->Header.Checksum = (UINT8) -AcpiTbChecksum (
            (void *) ExternalFadt, ExternalFadt->Header.Length);
    }
    else if (AcpiGbl_UseHwReducedFadt)
    {
        memcpy (&LocalFADT, HwReducedFadtCode, ACPI_FADT_V5_SIZE);
        LocalFADT.Dsdt = 0;
        LocalFADT.XDsdt = DsdtAddress;
    }
    else
    {
        UINT64		    Tmp64;

        /*
         * Build a local FADT so we can test the hardware/event init
         */
        LocalFADT.Header.Revision = 5;

        /* Setup FADT header and DSDT/FACS addresses */

        LocalFADT.Dsdt = 0;
        LocalFADT.Facs = 0;

        LocalFADT.XDsdt = DsdtAddress;
        Tmp64 = ACPI_PTR_TO_PHYSADDR (&LocalFACS);
        ACPI_MOVE_64_TO_64(&LocalFADT.XFacs, &Tmp64);

        /* Miscellaneous FADT fields */

        LocalFADT.Gpe0BlockLength = 0x20;
        Tmp32 = 0x00003210;
        ACPI_MOVE_32_TO_32(&LocalFADT.Gpe0Block, &Tmp32);

        LocalFADT.Gpe1BlockLength = 0x20;
        Tmp32 = 0x0000BA98;
        ACPI_MOVE_32_TO_32(&LocalFADT.Gpe1Block, &Tmp32);
        LocalFADT.Gpe1Base = 0x80;

        LocalFADT.Pm1EventLength = 4;
        Tmp32 = 0x00001aaa;
        ACPI_MOVE_32_TO_32(&LocalFADT.Pm1aEventBlock, &Tmp32);
        Tmp32 = 0x00001bbb;
        ACPI_MOVE_32_TO_32(&LocalFADT.Pm1bEventBlock, &Tmp32);

        LocalFADT.Pm1ControlLength = 2;
        Tmp32 = 0xB0;
        ACPI_MOVE_32_TO_32(&LocalFADT.Pm1aControlBlock, &Tmp32);

        LocalFADT.PmTimerLength = 4;
        Tmp32 = 0xA0;
        ACPI_MOVE_32_TO_32(&LocalFADT.PmTimerBlock, &Tmp32);

        Tmp32 = 0xC0;
        ACPI_MOVE_32_TO_32(&LocalFADT.Pm2ControlBlock, &Tmp32);
        LocalFADT.Pm2ControlLength = 1;

        /* Setup one example X-64 GAS field */

        LocalFADT.XPm1bEventBlock.SpaceId = ACPI_ADR_SPACE_SYSTEM_IO;
	ACPI_MOVE_32_TO_32(&Tmp32, &LocalFADT.Pm1bEventBlock);
	Tmp64 = (UINT64)Tmp32;
        ACPI_MOVE_64_TO_64(&LocalFADT.XPm1bEventBlock.Address, &Tmp64);
        LocalFADT.XPm1bEventBlock.BitWidth = (UINT8)
            ACPI_MUL_8 (LocalFADT.Pm1EventLength);
    }

    AeInitializeTableHeader ((void *) &LocalFADT,
        ACPI_SIG_FADT, sizeof (ACPI_TABLE_FADT));

    /* Build a FACS */

    memset (&LocalFACS, 0, sizeof (ACPI_TABLE_FACS));
    ACPI_COPY_NAMESEG (LocalFACS.Signature, ACPI_SIG_FACS);

    Tmp32 = sizeof (ACPI_TABLE_FACS);
    ACPI_MOVE_32_TO_32(&LocalFACS.Length, &Tmp32);
    Tmp32 = 0x11AA0011;
    ACPI_MOVE_32_TO_32(&LocalFACS.GlobalLock, &Tmp32);

    /* Build the optional local tables */

    if (AcpiGbl_LoadTestTables)
    {
	UINT32 Tmp32;

        /*
         * Build a fake table [TEST] so that we make sure that the
         * ACPICA core ignores it
         */
        memset (&LocalTEST, 0, sizeof (ACPI_TABLE_HEADER));
        ACPI_COPY_NAMESEG (LocalTEST.Signature, "TEST");

        LocalTEST.Revision = 1;
        Tmp32 = sizeof (ACPI_TABLE_HEADER);
        ACPI_MOVE_32_TO_32(&LocalTEST.Length, &Tmp32);

        LocalTEST.Checksum = 0;
        LocalTEST.Checksum = (UINT8) -AcpiTbChecksum (
            (void *) &LocalTEST, Tmp32);

        /*
         * Build a fake table with a bad signature [BAD!] so that we make
         * sure that the ACPICA core ignores it
         */
        memset (&LocalBADTABLE, 0, sizeof (ACPI_TABLE_HEADER));
        ACPI_COPY_NAMESEG (LocalBADTABLE.Signature, "BAD!");

        LocalBADTABLE.Revision = 1;
        Tmp32 = sizeof (ACPI_TABLE_HEADER);
        ACPI_MOVE_32_TO_32(&LocalBADTABLE.Length, &Tmp32);

        LocalBADTABLE.Checksum = 0;
        LocalBADTABLE.Checksum = (UINT8) -AcpiTbChecksum (
            (void *) &LocalBADTABLE, Tmp32);
    }

    return (AE_OK);
}


/******************************************************************************
 *
 * FUNCTION:    AeInstallTables
 *
 * PARAMETERS:  None
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Install the various ACPI tables
 *
 *****************************************************************************/

ACPI_STATUS
AeInstallTables (
    void)
{
    ACPI_STATUS             Status;
    ACPI_TABLE_HEADER       Header;
    ACPI_TABLE_HEADER       *Table;
    UINT32                  i;


    Status = AcpiInitializeTables (NULL, ACPI_MAX_INIT_TABLES, TRUE);
    ACPI_CHECK_OK (AcpiInitializeTables, Status);

    /*
     * The following code is prepared to test the deferred table
     * verification mechanism. When AcpiGbl_EnableTableValidation is set
     * to FALSE by default, AcpiReallocateRootTable() sets it back to TRUE
     * and triggers the deferred table verification mechanism accordingly.
     */
    (void) AcpiReallocateRootTable ();

    if (AcpiGbl_LoadTestTables)
    {
        /* Test multiple table/UEFI support. First, get the headers */

        Status = AcpiGetTableHeader (ACPI_SIG_UEFI, 1, &Header);
        ACPI_CHECK_OK (AcpiGetTableHeader, Status);

        Status = AcpiGetTableHeader (ACPI_SIG_UEFI, 2, &Header);
        ACPI_CHECK_OK (AcpiGetTableHeader, Status);

        Status = AcpiGetTableHeader (ACPI_SIG_UEFI, 3, &Header);
        ACPI_CHECK_STATUS (AcpiGetTableHeader, Status, AE_NOT_FOUND);

        /* Now get the actual tables */

        Status = AcpiGetTable (ACPI_SIG_UEFI, 1, &Table);
        ACPI_CHECK_OK (AcpiGetTable, Status);

        Status = AcpiGetTable (ACPI_SIG_UEFI, 2, &Table);
        ACPI_CHECK_OK (AcpiGetTable, Status);

        Status = AcpiGetTable (ACPI_SIG_UEFI, 3, &Table);
        ACPI_CHECK_STATUS (AcpiGetTable, Status, AE_NOT_FOUND);
    }

    /* Check that we can get all of the ACPI tables */

    for (i = 0; ; i++)
    {
        Status = AcpiGetTableByIndex (i, &Table);
        if ((Status == AE_BAD_PARAMETER) || !Table)
        {
            break;
        }

        ACPI_CHECK_OK (AcpiGetTableByIndex, Status);
    }

    return (AE_OK);
}


/******************************************************************************
 *
 * FUNCTION:    AeLoadTables
 *
 * PARAMETERS:  None
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Load the definition block ACPI tables
 *
 *****************************************************************************/

ACPI_STATUS
AeLoadTables (
    void)
{
    ACPI_STATUS             Status;


    Status = AcpiLoadTables ();
    ACPI_CHECK_OK (AcpiLoadTables, Status);

    /*
     * Test run-time control method installation. Do it twice to test code
     * for an existing name.
     */
    Status = AcpiInstallMethod (MethodCode);
    ACPI_CHECK_OK (AcpiInstallMethod, Status);

    Status = AcpiInstallMethod (MethodCode);
    ACPI_CHECK_OK (AcpiInstallMethod, Status);

    return (AE_OK);
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsGetRootPointer
 *
 * PARAMETERS:  Flags       - not used
 *              Address     - Where the root pointer is returned
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Return a local RSDP, used to dynamically load tables via the
 *              standard ACPI mechanism.
 *
 *****************************************************************************/

ACPI_PHYSICAL_ADDRESS
AcpiOsGetRootPointer (
    void)
{

    return (ACPI_PTR_TO_PHYSADDR (&LocalRSDP));
}
