#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")
typedef struct osl_table_info
{
struct osl_table_info *Next;
UINT32 Instance;
char Signature[ACPI_NAME_SIZE];
} OSL_TABLE_INFO;
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;
UINT8 Gbl_TableListInitialized = FALSE;
ACPI_TABLE_RSDP Gbl_Rsdp;
ACPI_TABLE_FADT *Gbl_Fadt = NULL;
ACPI_TABLE_RSDT *Gbl_Rsdt = NULL;
ACPI_TABLE_XSDT *Gbl_Xsdt = NULL;
ACPI_PHYSICAL_ADDRESS Gbl_FadtAddress = 0;
ACPI_PHYSICAL_ADDRESS Gbl_RsdpAddress = 0;
UINT8 Gbl_Revision = 0;
OSL_TABLE_INFO *Gbl_TableListHead = NULL;
UINT32 Gbl_TableCount = 0;
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);
}
}
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;
Status = OslTableInitialize();
if (ACPI_FAILURE(Status)) {
return (Status);
}
Status = OslMapTable(Address, NULL, &MappedTable);
if (ACPI_FAILURE(Status)) {
return (Status);
}
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);
}
ACPI_STATUS
AcpiOsGetTableByName(char *Signature, UINT32 Instance,
ACPI_TABLE_HEADER **Table, ACPI_PHYSICAL_ADDRESS *Address)
{
ACPI_STATUS Status;
Status = OslTableInitialize();
if (ACPI_FAILURE(Status)) {
return (Status);
}
Status = OslGetBiosTable(Signature, Instance, Table, Address);
return (Status);
}
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);
}
ACPI_STATUS
AcpiOsGetTableByIndex(UINT32 Index, ACPI_TABLE_HEADER **Table,
UINT32 *Instance, ACPI_PHYSICAL_ADDRESS *Address)
{
OSL_TABLE_INFO *Info;
ACPI_STATUS Status;
UINT32 i;
Status = OslTableInitialize();
if (ACPI_FAILURE(Status)) {
return (Status);
}
if (Index >= Gbl_TableCount) {
return (AE_LIMIT);
}
Info = Gbl_TableListHead;
for (i = 0; i < Index; i++) {
Info = Info->Next;
}
Status = AcpiOsGetTableByName(Info->Signature, Info->Instance,
Table, Address);
if (ACPI_SUCCESS(Status)) {
*Instance = Info->Instance;
}
return (Status);
}
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);
}
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);
physaddr <<= 4;
AcpiOsUnmapMemory(mapp, ACPI_EBDA_PTR_LENGTH);
if (physaddr <= 0x400)
goto try_bios;
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:
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));
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);
}
static BOOLEAN
OslCanUseXsdt(void)
{
if (Gbl_Revision && !AcpiGbl_DoNotUseXsdt) {
return (TRUE);
} else {
return (FALSE);
}
}
static ACPI_STATUS
OslTableInitialize(void)
{
ACPI_STATUS Status;
ACPI_PHYSICAL_ADDRESS Address;
if (Gbl_TableListInitialized) {
return (AE_OK);
}
Status = OslLoadRsdp();
if (ACPI_FAILURE(Status)) {
return (Status);
}
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);
}
}
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);
}
}
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);
}
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);
}
Status = OslListBiosTables();
if (ACPI_FAILURE(Status)) {
return (Status);
}
Gbl_TableListInitialized = TRUE;
return (AE_OK);
}
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 {
ItemSize = sizeof (UINT32);
TableData = ACPI_CAST8(Gbl_Rsdt) + sizeof (ACPI_TABLE_HEADER);
NumberOfTables = (UINT32)
((Gbl_Rsdt->Header.Length - sizeof (ACPI_TABLE_HEADER))
/ ItemSize);
}
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));
}
if (TableAddress == 0) {
continue;
}
Status = OslMapTable(TableAddress, NULL, &MappedTable);
if (ACPI_FAILURE(Status)) {
return (Status);
}
OslAddTableToList(MappedTable->Signature);
OslUnmapTable(MappedTable);
}
return (AE_OK);
}
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;
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);
}
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;
}
Status = OslMapTable(TableAddress, Signature, &MappedTable);
if (ACPI_FAILURE(Status)) {
return (Status);
}
TableLength = ApGetTableLength(MappedTable);
} else {
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 {
ItemSize = sizeof (UINT32);
TableData = ACPI_CAST8(Gbl_Rsdt) +
sizeof (ACPI_TABLE_HEADER);
NumberOfTables = (UINT8) ((Gbl_Rsdt->Header.Length -
sizeof (ACPI_TABLE_HEADER))
/ ItemSize);
}
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));
}
if (TableAddress == 0) {
continue;
}
Status = OslMapTable(TableAddress, NULL, &MappedTable);
if (ACPI_FAILURE(Status)) {
return (Status);
}
TableLength = MappedTable->Length;
if (!ACPI_COMPARE_NAME(MappedTable->Signature,
Signature)) {
OslUnmapTable(MappedTable);
MappedTable = NULL;
continue;
}
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;
}
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);
}
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);
}
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 (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);
}
}
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);
}
static void
OslUnmapTable(ACPI_TABLE_HEADER *Table)
{
if (Table != NULL) {
AcpiOsUnmapMemory(Table, ApGetTableLength(Table));
}
}
static ACPI_STATUS
OslTableNameFromFile(char *Filename, char *Signature, UINT32 *Instance)
{
if (strlen(Filename) < ACPI_NAME_SIZE) {
return (AE_BAD_SIGNATURE);
}
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;
}
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);
}