/*
 *
 * 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);
}
