root/usr/src/cmd/acpi/acpidump/osillumostbl.c
/*
 *
 * Module Name: osillumostbl - illumos OSL for obtaining ACPI tables
 * This file is derived from the Intel oslinuxtbl source file.
 *
 */

/*
 * Copyright (C) 2000 - 2016, 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.
 */

/*
 * Copyright (c) 2018, Joyent, Inc.
 */

#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <libdevinfo.h>
#include "acpidump.h"

#define _COMPONENT      ACPI_OS_SERVICES
    ACPI_MODULE_NAME("osillumostbl")

/* List of information about obtained ACPI tables */

typedef struct osl_table_info
{
        struct osl_table_info   *Next;
        UINT32                  Instance;
        char                    Signature[ACPI_NAME_SIZE];
} OSL_TABLE_INFO;

/* Local prototypes */
static ACPI_STATUS
OslTableInitialize(void);
static ACPI_STATUS OslTableNameFromFile(char *, char *, UINT32 *);
static ACPI_STATUS OslAddTableToList(char *);
static ACPI_STATUS OslMapTable(ACPI_SIZE, char *, ACPI_TABLE_HEADER **);
static void OslUnmapTable(ACPI_TABLE_HEADER *);
static ACPI_STATUS OslLoadRsdp(void);
static ACPI_STATUS OslListBiosTables(void);
static ACPI_STATUS OslGetBiosTable(char *, UINT32, ACPI_TABLE_HEADER **,
    ACPI_PHYSICAL_ADDRESS *);
static ACPI_STATUS OslGetLastStatus(ACPI_STATUS);

static int pagesize;

/* Initialization flags */
UINT8                   Gbl_TableListInitialized = FALSE;

/* Local copies of main ACPI tables */
ACPI_TABLE_RSDP         Gbl_Rsdp;
ACPI_TABLE_FADT         *Gbl_Fadt = NULL;
ACPI_TABLE_RSDT         *Gbl_Rsdt = NULL;
ACPI_TABLE_XSDT         *Gbl_Xsdt = NULL;

/* Table addresses */
ACPI_PHYSICAL_ADDRESS   Gbl_FadtAddress = 0;
ACPI_PHYSICAL_ADDRESS   Gbl_RsdpAddress = 0;

/* Revision of RSD PTR */
UINT8                   Gbl_Revision = 0;

OSL_TABLE_INFO          *Gbl_TableListHead = NULL;
UINT32                  Gbl_TableCount = 0;

/*
 *
 * FUNCTION:    OslGetLastStatus
 *
 * PARAMETERS:  DefaultStatus   - Default error status to return
 *
 * RETURN:      Status; Converted from errno.
 *
 * DESCRIPTION: Get last errno and conver it to ACPI_STATUS.
 *
 */
static ACPI_STATUS
OslGetLastStatus(ACPI_STATUS DefaultStatus)
{
        switch (errno) {
        case EACCES:
        case EPERM:
                return (AE_ACCESS);

        case ENOENT:
                return (AE_NOT_FOUND);

        case ENOMEM:
                return (AE_NO_MEMORY);

        default:
                return (DefaultStatus);
        }
}

/*
 *
 * FUNCTION:    AcpiOsGetTableByAddress
 *
 * PARAMETERS:  Address         - Physical address of the ACPI table
 *              Table           - Where a pointer to the table is returned
 *
 * RETURN:      Status; Table buffer is returned if AE_OK.
 *              AE_NOT_FOUND: A valid table was not found at the address
 *
 * DESCRIPTION: Get an ACPI table via a physical memory address.
 *
 */
ACPI_STATUS
AcpiOsGetTableByAddress(ACPI_PHYSICAL_ADDRESS Address,
    ACPI_TABLE_HEADER **Table)
{
        UINT32                  TableLength;
        ACPI_TABLE_HEADER       *MappedTable;
        ACPI_TABLE_HEADER       *LocalTable = NULL;
        ACPI_STATUS             Status = AE_OK;

        /*
         * Get main ACPI tables from memory on first invocation of this
         * function
         */
        Status = OslTableInitialize();
        if (ACPI_FAILURE(Status)) {
                return (Status);
        }

        /* Map the table and validate it */

        Status = OslMapTable(Address, NULL, &MappedTable);
        if (ACPI_FAILURE(Status)) {
                return (Status);
        }

        /* Copy table to local buffer and return it */

        TableLength = ApGetTableLength(MappedTable);
        if (TableLength == 0) {
                Status = AE_BAD_HEADER;
                goto Exit;
        }

        LocalTable = calloc(1, TableLength);
        if (!LocalTable) {
                Status = AE_NO_MEMORY;
                goto Exit;
        }

        memcpy(LocalTable, MappedTable, TableLength);

Exit:
        OslUnmapTable(MappedTable);
        *Table = LocalTable;
        return (Status);
}

/*
 *
 * FUNCTION:    AcpiOsGetTableByName
 *
 * PARAMETERS:  Signature       - ACPI Signature for desired table. Must be
 *                                a null terminated 4-character string.
 *              Instance        - Multiple table support for SSDT/UEFI (0...n)
 *                                Must be 0 for other tables.
 *              Table           - Where a pointer to the table is returned
 *              Address         - Where the table physical address is returned
 *
 * RETURN:      Status; Table buffer and physical address returned if AE_OK.
 *              AE_LIMIT: Instance is beyond valid limit
 *              AE_NOT_FOUND: A table with the signature was not found
 *
 * NOTE:        Assumes the input signature is uppercase.
 *
 */
ACPI_STATUS
AcpiOsGetTableByName(char *Signature, UINT32 Instance,
    ACPI_TABLE_HEADER **Table, ACPI_PHYSICAL_ADDRESS *Address)
{
        ACPI_STATUS     Status;

        /*
         * Get main ACPI tables from memory on first invocation of this
         * function
         */
        Status = OslTableInitialize();
        if (ACPI_FAILURE(Status)) {
                return (Status);
        }

        /* attempt to extract it from the RSDT/XSDT */
        Status = OslGetBiosTable(Signature, Instance, Table, Address);

        return (Status);
}

/*
 *
 * FUNCTION:    OslAddTableToList
 *
 * PARAMETERS:  Signature       - Table signature
 *
 * RETURN:      Status; Successfully added if AE_OK.
 *              AE_NO_MEMORY: Memory allocation error
 *
 * DESCRIPTION: Insert a table structure into OSL table list.
 *
 */
static ACPI_STATUS
OslAddTableToList(char *Signature)
{
        OSL_TABLE_INFO  *NewInfo;
        OSL_TABLE_INFO  *Next;
        UINT32          NextInstance = 0;
        UINT32          Instance = 0;
        BOOLEAN         Found = FALSE;

        NewInfo = calloc(1, sizeof (OSL_TABLE_INFO));
        if (NewInfo == NULL) {
                return (AE_NO_MEMORY);
        }

        ACPI_MOVE_NAME(NewInfo->Signature, Signature);

        if (!Gbl_TableListHead) {
                Gbl_TableListHead = NewInfo;
        } else {
                Next = Gbl_TableListHead;

                while (1) {
                        if (ACPI_COMPARE_NAME(Next->Signature, Signature)) {
                                if (Next->Instance == 0) {
                                        Found = TRUE;
                                }
                                if (Next->Instance >= NextInstance) {
                                        NextInstance = Next->Instance + 1;
                                }
                        }

                        if (!Next->Next) {
                                break;
                        }
                        Next = Next->Next;
                }
                Next->Next = NewInfo;
        }

        if (Found) {
                Instance = NextInstance;
        }

        NewInfo->Instance = Instance;
        Gbl_TableCount++;

        return (AE_OK);
}

/*
 *
 * FUNCTION:    AcpiOsGetTableByIndex
 *
 * PARAMETERS:  Index           - Which table to get
 *              Table           - Where a pointer to the table is returned
 *              Instance        - Where a pointer to the table instance no. is
 *                                returned
 *              Address         - Where the table physical address is returned
 *
 * RETURN:      Status; Table buffer and physical address returned if AE_OK.
 *              AE_LIMIT: Index is beyond valid limit
 *
 * DESCRIPTION: Get an ACPI table via an index value (0 through n). Returns
 *              AE_LIMIT when an invalid index is reached. Index is not
 *              necessarily an index into the RSDT/XSDT.
 *
 */
ACPI_STATUS
AcpiOsGetTableByIndex(UINT32 Index, ACPI_TABLE_HEADER **Table,
    UINT32 *Instance, ACPI_PHYSICAL_ADDRESS *Address)
{
        OSL_TABLE_INFO  *Info;
        ACPI_STATUS     Status;
        UINT32          i;

        /*
         * Get main ACPI tables from memory on first invocation of this
         * function.
         */

        Status = OslTableInitialize();
        if (ACPI_FAILURE(Status)) {
                return (Status);
        }

        /* Validate Index */

        if (Index >= Gbl_TableCount) {
                return (AE_LIMIT);
        }

        /* Point to the table list entry specified by the Index argument */

        Info = Gbl_TableListHead;
        for (i = 0; i < Index; i++) {
                Info = Info->Next;
        }

        /* Now we can just get the table via the signature */

        Status = AcpiOsGetTableByName(Info->Signature, Info->Instance,
            Table, Address);

        if (ACPI_SUCCESS(Status)) {
                *Instance = Info->Instance;
        }
        return (Status);
}

/*
 *
 * FUNCTION:    OslLoadRsdp
 *
 * PARAMETERS:  None
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Scan and load RSDP.
 * See the find_rsdp() function in usr/src/uts/i86pc/os/fakebop.c, which is how
 * the kernel finds the RSDP. That algorithm matches AcpiFindRootPointer().
 *
 * If the system is not using BIOS, and ACPI information was passed to the
 * system from the boot loader, then the RSDP is recorded in the "acpi-root-tab"
 * property.
 *
 * The code here is derived from AcpiFindRootPointer, except that we will
 * try the "acpi-root-tab" property first. If the property does not exist or
 * we do not find the root, then we scan the EBDA. Finally, we will search
 * the BIOS and copy the table if found.
 */
static ACPI_STATUS
OslLoadRsdp(void)
{
        UINT8                   *mapp = NULL;
        ACPI_TABLE_HEADER       *tblp;
        ACPI_SIZE               mapsize = sizeof (ACPI_TABLE_RSDP);
        ACPI_PHYSICAL_ADDRESS   physaddr;
        di_node_t               root;
        int64_t                 *val64;

        if ((root = di_init("/", DINFOPROP)) != DI_NODE_NIL) {
                if (di_prop_lookup_int64(DDI_DEV_T_ANY, root,
                    "acpi-root-tab", &val64) == 1) {
                        physaddr = (ACPI_PHYSICAL_ADDRESS)*val64;
                        mapp = AcpiOsMapMemory(physaddr, mapsize);
                }
                di_fini(root);
        }

        if (mapp != NULL) {
                tblp = ACPI_CAST_PTR(ACPI_TABLE_HEADER,
                    AcpiTbScanMemoryForRsdp(mapp, mapsize));
                if (tblp != NULL) {
                        physaddr += (ACPI_PHYSICAL_ADDRESS)
                            ACPI_PTR_DIFF(tblp, mapp);
                        Gbl_RsdpAddress = physaddr;
                        memcpy(&Gbl_Rsdp, tblp, sizeof (ACPI_TABLE_RSDP));
                        AcpiOsUnmapMemory(mapp, mapsize);
                        return (AE_OK);
                }
                AcpiOsUnmapMemory(mapp, mapsize);
        }

        /* 1a) Get the location of the Extended BIOS Data Area (EBDA) */
        mapp = AcpiOsMapMemory((ACPI_PHYSICAL_ADDRESS)ACPI_EBDA_PTR_LOCATION,
            ACPI_EBDA_PTR_LENGTH);
        if (mapp == NULL)
                goto try_bios;

        ACPI_MOVE_16_TO_32(&physaddr, mapp);

        /* Convert segment part to physical address */
        physaddr <<= 4;
        AcpiOsUnmapMemory(mapp, ACPI_EBDA_PTR_LENGTH);

        /* EBDA present? */
        if (physaddr <= 0x400)
                goto try_bios;

        /*
         * 1b) Search EBDA paragraphs (EBDA is required to be a minimum of 1K
         * length)
         */
        mapp = AcpiOsMapMemory(physaddr, ACPI_EBDA_WINDOW_SIZE);
        if (mapp == NULL) {
                (void) fprintf(stderr, "EBDA (0x%p) found, but is not "
                    "mappable\n", physaddr);
                goto try_bios;
        }

        tblp = ACPI_CAST_PTR(ACPI_TABLE_HEADER,
            AcpiTbScanMemoryForRsdp(mapp, ACPI_EBDA_WINDOW_SIZE));
        if (tblp != NULL) {
                physaddr += (ACPI_PHYSICAL_ADDRESS) ACPI_PTR_DIFF(tblp, mapp);
                Gbl_RsdpAddress = physaddr;
                memcpy(&Gbl_Rsdp, tblp, sizeof (ACPI_TABLE_RSDP));
                AcpiOsUnmapMemory(mapp, ACPI_EBDA_WINDOW_SIZE);

                return (AE_OK);
        }
        AcpiOsUnmapMemory(mapp, ACPI_EBDA_WINDOW_SIZE);

try_bios:
        /* Try to get RSDP from BIOS memory */
        if (Gbl_RsdpBase != 0) {
                physaddr = Gbl_RsdpBase;
                mapsize = sizeof (ACPI_TABLE_RSDP);
        } else {
                physaddr = ACPI_HI_RSDP_WINDOW_BASE;
                mapsize = ACPI_HI_RSDP_WINDOW_SIZE;
        }

        mapp = AcpiOsMapMemory(physaddr, mapsize);
        if (mapp == NULL)
                return (OslGetLastStatus(AE_BAD_ADDRESS));

        /* Search low memory for the RSDP */
        tblp = ACPI_CAST_PTR(ACPI_TABLE_HEADER,
            AcpiTbScanMemoryForRsdp(mapp, mapsize));
        if (tblp == NULL) {
                AcpiOsUnmapMemory(mapp, mapsize);
                return (AE_NOT_FOUND);
        }

        physaddr += (ACPI_PHYSICAL_ADDRESS) ACPI_PTR_DIFF(tblp, mapp);
        Gbl_RsdpAddress = physaddr;
        memcpy(&Gbl_Rsdp, tblp, sizeof (ACPI_TABLE_RSDP));
        AcpiOsUnmapMemory(mapp, mapsize);

        return (AE_OK);
}

/*
 *
 * FUNCTION:    OslCanUseXsdt
 *
 * PARAMETERS:  None
 *
 * RETURN:      TRUE if XSDT is allowed to be used.
 *
 * DESCRIPTION: This function collects logic that can be used to determine if
 *              XSDT should be used instead of RSDT.
 *
 */
static BOOLEAN
OslCanUseXsdt(void)
{
        if (Gbl_Revision && !AcpiGbl_DoNotUseXsdt) {
                return (TRUE);
        } else {
                return (FALSE);
        }
}

/*
 *
 * FUNCTION:    OslTableInitialize
 *
 * PARAMETERS:  None
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Initialize ACPI table data. Get and store main ACPI tables to
 *              local variables. Main ACPI tables include RSDT, FADT, RSDT,
 *              and/or XSDT.
 *
 */
static ACPI_STATUS
OslTableInitialize(void)
{
        ACPI_STATUS             Status;
        ACPI_PHYSICAL_ADDRESS   Address;

        if (Gbl_TableListInitialized) {
                return (AE_OK);
        }

        /* Get RSDP from memory */

        Status = OslLoadRsdp();
        if (ACPI_FAILURE(Status)) {
                return (Status);
        }

        /* Get XSDT from memory */

        if (Gbl_Rsdp.Revision && !Gbl_DoNotDumpXsdt) {
                if (Gbl_Xsdt) {
                        free(Gbl_Xsdt);
                        Gbl_Xsdt = NULL;
                }

                Gbl_Revision = 2;
                Status = OslGetBiosTable(ACPI_SIG_XSDT, 0,
                    ACPI_CAST_PTR(ACPI_TABLE_HEADER *, &Gbl_Xsdt), &Address);
                if (ACPI_FAILURE(Status)) {
                        return (Status);
                }
        }

        /* Get RSDT from memory */

        if (Gbl_Rsdp.RsdtPhysicalAddress) {
                if (Gbl_Rsdt) {
                        free(Gbl_Rsdt);
                        Gbl_Rsdt = NULL;
                }

                Status = OslGetBiosTable(ACPI_SIG_RSDT, 0,
                    ACPI_CAST_PTR(ACPI_TABLE_HEADER *, &Gbl_Rsdt), &Address);
                if (ACPI_FAILURE(Status)) {
                        return (Status);
                }
        }

        /* Get FADT from memory */

        if (Gbl_Fadt) {
                free(Gbl_Fadt);
                Gbl_Fadt = NULL;
        }

        Status = OslGetBiosTable(ACPI_SIG_FADT, 0,
            ACPI_CAST_PTR(ACPI_TABLE_HEADER *, &Gbl_Fadt), &Gbl_FadtAddress);
        if (ACPI_FAILURE(Status)) {
                return (Status);
        }

        /* Add mandatory tables to global table list first */

        Status = OslAddTableToList(ACPI_RSDP_NAME);
        if (ACPI_FAILURE(Status)) {
                return (Status);
        }

        Status = OslAddTableToList(ACPI_SIG_RSDT);
        if (ACPI_FAILURE(Status)) {
                return (Status);
        }

        if (Gbl_Revision == 2) {
                Status = OslAddTableToList(ACPI_SIG_XSDT);
                if (ACPI_FAILURE(Status)) {
                        return (Status);
                }
        }

        Status = OslAddTableToList(ACPI_SIG_DSDT);
        if (ACPI_FAILURE(Status)) {
                return (Status);
        }

        Status = OslAddTableToList(ACPI_SIG_FACS);
        if (ACPI_FAILURE(Status)) {
                return (Status);
        }

        /* Add all tables found in the memory */

        Status = OslListBiosTables();
        if (ACPI_FAILURE(Status)) {
                return (Status);
        }

        Gbl_TableListInitialized = TRUE;
        return (AE_OK);
}


/*
 *
 * FUNCTION:    OslListBiosTables
 *
 * PARAMETERS:  None
 *
 * RETURN:      Status; Table list is initialized if AE_OK.
 *
 * DESCRIPTION: Add ACPI tables to the table list from memory.
 */
static ACPI_STATUS
OslListBiosTables(void)
{
        ACPI_TABLE_HEADER       *MappedTable = NULL;
        UINT8                   *TableData;
        UINT32                  NumberOfTables;
        UINT8                   ItemSize;
        ACPI_PHYSICAL_ADDRESS   TableAddress = 0;
        ACPI_STATUS             Status = AE_OK;
        UINT32                  i;

        if (OslCanUseXsdt()) {
                ItemSize = sizeof (UINT64);
                TableData = ACPI_CAST8(Gbl_Xsdt) + sizeof (ACPI_TABLE_HEADER);
                NumberOfTables = (UINT32)
                    ((Gbl_Xsdt->Header.Length - sizeof (ACPI_TABLE_HEADER))
                    / ItemSize);

        } else {
                /* Use RSDT if XSDT is not available */
                ItemSize = sizeof (UINT32);
                TableData = ACPI_CAST8(Gbl_Rsdt) + sizeof (ACPI_TABLE_HEADER);
                NumberOfTables = (UINT32)
                    ((Gbl_Rsdt->Header.Length - sizeof (ACPI_TABLE_HEADER))
                    / ItemSize);
        }

        /* Search RSDT/XSDT for the requested table */

        for (i = 0; i < NumberOfTables; ++i, TableData += ItemSize) {
                if (OslCanUseXsdt()) {
                        TableAddress =
                            (ACPI_PHYSICAL_ADDRESS) (*ACPI_CAST64(TableData));
                } else {
                        TableAddress =
                            (ACPI_PHYSICAL_ADDRESS) (*ACPI_CAST32(TableData));
                }

                /* Skip NULL entries in RSDT/XSDT */
                if (TableAddress == 0) {
                        continue;
                }

                Status = OslMapTable(TableAddress, NULL, &MappedTable);
                if (ACPI_FAILURE(Status)) {
                        return (Status);
                }

                OslAddTableToList(MappedTable->Signature);
                OslUnmapTable(MappedTable);
        }

        return (AE_OK);
}

/*
 *
 * FUNCTION:    OslGetBiosTable
 *
 * PARAMETERS:  Signature       - ACPI Signature for common table. Must be
 *                                a null terminated 4-character string.
 *              Instance        - Multiple table support for SSDT/UEFI (0...n)
 *                                Must be 0 for other tables.
 *              Table           - Where a pointer to the table is returned
 *              Address         - Where the table physical address is returned
 *
 * RETURN:      Status; Table buffer and physical address returned if AE_OK.
 *              AE_LIMIT: Instance is beyond valid limit
 *              AE_NOT_FOUND: A table with the signature was not found
 *
 * DESCRIPTION: Get a BIOS provided ACPI table
 *
 * NOTE:        Assumes the input signature is uppercase.
 *
 */
static ACPI_STATUS
OslGetBiosTable(char *Signature, UINT32 Instance, ACPI_TABLE_HEADER **Table,
    ACPI_PHYSICAL_ADDRESS *Address)
{
        ACPI_TABLE_HEADER       *LocalTable = NULL;
        ACPI_TABLE_HEADER       *MappedTable = NULL;
        UINT8                   *TableData;
        UINT8                   NumberOfTables;
        UINT8                   ItemSize;
        UINT32                  CurrentInstance = 0;
        ACPI_PHYSICAL_ADDRESS   TableAddress = 0;
        UINT32                  TableLength = 0;
        ACPI_STATUS             Status = AE_OK;
        UINT32                  i;

        /* Handle special tables whose addresses are not in RSDT/XSDT */

        if (ACPI_COMPARE_NAME(Signature, ACPI_RSDP_NAME) ||
            ACPI_COMPARE_NAME(Signature, ACPI_SIG_RSDT) ||
            ACPI_COMPARE_NAME(Signature, ACPI_SIG_XSDT) ||
            ACPI_COMPARE_NAME(Signature, ACPI_SIG_DSDT) ||
            ACPI_COMPARE_NAME(Signature, ACPI_SIG_FACS)) {
                if (Instance > 0) {
                        return (AE_LIMIT);
                }

                /*
                 * Get the appropriate address, either 32-bit or 64-bit. Be very
                 * careful about the FADT length and validate table addresses.
                 * Note: The 64-bit addresses have priority.
                 */
                if (ACPI_COMPARE_NAME(Signature, ACPI_SIG_DSDT)) {
                        if ((Gbl_Fadt->Header.Length >= MIN_FADT_FOR_XDSDT) &&
                            Gbl_Fadt->XDsdt) {
                                TableAddress =
                                    (ACPI_PHYSICAL_ADDRESS) Gbl_Fadt->XDsdt;

                        } else if (Gbl_Fadt->Header.Length >=
                            MIN_FADT_FOR_DSDT && Gbl_Fadt->Dsdt) {
                                TableAddress =
                                    (ACPI_PHYSICAL_ADDRESS) Gbl_Fadt->Dsdt;
                        }

                } else if (ACPI_COMPARE_NAME(Signature, ACPI_SIG_FACS)) {
                        if ((Gbl_Fadt->Header.Length >= MIN_FADT_FOR_XFACS) &&
                            Gbl_Fadt->XFacs) {
                                TableAddress =
                                    (ACPI_PHYSICAL_ADDRESS) Gbl_Fadt->XFacs;

                        } else if (Gbl_Fadt->Header.Length >=
                            MIN_FADT_FOR_FACS && Gbl_Fadt->Facs) {
                                TableAddress =
                                    (ACPI_PHYSICAL_ADDRESS) Gbl_Fadt->Facs;
                        }

                } else if (ACPI_COMPARE_NAME(Signature, ACPI_SIG_XSDT)) {
                        if (!Gbl_Revision) {
                                return (AE_BAD_SIGNATURE);
                        }
                        TableAddress = (ACPI_PHYSICAL_ADDRESS)
                            Gbl_Rsdp.XsdtPhysicalAddress;

                } else if (ACPI_COMPARE_NAME(Signature, ACPI_SIG_RSDT)) {
                        TableAddress = (ACPI_PHYSICAL_ADDRESS)
                            Gbl_Rsdp.RsdtPhysicalAddress;

                } else {
                        TableAddress = (ACPI_PHYSICAL_ADDRESS) Gbl_RsdpAddress;
                        Signature = ACPI_SIG_RSDP;
                }

                /* Now we can get the requested special table */

                Status = OslMapTable(TableAddress, Signature, &MappedTable);
                if (ACPI_FAILURE(Status)) {
                        return (Status);
                }

                TableLength = ApGetTableLength(MappedTable);

        } else {
                /* Case for a normal ACPI table */
                if (OslCanUseXsdt()) {
                        ItemSize = sizeof (UINT64);
                        TableData = ACPI_CAST8(Gbl_Xsdt) +
                            sizeof (ACPI_TABLE_HEADER);
                        NumberOfTables = (UINT8) ((Gbl_Xsdt->Header.Length -
                            sizeof (ACPI_TABLE_HEADER))
                            / ItemSize);

                } else {
                        /* Use RSDT if XSDT is not available */
                        ItemSize = sizeof (UINT32);
                        TableData = ACPI_CAST8(Gbl_Rsdt) +
                            sizeof (ACPI_TABLE_HEADER);
                        NumberOfTables = (UINT8) ((Gbl_Rsdt->Header.Length -
                            sizeof (ACPI_TABLE_HEADER))
                            / ItemSize);
                }

                /* Search RSDT/XSDT for the requested table */

                for (i = 0; i < NumberOfTables; ++i, TableData += ItemSize) {
                        if (OslCanUseXsdt()) {
                                TableAddress = (ACPI_PHYSICAL_ADDRESS)
                                    (*ACPI_CAST64(TableData));
                        } else {
                                TableAddress = (ACPI_PHYSICAL_ADDRESS)
                                    (*ACPI_CAST32(TableData));
                        }

                        /* Skip NULL entries in RSDT/XSDT */

                        if (TableAddress == 0) {
                                continue;
                        }

                        Status = OslMapTable(TableAddress, NULL, &MappedTable);
                        if (ACPI_FAILURE(Status)) {
                                return (Status);
                        }
                        TableLength = MappedTable->Length;

                        /* Does this table match the requested signature? */

                        if (!ACPI_COMPARE_NAME(MappedTable->Signature,
                            Signature)) {
                                OslUnmapTable(MappedTable);
                                MappedTable = NULL;
                                continue;
                        }

                        /* Match table instance (for SSDT/UEFI tables) */

                        if (CurrentInstance != Instance) {
                                OslUnmapTable(MappedTable);
                                MappedTable = NULL;
                                CurrentInstance++;
                                continue;
                        }

                        break;
                }
        }

        if (MappedTable == NULL) {
                return (AE_LIMIT);
        }

        if (TableLength == 0) {
                Status = AE_BAD_HEADER;
                goto Exit;
        }

        /* Copy table to local buffer and return it */

        LocalTable = calloc(1, TableLength);
        if (LocalTable == NULL) {
                Status = AE_NO_MEMORY;
                goto Exit;
        }

        memcpy(LocalTable, MappedTable, TableLength);
        *Address = TableAddress;
        *Table = LocalTable;

Exit:
        OslUnmapTable(MappedTable);
        return (Status);
}

/*
 *
 * FUNCTION:    OslMapTable
 *
 * PARAMETERS:  Address             - Address of the table in memory
 *              Signature           - Optional ACPI Signature for desired table.
 *                                    Null terminated 4-character string.
 *              Table               - Where a pointer to the mapped table is
 *                                    returned
 *
 * RETURN:      Status; Mapped table is returned if AE_OK.
 *              AE_NOT_FOUND: A valid table was not found at the address
 *
 * DESCRIPTION: Map entire ACPI table into caller's address space.
 *
 */
static ACPI_STATUS
OslMapTable(ACPI_SIZE Address, char *Signature, ACPI_TABLE_HEADER **Table)
{
        ACPI_TABLE_HEADER       *MappedTable;
        UINT32                  Length;

        if (Address == 0) {
                return (AE_BAD_ADDRESS);
        }

        /*
         * Map the header so we can get the table length.
         * Use sizeof (ACPI_TABLE_HEADER) as:
         * 1. it is bigger than 24 to include RSDP->Length
         * 2. it is smaller than sizeof (ACPI_TABLE_RSDP)
         */
        MappedTable = AcpiOsMapMemory(Address, sizeof (ACPI_TABLE_HEADER));
        if (MappedTable == NULL) {
                (void) fprintf(stderr, "Could not map table header at "
                    "0x%8.8X%8.8X\n", ACPI_FORMAT_UINT64(Address));
                return (OslGetLastStatus(AE_BAD_ADDRESS));
        }

        /* If specified, signature must match */

        if (Signature != NULL) {
                if (ACPI_VALIDATE_RSDP_SIG(Signature)) {
                        if (!ACPI_VALIDATE_RSDP_SIG(MappedTable->Signature)) {
                                AcpiOsUnmapMemory(MappedTable,
                                    sizeof (ACPI_TABLE_HEADER));
                                return (AE_BAD_SIGNATURE);
                        }
                } else if (!ACPI_COMPARE_NAME(Signature,
                    MappedTable->Signature)) {
                        AcpiOsUnmapMemory(MappedTable,
                            sizeof (ACPI_TABLE_HEADER));
                        return (AE_BAD_SIGNATURE);
                }
        }

        /* Map the entire table */

        Length = ApGetTableLength(MappedTable);
        AcpiOsUnmapMemory(MappedTable, sizeof (ACPI_TABLE_HEADER));
        if (Length == 0) {
                return (AE_BAD_HEADER);
        }

        MappedTable = AcpiOsMapMemory(Address, Length);
        if (MappedTable == NULL) {
                (void) fprintf(stderr, "Could not map table at 0x%8.8X%8.8X "
                    "length %8.8X\n", ACPI_FORMAT_UINT64(Address), Length);
                return (OslGetLastStatus(AE_INVALID_TABLE_LENGTH));
        }

        (void) ApIsValidChecksum(MappedTable);

        *Table = MappedTable;
        return (AE_OK);
}


/*
 *
 * FUNCTION:    OslUnmapTable
 *
 * PARAMETERS:  Table               - A pointer to the mapped table
 *
 * RETURN:      None
 *
 * DESCRIPTION: Unmap entire ACPI table.
 *
 */
static void
OslUnmapTable(ACPI_TABLE_HEADER *Table)
{
        if (Table != NULL) {
                AcpiOsUnmapMemory(Table, ApGetTableLength(Table));
        }
}

/*
 *
 * FUNCTION:    OslTableNameFromFile
 *
 * PARAMETERS:  Filename            - File that contains the desired table
 *              Signature           - Pointer to 4-character buffer to store
 *                                    extracted table signature.
 *              Instance            - Pointer to integer to store extracted
 *                                    table instance number.
 *
 * RETURN:      Status; Table name is extracted if AE_OK.
 *
 * DESCRIPTION: Extract table signature and instance number from a table file
 *              name.
 *
 */
static ACPI_STATUS
OslTableNameFromFile(char *Filename, char *Signature, UINT32 *Instance)
{
        /* Ignore meaningless files */

        if (strlen(Filename) < ACPI_NAME_SIZE) {
                return (AE_BAD_SIGNATURE);
        }

        /* Extract instance number */

        if (isdigit((int)Filename[ACPI_NAME_SIZE])) {
                sscanf(&Filename[ACPI_NAME_SIZE], "%u", Instance);
        } else if (strlen(Filename) != ACPI_NAME_SIZE) {
                return (AE_BAD_SIGNATURE);
        } else {
                *Instance = 0;
        }

        /* Extract signature */

        ACPI_MOVE_NAME(Signature, Filename);
        return (AE_OK);
}

void *
AcpiOsMapMemory(ACPI_PHYSICAL_ADDRESS Where, ACPI_SIZE Length)
{
        int fd;
        void *p;
        ulong_t offset;

        if ((fd = open("/dev/xsvc", O_RDONLY)) < 0)
                return (NULL);

        if (pagesize == 0) {
                pagesize = getpagesize();
        }

        offset = Where % pagesize;
        p = mmap(NULL, Length + offset, PROT_READ, MAP_SHARED | MAP_NORESERVE,
            fd, Where - offset);

        (void) close(fd);

        if (p == MAP_FAILED)
                return (NULL);
        p = (char *)p + offset;
        return (p);
}

void
AcpiOsUnmapMemory(void *LogicalAddress, ACPI_SIZE Size)
{
        ulong_t offset;
        void *p;

        offset = (ulong_t)LogicalAddress % pagesize;
        p = (void *)((char *)LogicalAddress - offset);

        (void) munmap(p, Size + offset);
}