/****************************************************************************** * * Module Name: oslinuxtbl - Linux OSL for obtaining ACPI tables * *****************************************************************************/ /* * 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. */ #include "acpidump.h" #define _COMPONENT ACPI_OS_SERVICES ACPI_MODULE_NAME ("oslinuxtbl") #ifndef PATH_MAX #define PATH_MAX 256 #endif /* 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 *Filename, char *Signature, UINT32 *Instance); static ACPI_STATUS OslAddTableToList ( char *Signature, UINT32 Instance); static ACPI_STATUS OslReadTableFromFile ( char *Filename, ACPI_SIZE FileOffset, char *Signature, ACPI_TABLE_HEADER **Table); static ACPI_STATUS OslMapTable ( ACPI_SIZE Address, char *Signature, ACPI_TABLE_HEADER **Table); static void OslUnmapTable ( ACPI_TABLE_HEADER *Table); static ACPI_PHYSICAL_ADDRESS OslFindRsdpViaEfiByKeyword ( FILE *File, const char *Keyword); static ACPI_PHYSICAL_ADDRESS OslFindRsdpViaEfi ( void); static ACPI_STATUS OslLoadRsdp ( void); static ACPI_STATUS OslListCustomizedTables ( char *Directory); static ACPI_STATUS OslGetCustomizedTable ( char *Pathname, char *Signature, UINT32 Instance, ACPI_TABLE_HEADER **Table, ACPI_PHYSICAL_ADDRESS *Address); static ACPI_STATUS OslListBiosTables ( void); static ACPI_STATUS OslGetBiosTable ( char *Signature, UINT32 Instance, ACPI_TABLE_HEADER **Table, ACPI_PHYSICAL_ADDRESS *Address); static ACPI_STATUS OslGetLastStatus ( ACPI_STATUS DefaultStatus); /* File locations */ #define DYNAMIC_TABLE_DIR "/sys/firmware/acpi/tables/dynamic" #define STATIC_TABLE_DIR "/sys/firmware/acpi/tables" #define EFI_SYSTAB "/sys/firmware/efi/systab" /* Should we get dynamically loaded SSDTs from DYNAMIC_TABLE_DIR? */ UINT8 Gbl_DumpDynamicTables = TRUE; /* 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); } /* Not a main ACPI table, attempt to extract it from the RSDT/XSDT */ if (!Gbl_DumpCustomizedTables) { /* Attempt to get the table from the memory */ Status = OslGetBiosTable (Signature, Instance, Table, Address); } else { /* Attempt to get the table from the static directory */ Status = OslGetCustomizedTable (STATIC_TABLE_DIR, Signature, Instance, Table, Address); } if (ACPI_FAILURE (Status) && Status == AE_LIMIT) { if (Gbl_DumpDynamicTables) { /* Attempt to get a dynamic table */ Status = OslGetCustomizedTable (DYNAMIC_TABLE_DIR, Signature, Instance, Table, Address); } } return (Status); } /****************************************************************************** * * FUNCTION: OslAddTableToList * * PARAMETERS: Signature - Table signature * Instance - Table instance * * 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, UINT32 Instance) { OSL_TABLE_INFO *NewInfo; OSL_TABLE_INFO *Next; UINT32 NextInstance = 0; BOOLEAN Found = FALSE; NewInfo = calloc (1, sizeof (OSL_TABLE_INFO)); if (!NewInfo) { 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 == Instance) { Found = TRUE; } if (Next->Instance >= NextInstance) { NextInstance = Next->Instance + 1; } } if (!Next->Next) { break; } Next = Next->Next; } Next->Next = NewInfo; } if (Found) { if (Instance) { fprintf (stderr, "%4.4s: Warning unmatched table instance %d, expected %d\n", Signature, Instance, NextInstance); } 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: OslFindRsdpViaEfiByKeyword * * PARAMETERS: Keyword - Character string indicating ACPI GUID version * in the EFI table * * RETURN: RSDP address if found * * DESCRIPTION: Find RSDP address via EFI using keyword indicating the ACPI * GUID version. * *****************************************************************************/ static ACPI_PHYSICAL_ADDRESS OslFindRsdpViaEfiByKeyword ( FILE *File, const char *Keyword) { char Buffer[80]; unsigned long long Address = 0; char Format[32]; snprintf (Format, 32, "%s=%s", Keyword, "%llx"); fseek (File, 0, SEEK_SET); while (fgets (Buffer, 80, File)) { if (sscanf (Buffer, Format, &Address) == 1) { break; } } return ((ACPI_PHYSICAL_ADDRESS) (Address)); } /****************************************************************************** * * FUNCTION: OslFindRsdpViaEfi * * PARAMETERS: None * * RETURN: RSDP address if found * * DESCRIPTION: Find RSDP address via EFI. * *****************************************************************************/ static ACPI_PHYSICAL_ADDRESS OslFindRsdpViaEfi ( void) { FILE *File; ACPI_PHYSICAL_ADDRESS Address = 0; File = fopen (EFI_SYSTAB, "r"); if (File) { Address = OslFindRsdpViaEfiByKeyword (File, "ACPI20"); if (!Address) { Address = OslFindRsdpViaEfiByKeyword (File, "ACPI"); } fclose (File); } return (Address); } /****************************************************************************** * * FUNCTION: OslLoadRsdp * * PARAMETERS: None * * RETURN: Status * * DESCRIPTION: Scan and load RSDP. * *****************************************************************************/ static ACPI_STATUS OslLoadRsdp ( void) { ACPI_TABLE_HEADER *MappedTable; UINT8 *RsdpAddress; ACPI_PHYSICAL_ADDRESS RsdpBase; ACPI_SIZE RsdpSize; /* Get RSDP from memory */ RsdpSize = sizeof (ACPI_TABLE_RSDP); if (Gbl_RsdpBase) { RsdpBase = Gbl_RsdpBase; } else { RsdpBase = OslFindRsdpViaEfi (); } if (!RsdpBase) { RsdpBase = ACPI_HI_RSDP_WINDOW_BASE; RsdpSize = ACPI_HI_RSDP_WINDOW_SIZE; } RsdpAddress = AcpiOsMapMemory (RsdpBase, RsdpSize); if (!RsdpAddress) { return (OslGetLastStatus (AE_BAD_ADDRESS)); } /* Search low memory for the RSDP */ MappedTable = ACPI_CAST_PTR (ACPI_TABLE_HEADER, AcpiTbScanMemoryForRsdp (RsdpAddress, RsdpSize)); if (!MappedTable) { AcpiOsUnmapMemory (RsdpAddress, RsdpSize); return (AE_NOT_FOUND); } Gbl_RsdpAddress = RsdpBase + (ACPI_CAST8 (MappedTable) - RsdpAddress); memcpy (&Gbl_Rsdp, MappedTable, sizeof (ACPI_TABLE_RSDP)); AcpiOsUnmapMemory (RsdpAddress, RsdpSize); 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); } if (!Gbl_DumpCustomizedTables) { /* 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, 0); if (ACPI_FAILURE (Status)) { return (Status); } Status = OslAddTableToList (ACPI_SIG_RSDT, 0); if (ACPI_FAILURE (Status)) { return (Status); } if (Gbl_Revision == 2) { Status = OslAddTableToList (ACPI_SIG_XSDT, 0); if (ACPI_FAILURE (Status)) { return (Status); } } Status = OslAddTableToList (ACPI_SIG_DSDT, 0); if (ACPI_FAILURE (Status)) { return (Status); } Status = OslAddTableToList (ACPI_SIG_FACS, 0); if (ACPI_FAILURE (Status)) { return (Status); } /* Add all tables found in the memory */ Status = OslListBiosTables (); if (ACPI_FAILURE (Status)) { return (Status); } } else { /* Add all tables found in the static directory */ Status = OslListCustomizedTables (STATIC_TABLE_DIR); if (ACPI_FAILURE (Status)) { return (Status); } } if (Gbl_DumpDynamicTables) { /* Add all dynamically loaded tables in the dynamic directory */ Status = OslListCustomizedTables (DYNAMIC_TABLE_DIR); 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. * * NOTE: This works on Linux as table customization does not modify the * addresses stored in RSDP/RSDT/XSDT/FADT. * *****************************************************************************/ static ACPI_STATUS OslListBiosTables ( void) { ACPI_TABLE_HEADER *MappedTable = NULL; UINT8 *TableData; UINT8 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 = (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) { continue; } Status = OslMapTable (TableAddress, NULL, &MappedTable); if (ACPI_FAILURE (Status)) { return (Status); } OslAddTableToList (MappedTable->Signature, 0); 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) { 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) { 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) { Status = AE_NO_MEMORY; goto Exit; } memcpy (LocalTable, MappedTable, TableLength); *Address = TableAddress; *Table = LocalTable; Exit: OslUnmapTable (MappedTable); return (Status); } /****************************************************************************** * * FUNCTION: OslListCustomizedTables * * PARAMETERS: Directory - Directory that contains the tables * * RETURN: Status; Table list is initialized if AE_OK. * * DESCRIPTION: Add ACPI tables to the table list from a directory. * *****************************************************************************/ static ACPI_STATUS OslListCustomizedTables ( char *Directory) { void *TableDir; UINT32 Instance; char TempName[ACPI_NAME_SIZE]; char *Filename; ACPI_STATUS Status = AE_OK; /* Open the requested directory */ TableDir = AcpiOsOpenDirectory (Directory, "*", REQUEST_FILE_ONLY); if (!TableDir) { return (OslGetLastStatus (AE_NOT_FOUND)); } /* Examine all entries in this directory */ while ((Filename = AcpiOsGetNextFilename (TableDir))) { /* Extract table name and instance number */ Status = OslTableNameFromFile (Filename, TempName, &Instance); /* Ignore meaningless files */ if (ACPI_FAILURE (Status)) { continue; } /* Add new info node to global table list */ Status = OslAddTableToList (TempName, Instance); if (ACPI_FAILURE (Status)) { break; } } AcpiOsCloseDirectory (TableDir); 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) { 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) { 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) { 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) { 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) { 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); } /****************************************************************************** * * FUNCTION: OslReadTableFromFile * * PARAMETERS: Filename - File that contains the desired table * FileOffset - Offset of the table in file * Signature - Optional ACPI Signature for desired table. * A null terminated 4-character string. * Table - Where a pointer to the table is returned * * RETURN: Status; Table buffer is returned if AE_OK. * * DESCRIPTION: Read a ACPI table from a file. * *****************************************************************************/ static ACPI_STATUS OslReadTableFromFile ( char *Filename, ACPI_SIZE FileOffset, char *Signature, ACPI_TABLE_HEADER **Table) { FILE *TableFile; ACPI_TABLE_HEADER Header; ACPI_TABLE_HEADER *LocalTable = NULL; UINT32 TableLength; INT32 Count; ACPI_STATUS Status = AE_OK; /* Open the file */ TableFile = fopen (Filename, "rb"); if (TableFile == NULL) { fprintf (stderr, "Could not open table file: %s\n", Filename); return (OslGetLastStatus (AE_NOT_FOUND)); } fseek (TableFile, FileOffset, SEEK_SET); /* Read the Table header to get the table length */ Count = fread (&Header, 1, sizeof (ACPI_TABLE_HEADER), TableFile); if (Count != sizeof (ACPI_TABLE_HEADER)) { fprintf (stderr, "Could not read table header: %s\n", Filename); Status = AE_BAD_HEADER; goto Exit; } /* If signature is specified, it must match the table */ if (Signature) { if (ACPI_VALIDATE_RSDP_SIG (Signature)) { if (!ACPI_VALIDATE_RSDP_SIG (Header.Signature)) { fprintf (stderr, "Incorrect RSDP signature: found %8.8s\n", Header.Signature); Status = AE_BAD_SIGNATURE; goto Exit; } } else if (!ACPI_COMPARE_NAME (Signature, Header.Signature)) { fprintf (stderr, "Incorrect signature: Expecting %4.4s, found %4.4s\n", Signature, Header.Signature); Status = AE_BAD_SIGNATURE; goto Exit; } } TableLength = ApGetTableLength (&Header); if (TableLength == 0) { Status = AE_BAD_HEADER; goto Exit; } /* Read the entire table into a local buffer */ LocalTable = calloc (1, TableLength); if (!LocalTable) { fprintf (stderr, "%4.4s: Could not allocate buffer for table of length %X\n", Header.Signature, TableLength); Status = AE_NO_MEMORY; goto Exit; } fseek (TableFile, FileOffset, SEEK_SET); Count = fread (LocalTable, 1, TableLength, TableFile); if (Count != TableLength) { fprintf (stderr, "%4.4s: Could not read table content\n", Header.Signature); Status = AE_INVALID_TABLE_LENGTH; goto Exit; } /* Validate checksum */ (void) ApIsValidChecksum (LocalTable); Exit: fclose (TableFile); *Table = LocalTable; return (Status); } /****************************************************************************** * * FUNCTION: OslGetCustomizedTable * * PARAMETERS: Pathname - Directory to find Linux customized table * 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 is returned if AE_OK. * AE_LIMIT: Instance is beyond valid limit * AE_NOT_FOUND: A table with the signature was not found * * DESCRIPTION: Get an OS customized table. * *****************************************************************************/ static ACPI_STATUS OslGetCustomizedTable ( char *Pathname, char *Signature, UINT32 Instance, ACPI_TABLE_HEADER **Table, ACPI_PHYSICAL_ADDRESS *Address) { void *TableDir; UINT32 CurrentInstance = 0; char TempName[ACPI_NAME_SIZE]; char TableFilename[PATH_MAX]; char *Filename; ACPI_STATUS Status; /* Open the directory for customized tables */ TableDir = AcpiOsOpenDirectory (Pathname, "*", REQUEST_FILE_ONLY); if (!TableDir) { return (OslGetLastStatus (AE_NOT_FOUND)); } /* Attempt to find the table in the directory */ while ((Filename = AcpiOsGetNextFilename (TableDir))) { /* Ignore meaningless files */ if (!ACPI_COMPARE_NAME (Filename, Signature)) { continue; } /* Extract table name and instance number */ Status = OslTableNameFromFile (Filename, TempName, &CurrentInstance); /* Ignore meaningless files */ if (ACPI_FAILURE (Status) || CurrentInstance != Instance) { continue; } /* Create the table pathname */ if (Instance != 0) { sprintf (TableFilename, "%s/%4.4s%d", Pathname, TempName, Instance); } else { sprintf (TableFilename, "%s/%4.4s", Pathname, TempName); } break; } AcpiOsCloseDirectory (TableDir); if (!Filename) { return (AE_LIMIT); } /* There is no physical address saved for customized tables, use zero */ *Address = 0; Status = OslReadTableFromFile (TableFilename, 0, NULL, Table); return (Status); }