1374 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1374 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * Module Name: oslinuxtbl - Linux OSL for obtaining ACPI tables
 | |
|  *
 | |
|  * Copyright (C) 2000 - 2022, Intel Corp.
 | |
|  *
 | |
|  *****************************************************************************/
 | |
| 
 | |
| #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;
 | |
| 	u32 instance;
 | |
| 	char signature[ACPI_NAMESEG_SIZE];
 | |
| 
 | |
| } osl_table_info;
 | |
| 
 | |
| /* Local prototypes */
 | |
| 
 | |
| static acpi_status osl_table_initialize(void);
 | |
| 
 | |
| static acpi_status
 | |
| osl_table_name_from_file(char *filename, char *signature, u32 *instance);
 | |
| 
 | |
| static acpi_status osl_add_table_to_list(char *signature, u32 instance);
 | |
| 
 | |
| static acpi_status
 | |
| osl_read_table_from_file(char *filename,
 | |
| 			 acpi_size file_offset,
 | |
| 			 struct acpi_table_header **table);
 | |
| 
 | |
| static acpi_status
 | |
| osl_map_table(acpi_size address,
 | |
| 	      char *signature, struct acpi_table_header **table);
 | |
| 
 | |
| static void osl_unmap_table(struct acpi_table_header *table);
 | |
| 
 | |
| static acpi_physical_address
 | |
| osl_find_rsdp_via_efi_by_keyword(FILE * file, const char *keyword);
 | |
| 
 | |
| static acpi_physical_address osl_find_rsdp_via_efi(void);
 | |
| 
 | |
| static acpi_status osl_load_rsdp(void);
 | |
| 
 | |
| static acpi_status osl_list_customized_tables(char *directory);
 | |
| 
 | |
| static acpi_status
 | |
| osl_get_customized_table(char *pathname,
 | |
| 			 char *signature,
 | |
| 			 u32 instance,
 | |
| 			 struct acpi_table_header **table,
 | |
| 			 acpi_physical_address *address);
 | |
| 
 | |
| static acpi_status osl_list_bios_tables(void);
 | |
| 
 | |
| static acpi_status
 | |
| osl_get_bios_table(char *signature,
 | |
| 		   u32 instance,
 | |
| 		   struct acpi_table_header **table,
 | |
| 		   acpi_physical_address *address);
 | |
| 
 | |
| static acpi_status osl_get_last_status(acpi_status default_status);
 | |
| 
 | |
| /* 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? */
 | |
| 
 | |
| u8 gbl_dump_dynamic_tables = TRUE;
 | |
| 
 | |
| /* Initialization flags */
 | |
| 
 | |
| u8 gbl_table_list_initialized = FALSE;
 | |
| 
 | |
| /* Local copies of main ACPI tables */
 | |
| 
 | |
| struct acpi_table_rsdp gbl_rsdp;
 | |
| struct acpi_table_fadt *gbl_fadt = NULL;
 | |
| struct acpi_table_rsdt *gbl_rsdt = NULL;
 | |
| struct acpi_table_xsdt *gbl_xsdt = NULL;
 | |
| 
 | |
| /* Table addresses */
 | |
| 
 | |
| acpi_physical_address gbl_fadt_address = 0;
 | |
| acpi_physical_address gbl_rsdp_address = 0;
 | |
| 
 | |
| /* Revision of RSD PTR */
 | |
| 
 | |
| u8 gbl_revision = 0;
 | |
| 
 | |
| struct osl_table_info *gbl_table_list_head = NULL;
 | |
| u32 gbl_table_count = 0;
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_get_last_status
 | |
|  *
 | |
|  * PARAMETERS:  default_status  - Default error status to return
 | |
|  *
 | |
|  * RETURN:      Status; Converted from errno.
 | |
|  *
 | |
|  * DESCRIPTION: Get last errno and convert it to acpi_status.
 | |
|  *
 | |
|  *****************************************************************************/
 | |
| 
 | |
| static acpi_status osl_get_last_status(acpi_status default_status)
 | |
| {
 | |
| 
 | |
| 	switch (errno) {
 | |
| 	case EACCES:
 | |
| 	case EPERM:
 | |
| 
 | |
| 		return (AE_ACCESS);
 | |
| 
 | |
| 	case ENOENT:
 | |
| 
 | |
| 		return (AE_NOT_FOUND);
 | |
| 
 | |
| 	case ENOMEM:
 | |
| 
 | |
| 		return (AE_NO_MEMORY);
 | |
| 
 | |
| 	default:
 | |
| 
 | |
| 		return (default_status);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    acpi_os_get_table_by_address
 | |
|  *
 | |
|  * 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
 | |
| acpi_os_get_table_by_address(acpi_physical_address address,
 | |
| 			     struct acpi_table_header **table)
 | |
| {
 | |
| 	u32 table_length;
 | |
| 	struct acpi_table_header *mapped_table;
 | |
| 	struct acpi_table_header *local_table = NULL;
 | |
| 	acpi_status status = AE_OK;
 | |
| 
 | |
| 	/* Get main ACPI tables from memory on first invocation of this function */
 | |
| 
 | |
| 	status = osl_table_initialize();
 | |
| 	if (ACPI_FAILURE(status)) {
 | |
| 		return (status);
 | |
| 	}
 | |
| 
 | |
| 	/* Map the table and validate it */
 | |
| 
 | |
| 	status = osl_map_table(address, NULL, &mapped_table);
 | |
| 	if (ACPI_FAILURE(status)) {
 | |
| 		return (status);
 | |
| 	}
 | |
| 
 | |
| 	/* Copy table to local buffer and return it */
 | |
| 
 | |
| 	table_length = ap_get_table_length(mapped_table);
 | |
| 	if (table_length == 0) {
 | |
| 		status = AE_BAD_HEADER;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	local_table = calloc(1, table_length);
 | |
| 	if (!local_table) {
 | |
| 		status = AE_NO_MEMORY;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	memcpy(local_table, mapped_table, table_length);
 | |
| 
 | |
| exit:
 | |
| 	osl_unmap_table(mapped_table);
 | |
| 	*table = local_table;
 | |
| 	return (status);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    acpi_os_get_table_by_name
 | |
|  *
 | |
|  * 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
 | |
| acpi_os_get_table_by_name(char *signature,
 | |
| 			  u32 instance,
 | |
| 			  struct acpi_table_header **table,
 | |
| 			  acpi_physical_address *address)
 | |
| {
 | |
| 	acpi_status status;
 | |
| 
 | |
| 	/* Get main ACPI tables from memory on first invocation of this function */
 | |
| 
 | |
| 	status = osl_table_initialize();
 | |
| 	if (ACPI_FAILURE(status)) {
 | |
| 		return (status);
 | |
| 	}
 | |
| 
 | |
| 	/* Not a main ACPI table, attempt to extract it from the RSDT/XSDT */
 | |
| 
 | |
| 	if (!gbl_dump_customized_tables) {
 | |
| 
 | |
| 		/* Attempt to get the table from the memory */
 | |
| 
 | |
| 		status =
 | |
| 		    osl_get_bios_table(signature, instance, table, address);
 | |
| 	} else {
 | |
| 		/* Attempt to get the table from the static directory */
 | |
| 
 | |
| 		status = osl_get_customized_table(STATIC_TABLE_DIR, signature,
 | |
| 						  instance, table, address);
 | |
| 	}
 | |
| 
 | |
| 	if (ACPI_FAILURE(status) && status == AE_LIMIT) {
 | |
| 		if (gbl_dump_dynamic_tables) {
 | |
| 
 | |
| 			/* Attempt to get a dynamic table */
 | |
| 
 | |
| 			status =
 | |
| 			    osl_get_customized_table(DYNAMIC_TABLE_DIR,
 | |
| 						     signature, instance, table,
 | |
| 						     address);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return (status);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_add_table_to_list
 | |
|  *
 | |
|  * 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 osl_add_table_to_list(char *signature, u32 instance)
 | |
| {
 | |
| 	struct osl_table_info *new_info;
 | |
| 	struct osl_table_info *next;
 | |
| 	u32 next_instance = 0;
 | |
| 	u8 found = FALSE;
 | |
| 
 | |
| 	new_info = calloc(1, sizeof(struct osl_table_info));
 | |
| 	if (!new_info) {
 | |
| 		return (AE_NO_MEMORY);
 | |
| 	}
 | |
| 
 | |
| 	ACPI_COPY_NAMESEG(new_info->signature, signature);
 | |
| 
 | |
| 	if (!gbl_table_list_head) {
 | |
| 		gbl_table_list_head = new_info;
 | |
| 	} else {
 | |
| 		next = gbl_table_list_head;
 | |
| 		while (1) {
 | |
| 			if (ACPI_COMPARE_NAMESEG(next->signature, signature)) {
 | |
| 				if (next->instance == instance) {
 | |
| 					found = TRUE;
 | |
| 				}
 | |
| 				if (next->instance >= next_instance) {
 | |
| 					next_instance = next->instance + 1;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (!next->next) {
 | |
| 				break;
 | |
| 			}
 | |
| 			next = next->next;
 | |
| 		}
 | |
| 		next->next = new_info;
 | |
| 	}
 | |
| 
 | |
| 	if (found) {
 | |
| 		if (instance) {
 | |
| 			fprintf(stderr,
 | |
| 				"%4.4s: Warning unmatched table instance %d, expected %d\n",
 | |
| 				signature, instance, next_instance);
 | |
| 		}
 | |
| 		instance = next_instance;
 | |
| 	}
 | |
| 
 | |
| 	new_info->instance = instance;
 | |
| 	gbl_table_count++;
 | |
| 
 | |
| 	return (AE_OK);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    acpi_os_get_table_by_index
 | |
|  *
 | |
|  * 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
 | |
| acpi_os_get_table_by_index(u32 index,
 | |
| 			   struct acpi_table_header **table,
 | |
| 			   u32 *instance, acpi_physical_address *address)
 | |
| {
 | |
| 	struct osl_table_info *info;
 | |
| 	acpi_status status;
 | |
| 	u32 i;
 | |
| 
 | |
| 	/* Get main ACPI tables from memory on first invocation of this function */
 | |
| 
 | |
| 	status = osl_table_initialize();
 | |
| 	if (ACPI_FAILURE(status)) {
 | |
| 		return (status);
 | |
| 	}
 | |
| 
 | |
| 	/* Validate Index */
 | |
| 
 | |
| 	if (index >= gbl_table_count) {
 | |
| 		return (AE_LIMIT);
 | |
| 	}
 | |
| 
 | |
| 	/* Point to the table list entry specified by the Index argument */
 | |
| 
 | |
| 	info = gbl_table_list_head;
 | |
| 	for (i = 0; i < index; i++) {
 | |
| 		info = info->next;
 | |
| 	}
 | |
| 
 | |
| 	/* Now we can just get the table via the signature */
 | |
| 
 | |
| 	status = acpi_os_get_table_by_name(info->signature, info->instance,
 | |
| 					   table, address);
 | |
| 
 | |
| 	if (ACPI_SUCCESS(status)) {
 | |
| 		*instance = info->instance;
 | |
| 	}
 | |
| 	return (status);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_find_rsdp_via_efi_by_keyword
 | |
|  *
 | |
|  * 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
 | |
| osl_find_rsdp_via_efi_by_keyword(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:    osl_find_rsdp_via_efi
 | |
|  *
 | |
|  * PARAMETERS:  None
 | |
|  *
 | |
|  * RETURN:      RSDP address if found
 | |
|  *
 | |
|  * DESCRIPTION: Find RSDP address via EFI.
 | |
|  *
 | |
|  *****************************************************************************/
 | |
| 
 | |
| static acpi_physical_address osl_find_rsdp_via_efi(void)
 | |
| {
 | |
| 	FILE *file;
 | |
| 	acpi_physical_address address = 0;
 | |
| 
 | |
| 	file = fopen(EFI_SYSTAB, "r");
 | |
| 	if (file) {
 | |
| 		address = osl_find_rsdp_via_efi_by_keyword(file, "ACPI20");
 | |
| 		if (!address) {
 | |
| 			address =
 | |
| 			    osl_find_rsdp_via_efi_by_keyword(file, "ACPI");
 | |
| 		}
 | |
| 		fclose(file);
 | |
| 	}
 | |
| 
 | |
| 	return (address);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_load_rsdp
 | |
|  *
 | |
|  * PARAMETERS:  None
 | |
|  *
 | |
|  * RETURN:      Status
 | |
|  *
 | |
|  * DESCRIPTION: Scan and load RSDP.
 | |
|  *
 | |
|  *****************************************************************************/
 | |
| 
 | |
| static acpi_status osl_load_rsdp(void)
 | |
| {
 | |
| 	struct acpi_table_header *mapped_table;
 | |
| 	u8 *rsdp_address;
 | |
| 	acpi_physical_address rsdp_base;
 | |
| 	acpi_size rsdp_size;
 | |
| 
 | |
| 	/* Get RSDP from memory */
 | |
| 
 | |
| 	rsdp_size = sizeof(struct acpi_table_rsdp);
 | |
| 	if (gbl_rsdp_base) {
 | |
| 		rsdp_base = gbl_rsdp_base;
 | |
| 	} else {
 | |
| 		rsdp_base = osl_find_rsdp_via_efi();
 | |
| 	}
 | |
| 
 | |
| 	if (!rsdp_base) {
 | |
| 		rsdp_base = ACPI_HI_RSDP_WINDOW_BASE;
 | |
| 		rsdp_size = ACPI_HI_RSDP_WINDOW_SIZE;
 | |
| 	}
 | |
| 
 | |
| 	rsdp_address = acpi_os_map_memory(rsdp_base, rsdp_size);
 | |
| 	if (!rsdp_address) {
 | |
| 		return (osl_get_last_status(AE_BAD_ADDRESS));
 | |
| 	}
 | |
| 
 | |
| 	/* Search low memory for the RSDP */
 | |
| 
 | |
| 	mapped_table = ACPI_CAST_PTR(struct acpi_table_header,
 | |
| 				     acpi_tb_scan_memory_for_rsdp(rsdp_address,
 | |
| 								  rsdp_size));
 | |
| 	if (!mapped_table) {
 | |
| 		acpi_os_unmap_memory(rsdp_address, rsdp_size);
 | |
| 		return (AE_NOT_FOUND);
 | |
| 	}
 | |
| 
 | |
| 	gbl_rsdp_address =
 | |
| 	    rsdp_base + (ACPI_CAST8(mapped_table) - rsdp_address);
 | |
| 
 | |
| 	memcpy(&gbl_rsdp, mapped_table, sizeof(struct acpi_table_rsdp));
 | |
| 	acpi_os_unmap_memory(rsdp_address, rsdp_size);
 | |
| 
 | |
| 	return (AE_OK);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_can_use_xsdt
 | |
|  *
 | |
|  * 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 u8 osl_can_use_xsdt(void)
 | |
| {
 | |
| 	if (gbl_revision && !acpi_gbl_do_not_use_xsdt) {
 | |
| 		return (TRUE);
 | |
| 	} else {
 | |
| 		return (FALSE);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_table_initialize
 | |
|  *
 | |
|  * 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 osl_table_initialize(void)
 | |
| {
 | |
| 	acpi_status status;
 | |
| 	acpi_physical_address address;
 | |
| 
 | |
| 	if (gbl_table_list_initialized) {
 | |
| 		return (AE_OK);
 | |
| 	}
 | |
| 
 | |
| 	if (!gbl_dump_customized_tables) {
 | |
| 
 | |
| 		/* Get RSDP from memory */
 | |
| 
 | |
| 		status = osl_load_rsdp();
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			return (status);
 | |
| 		}
 | |
| 
 | |
| 		/* Get XSDT from memory */
 | |
| 
 | |
| 		if (gbl_rsdp.revision && !gbl_do_not_dump_xsdt) {
 | |
| 			if (gbl_xsdt) {
 | |
| 				free(gbl_xsdt);
 | |
| 				gbl_xsdt = NULL;
 | |
| 			}
 | |
| 
 | |
| 			gbl_revision = 2;
 | |
| 			status = osl_get_bios_table(ACPI_SIG_XSDT, 0,
 | |
| 						    ACPI_CAST_PTR(struct
 | |
| 								  acpi_table_header
 | |
| 								  *, &gbl_xsdt),
 | |
| 						    &address);
 | |
| 			if (ACPI_FAILURE(status)) {
 | |
| 				return (status);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* Get RSDT from memory */
 | |
| 
 | |
| 		if (gbl_rsdp.rsdt_physical_address) {
 | |
| 			if (gbl_rsdt) {
 | |
| 				free(gbl_rsdt);
 | |
| 				gbl_rsdt = NULL;
 | |
| 			}
 | |
| 
 | |
| 			status = osl_get_bios_table(ACPI_SIG_RSDT, 0,
 | |
| 						    ACPI_CAST_PTR(struct
 | |
| 								  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 = osl_get_bios_table(ACPI_SIG_FADT, 0,
 | |
| 					    ACPI_CAST_PTR(struct
 | |
| 							  acpi_table_header *,
 | |
| 							  &gbl_fadt),
 | |
| 					    &gbl_fadt_address);
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			return (status);
 | |
| 		}
 | |
| 
 | |
| 		/* Add mandatory tables to global table list first */
 | |
| 
 | |
| 		status = osl_add_table_to_list(ACPI_RSDP_NAME, 0);
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			return (status);
 | |
| 		}
 | |
| 
 | |
| 		status = osl_add_table_to_list(ACPI_SIG_RSDT, 0);
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			return (status);
 | |
| 		}
 | |
| 
 | |
| 		if (gbl_revision == 2) {
 | |
| 			status = osl_add_table_to_list(ACPI_SIG_XSDT, 0);
 | |
| 			if (ACPI_FAILURE(status)) {
 | |
| 				return (status);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		status = osl_add_table_to_list(ACPI_SIG_DSDT, 0);
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			return (status);
 | |
| 		}
 | |
| 
 | |
| 		status = osl_add_table_to_list(ACPI_SIG_FACS, 0);
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			return (status);
 | |
| 		}
 | |
| 
 | |
| 		/* Add all tables found in the memory */
 | |
| 
 | |
| 		status = osl_list_bios_tables();
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			return (status);
 | |
| 		}
 | |
| 	} else {
 | |
| 		/* Add all tables found in the static directory */
 | |
| 
 | |
| 		status = osl_list_customized_tables(STATIC_TABLE_DIR);
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			return (status);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (gbl_dump_dynamic_tables) {
 | |
| 
 | |
| 		/* Add all dynamically loaded tables in the dynamic directory */
 | |
| 
 | |
| 		status = osl_list_customized_tables(DYNAMIC_TABLE_DIR);
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			return (status);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	gbl_table_list_initialized = TRUE;
 | |
| 	return (AE_OK);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_list_bios_tables
 | |
|  *
 | |
|  * 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 osl_list_bios_tables(void)
 | |
| {
 | |
| 	struct acpi_table_header *mapped_table = NULL;
 | |
| 	u8 *table_data;
 | |
| 	u8 number_of_tables;
 | |
| 	u8 item_size;
 | |
| 	acpi_physical_address table_address = 0;
 | |
| 	acpi_status status = AE_OK;
 | |
| 	u32 i;
 | |
| 
 | |
| 	if (osl_can_use_xsdt()) {
 | |
| 		item_size = sizeof(u64);
 | |
| 		table_data =
 | |
| 		    ACPI_CAST8(gbl_xsdt) + sizeof(struct acpi_table_header);
 | |
| 		number_of_tables =
 | |
| 		    (u8)((gbl_xsdt->header.length -
 | |
| 			  sizeof(struct acpi_table_header))
 | |
| 			 / item_size);
 | |
| 	} else {		/* Use RSDT if XSDT is not available */
 | |
| 
 | |
| 		item_size = sizeof(u32);
 | |
| 		table_data =
 | |
| 		    ACPI_CAST8(gbl_rsdt) + sizeof(struct acpi_table_header);
 | |
| 		number_of_tables =
 | |
| 		    (u8)((gbl_rsdt->header.length -
 | |
| 			  sizeof(struct acpi_table_header))
 | |
| 			 / item_size);
 | |
| 	}
 | |
| 
 | |
| 	/* Search RSDT/XSDT for the requested table */
 | |
| 
 | |
| 	for (i = 0; i < number_of_tables; ++i, table_data += item_size) {
 | |
| 		if (osl_can_use_xsdt()) {
 | |
| 			table_address =
 | |
| 			    (acpi_physical_address)(*ACPI_CAST64(table_data));
 | |
| 		} else {
 | |
| 			table_address =
 | |
| 			    (acpi_physical_address)(*ACPI_CAST32(table_data));
 | |
| 		}
 | |
| 
 | |
| 		/* Skip NULL entries in RSDT/XSDT */
 | |
| 
 | |
| 		if (table_address == 0) {
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		status = osl_map_table(table_address, NULL, &mapped_table);
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			return (status);
 | |
| 		}
 | |
| 
 | |
| 		osl_add_table_to_list(mapped_table->signature, 0);
 | |
| 		osl_unmap_table(mapped_table);
 | |
| 	}
 | |
| 
 | |
| 	return (AE_OK);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_get_bios_table
 | |
|  *
 | |
|  * 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
 | |
| osl_get_bios_table(char *signature,
 | |
| 		   u32 instance,
 | |
| 		   struct acpi_table_header **table,
 | |
| 		   acpi_physical_address *address)
 | |
| {
 | |
| 	struct acpi_table_header *local_table = NULL;
 | |
| 	struct acpi_table_header *mapped_table = NULL;
 | |
| 	u8 *table_data;
 | |
| 	u8 number_of_tables;
 | |
| 	u8 item_size;
 | |
| 	u32 current_instance = 0;
 | |
| 	acpi_physical_address table_address;
 | |
| 	acpi_physical_address first_table_address = 0;
 | |
| 	u32 table_length = 0;
 | |
| 	acpi_status status = AE_OK;
 | |
| 	u32 i;
 | |
| 
 | |
| 	/* Handle special tables whose addresses are not in RSDT/XSDT */
 | |
| 
 | |
| 	if (ACPI_COMPARE_NAMESEG(signature, ACPI_RSDP_NAME) ||
 | |
| 	    ACPI_COMPARE_NAMESEG(signature, ACPI_SIG_RSDT) ||
 | |
| 	    ACPI_COMPARE_NAMESEG(signature, ACPI_SIG_XSDT) ||
 | |
| 	    ACPI_COMPARE_NAMESEG(signature, ACPI_SIG_DSDT) ||
 | |
| 	    ACPI_COMPARE_NAMESEG(signature, ACPI_SIG_FACS)) {
 | |
| 
 | |
| find_next_instance:
 | |
| 
 | |
| 		table_address = 0;
 | |
| 
 | |
| 		/*
 | |
| 		 * 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_NAMESEG(signature, ACPI_SIG_DSDT)) {
 | |
| 			if (current_instance < 2) {
 | |
| 				if ((gbl_fadt->header.length >=
 | |
| 				     MIN_FADT_FOR_XDSDT) && gbl_fadt->Xdsdt
 | |
| 				    && current_instance == 0) {
 | |
| 					table_address =
 | |
| 					    (acpi_physical_address)gbl_fadt->
 | |
| 					    Xdsdt;
 | |
| 				} else
 | |
| 				    if ((gbl_fadt->header.length >=
 | |
| 					 MIN_FADT_FOR_DSDT)
 | |
| 					&& gbl_fadt->dsdt !=
 | |
| 					first_table_address) {
 | |
| 					table_address =
 | |
| 					    (acpi_physical_address)gbl_fadt->
 | |
| 					    dsdt;
 | |
| 				}
 | |
| 			}
 | |
| 		} else if (ACPI_COMPARE_NAMESEG(signature, ACPI_SIG_FACS)) {
 | |
| 			if (current_instance < 2) {
 | |
| 				if ((gbl_fadt->header.length >=
 | |
| 				     MIN_FADT_FOR_XFACS) && gbl_fadt->Xfacs
 | |
| 				    && current_instance == 0) {
 | |
| 					table_address =
 | |
| 					    (acpi_physical_address)gbl_fadt->
 | |
| 					    Xfacs;
 | |
| 				} else
 | |
| 				    if ((gbl_fadt->header.length >=
 | |
| 					 MIN_FADT_FOR_FACS)
 | |
| 					&& gbl_fadt->facs !=
 | |
| 					first_table_address) {
 | |
| 					table_address =
 | |
| 					    (acpi_physical_address)gbl_fadt->
 | |
| 					    facs;
 | |
| 				}
 | |
| 			}
 | |
| 		} else if (ACPI_COMPARE_NAMESEG(signature, ACPI_SIG_XSDT)) {
 | |
| 			if (!gbl_revision) {
 | |
| 				return (AE_BAD_SIGNATURE);
 | |
| 			}
 | |
| 			if (current_instance == 0) {
 | |
| 				table_address =
 | |
| 				    (acpi_physical_address)gbl_rsdp.
 | |
| 				    xsdt_physical_address;
 | |
| 			}
 | |
| 		} else if (ACPI_COMPARE_NAMESEG(signature, ACPI_SIG_RSDT)) {
 | |
| 			if (current_instance == 0) {
 | |
| 				table_address =
 | |
| 				    (acpi_physical_address)gbl_rsdp.
 | |
| 				    rsdt_physical_address;
 | |
| 			}
 | |
| 		} else {
 | |
| 			if (current_instance == 0) {
 | |
| 				table_address =
 | |
| 				    (acpi_physical_address)gbl_rsdp_address;
 | |
| 				signature = ACPI_SIG_RSDP;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (table_address == 0) {
 | |
| 			goto exit_find_table;
 | |
| 		}
 | |
| 
 | |
| 		/* Now we can get the requested special table */
 | |
| 
 | |
| 		status = osl_map_table(table_address, signature, &mapped_table);
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			return (status);
 | |
| 		}
 | |
| 
 | |
| 		table_length = ap_get_table_length(mapped_table);
 | |
| 		if (first_table_address == 0) {
 | |
| 			first_table_address = table_address;
 | |
| 		}
 | |
| 
 | |
| 		/* Match table instance */
 | |
| 
 | |
| 		if (current_instance != instance) {
 | |
| 			osl_unmap_table(mapped_table);
 | |
| 			mapped_table = NULL;
 | |
| 			current_instance++;
 | |
| 			goto find_next_instance;
 | |
| 		}
 | |
| 	} else {		/* Case for a normal ACPI table */
 | |
| 
 | |
| 		if (osl_can_use_xsdt()) {
 | |
| 			item_size = sizeof(u64);
 | |
| 			table_data =
 | |
| 			    ACPI_CAST8(gbl_xsdt) +
 | |
| 			    sizeof(struct acpi_table_header);
 | |
| 			number_of_tables =
 | |
| 			    (u8)((gbl_xsdt->header.length -
 | |
| 				  sizeof(struct acpi_table_header))
 | |
| 				 / item_size);
 | |
| 		} else {	/* Use RSDT if XSDT is not available */
 | |
| 
 | |
| 			item_size = sizeof(u32);
 | |
| 			table_data =
 | |
| 			    ACPI_CAST8(gbl_rsdt) +
 | |
| 			    sizeof(struct acpi_table_header);
 | |
| 			number_of_tables =
 | |
| 			    (u8)((gbl_rsdt->header.length -
 | |
| 				  sizeof(struct acpi_table_header))
 | |
| 				 / item_size);
 | |
| 		}
 | |
| 
 | |
| 		/* Search RSDT/XSDT for the requested table */
 | |
| 
 | |
| 		for (i = 0; i < number_of_tables; ++i, table_data += item_size) {
 | |
| 			if (osl_can_use_xsdt()) {
 | |
| 				table_address =
 | |
| 				    (acpi_physical_address)(*ACPI_CAST64
 | |
| 							    (table_data));
 | |
| 			} else {
 | |
| 				table_address =
 | |
| 				    (acpi_physical_address)(*ACPI_CAST32
 | |
| 							    (table_data));
 | |
| 			}
 | |
| 
 | |
| 			/* Skip NULL entries in RSDT/XSDT */
 | |
| 
 | |
| 			if (table_address == 0) {
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			status =
 | |
| 			    osl_map_table(table_address, NULL, &mapped_table);
 | |
| 			if (ACPI_FAILURE(status)) {
 | |
| 				return (status);
 | |
| 			}
 | |
| 			table_length = mapped_table->length;
 | |
| 
 | |
| 			/* Does this table match the requested signature? */
 | |
| 
 | |
| 			if (!ACPI_COMPARE_NAMESEG
 | |
| 			    (mapped_table->signature, signature)) {
 | |
| 				osl_unmap_table(mapped_table);
 | |
| 				mapped_table = NULL;
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			/* Match table instance (for SSDT/UEFI tables) */
 | |
| 
 | |
| 			if (current_instance != instance) {
 | |
| 				osl_unmap_table(mapped_table);
 | |
| 				mapped_table = NULL;
 | |
| 				current_instance++;
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| exit_find_table:
 | |
| 
 | |
| 	if (!mapped_table) {
 | |
| 		return (AE_LIMIT);
 | |
| 	}
 | |
| 
 | |
| 	if (table_length == 0) {
 | |
| 		status = AE_BAD_HEADER;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	/* Copy table to local buffer and return it */
 | |
| 
 | |
| 	local_table = calloc(1, table_length);
 | |
| 	if (!local_table) {
 | |
| 		status = AE_NO_MEMORY;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	memcpy(local_table, mapped_table, table_length);
 | |
| 	*address = table_address;
 | |
| 	*table = local_table;
 | |
| 
 | |
| exit:
 | |
| 	osl_unmap_table(mapped_table);
 | |
| 	return (status);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_list_customized_tables
 | |
|  *
 | |
|  * 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 osl_list_customized_tables(char *directory)
 | |
| {
 | |
| 	void *table_dir;
 | |
| 	u32 instance;
 | |
| 	char temp_name[ACPI_NAMESEG_SIZE];
 | |
| 	char *filename;
 | |
| 	acpi_status status = AE_OK;
 | |
| 
 | |
| 	/* Open the requested directory */
 | |
| 
 | |
| 	table_dir = acpi_os_open_directory(directory, "*", REQUEST_FILE_ONLY);
 | |
| 	if (!table_dir) {
 | |
| 		return (osl_get_last_status(AE_NOT_FOUND));
 | |
| 	}
 | |
| 
 | |
| 	/* Examine all entries in this directory */
 | |
| 
 | |
| 	while ((filename = acpi_os_get_next_filename(table_dir))) {
 | |
| 
 | |
| 		/* Extract table name and instance number */
 | |
| 
 | |
| 		status =
 | |
| 		    osl_table_name_from_file(filename, temp_name, &instance);
 | |
| 
 | |
| 		/* Ignore meaningless files */
 | |
| 
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* Add new info node to global table list */
 | |
| 
 | |
| 		status = osl_add_table_to_list(temp_name, instance);
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	acpi_os_close_directory(table_dir);
 | |
| 	return (status);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_map_table
 | |
|  *
 | |
|  * 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
 | |
| osl_map_table(acpi_size address,
 | |
| 	      char *signature, struct acpi_table_header **table)
 | |
| {
 | |
| 	struct acpi_table_header *mapped_table;
 | |
| 	u32 length;
 | |
| 
 | |
| 	if (!address) {
 | |
| 		return (AE_BAD_ADDRESS);
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Map the header so we can get the table length.
 | |
| 	 * Use sizeof (struct acpi_table_header) as:
 | |
| 	 * 1. it is bigger than 24 to include RSDP->Length
 | |
| 	 * 2. it is smaller than sizeof (struct acpi_table_rsdp)
 | |
| 	 */
 | |
| 	mapped_table =
 | |
| 	    acpi_os_map_memory(address, sizeof(struct acpi_table_header));
 | |
| 	if (!mapped_table) {
 | |
| 		fprintf(stderr, "Could not map table header at 0x%8.8X%8.8X\n",
 | |
| 			ACPI_FORMAT_UINT64(address));
 | |
| 		return (osl_get_last_status(AE_BAD_ADDRESS));
 | |
| 	}
 | |
| 
 | |
| 	/* If specified, signature must match */
 | |
| 
 | |
| 	if (signature) {
 | |
| 		if (ACPI_VALIDATE_RSDP_SIG(signature)) {
 | |
| 			if (!ACPI_VALIDATE_RSDP_SIG(mapped_table->signature)) {
 | |
| 				acpi_os_unmap_memory(mapped_table,
 | |
| 						     sizeof(struct
 | |
| 							    acpi_table_header));
 | |
| 				return (AE_BAD_SIGNATURE);
 | |
| 			}
 | |
| 		} else
 | |
| 		    if (!ACPI_COMPARE_NAMESEG
 | |
| 			(signature, mapped_table->signature)) {
 | |
| 			acpi_os_unmap_memory(mapped_table,
 | |
| 					     sizeof(struct acpi_table_header));
 | |
| 			return (AE_BAD_SIGNATURE);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Map the entire table */
 | |
| 
 | |
| 	length = ap_get_table_length(mapped_table);
 | |
| 	acpi_os_unmap_memory(mapped_table, sizeof(struct acpi_table_header));
 | |
| 	if (length == 0) {
 | |
| 		return (AE_BAD_HEADER);
 | |
| 	}
 | |
| 
 | |
| 	mapped_table = acpi_os_map_memory(address, length);
 | |
| 	if (!mapped_table) {
 | |
| 		fprintf(stderr,
 | |
| 			"Could not map table at 0x%8.8X%8.8X length %8.8X\n",
 | |
| 			ACPI_FORMAT_UINT64(address), length);
 | |
| 		return (osl_get_last_status(AE_INVALID_TABLE_LENGTH));
 | |
| 	}
 | |
| 
 | |
| 	(void)ap_is_valid_checksum(mapped_table);
 | |
| 
 | |
| 	*table = mapped_table;
 | |
| 	return (AE_OK);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_unmap_table
 | |
|  *
 | |
|  * PARAMETERS:  table               - A pointer to the mapped table
 | |
|  *
 | |
|  * RETURN:      None
 | |
|  *
 | |
|  * DESCRIPTION: Unmap entire ACPI table.
 | |
|  *
 | |
|  *****************************************************************************/
 | |
| 
 | |
| static void osl_unmap_table(struct acpi_table_header *table)
 | |
| {
 | |
| 	if (table) {
 | |
| 		acpi_os_unmap_memory(table, ap_get_table_length(table));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_table_name_from_file
 | |
|  *
 | |
|  * 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
 | |
| osl_table_name_from_file(char *filename, char *signature, u32 *instance)
 | |
| {
 | |
| 
 | |
| 	/* Ignore meaningless files */
 | |
| 
 | |
| 	if (strlen(filename) < ACPI_NAMESEG_SIZE) {
 | |
| 		return (AE_BAD_SIGNATURE);
 | |
| 	}
 | |
| 
 | |
| 	/* Extract instance number */
 | |
| 
 | |
| 	if (isdigit((int)filename[ACPI_NAMESEG_SIZE])) {
 | |
| 		sscanf(&filename[ACPI_NAMESEG_SIZE], "%u", instance);
 | |
| 	} else if (strlen(filename) != ACPI_NAMESEG_SIZE) {
 | |
| 		return (AE_BAD_SIGNATURE);
 | |
| 	} else {
 | |
| 		*instance = 0;
 | |
| 	}
 | |
| 
 | |
| 	/* Extract signature */
 | |
| 
 | |
| 	ACPI_COPY_NAMESEG(signature, filename);
 | |
| 	return (AE_OK);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_read_table_from_file
 | |
|  *
 | |
|  * PARAMETERS:  filename            - File that contains the desired table
 | |
|  *              file_offset         - Offset of the table in file
 | |
|  *              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
 | |
| osl_read_table_from_file(char *filename,
 | |
| 			 acpi_size file_offset,
 | |
| 			 struct acpi_table_header **table)
 | |
| {
 | |
| 	FILE *table_file;
 | |
| 	struct acpi_table_header header;
 | |
| 	struct acpi_table_header *local_table = NULL;
 | |
| 	u32 table_length;
 | |
| 	s32 count;
 | |
| 	acpi_status status = AE_OK;
 | |
| 
 | |
| 	/* Open the file */
 | |
| 
 | |
| 	table_file = fopen(filename, "rb");
 | |
| 	if (table_file == NULL) {
 | |
| 		fprintf(stderr, "Could not open table file: %s\n", filename);
 | |
| 		return (osl_get_last_status(AE_NOT_FOUND));
 | |
| 	}
 | |
| 
 | |
| 	fseek(table_file, file_offset, SEEK_SET);
 | |
| 
 | |
| 	/* Read the Table header to get the table length */
 | |
| 
 | |
| 	count = fread(&header, 1, sizeof(struct acpi_table_header), table_file);
 | |
| 	if (count != sizeof(struct acpi_table_header)) {
 | |
| 		fprintf(stderr, "Could not read table header: %s\n", filename);
 | |
| 		status = AE_BAD_HEADER;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| #ifdef ACPI_OBSOLETE_FUNCTIONS
 | |
| 
 | |
| 	/* 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_NAMESEG(signature, header.signature)) {
 | |
| 			fprintf(stderr,
 | |
| 				"Incorrect signature: Expecting %4.4s, found %4.4s\n",
 | |
| 				signature, header.signature);
 | |
| 			status = AE_BAD_SIGNATURE;
 | |
| 			goto exit;
 | |
| 		}
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	table_length = ap_get_table_length(&header);
 | |
| 	if (table_length == 0) {
 | |
| 		status = AE_BAD_HEADER;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	/* Read the entire table into a local buffer */
 | |
| 
 | |
| 	local_table = calloc(1, table_length);
 | |
| 	if (!local_table) {
 | |
| 		fprintf(stderr,
 | |
| 			"%4.4s: Could not allocate buffer for table of length %X\n",
 | |
| 			header.signature, table_length);
 | |
| 		status = AE_NO_MEMORY;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	fseek(table_file, file_offset, SEEK_SET);
 | |
| 
 | |
| 	count = fread(local_table, 1, table_length, table_file);
 | |
| 	if (count != table_length) {
 | |
| 		fprintf(stderr, "%4.4s: Could not read table content\n",
 | |
| 			header.signature);
 | |
| 		status = AE_INVALID_TABLE_LENGTH;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	/* Validate checksum */
 | |
| 
 | |
| 	(void)ap_is_valid_checksum(local_table);
 | |
| 
 | |
| exit:
 | |
| 	fclose(table_file);
 | |
| 	*table = local_table;
 | |
| 	return (status);
 | |
| }
 | |
| 
 | |
| /******************************************************************************
 | |
|  *
 | |
|  * FUNCTION:    osl_get_customized_table
 | |
|  *
 | |
|  * 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
 | |
| osl_get_customized_table(char *pathname,
 | |
| 			 char *signature,
 | |
| 			 u32 instance,
 | |
| 			 struct acpi_table_header **table,
 | |
| 			 acpi_physical_address *address)
 | |
| {
 | |
| 	void *table_dir;
 | |
| 	u32 current_instance = 0;
 | |
| 	char temp_name[ACPI_NAMESEG_SIZE];
 | |
| 	char table_filename[PATH_MAX];
 | |
| 	char *filename;
 | |
| 	acpi_status status;
 | |
| 
 | |
| 	/* Open the directory for customized tables */
 | |
| 
 | |
| 	table_dir = acpi_os_open_directory(pathname, "*", REQUEST_FILE_ONLY);
 | |
| 	if (!table_dir) {
 | |
| 		return (osl_get_last_status(AE_NOT_FOUND));
 | |
| 	}
 | |
| 
 | |
| 	/* Attempt to find the table in the directory */
 | |
| 
 | |
| 	while ((filename = acpi_os_get_next_filename(table_dir))) {
 | |
| 
 | |
| 		/* Ignore meaningless files */
 | |
| 
 | |
| 		if (!ACPI_COMPARE_NAMESEG(filename, signature)) {
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* Extract table name and instance number */
 | |
| 
 | |
| 		status =
 | |
| 		    osl_table_name_from_file(filename, temp_name,
 | |
| 					     ¤t_instance);
 | |
| 
 | |
| 		/* Ignore meaningless files */
 | |
| 
 | |
| 		if (ACPI_FAILURE(status) || current_instance != instance) {
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* Create the table pathname */
 | |
| 
 | |
| 		if (instance != 0) {
 | |
| 			sprintf(table_filename, "%s/%4.4s%d", pathname,
 | |
| 				temp_name, instance);
 | |
| 		} else {
 | |
| 			sprintf(table_filename, "%s/%4.4s", pathname,
 | |
| 				temp_name);
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	acpi_os_close_directory(table_dir);
 | |
| 
 | |
| 	if (!filename) {
 | |
| 		return (AE_LIMIT);
 | |
| 	}
 | |
| 
 | |
| 	/* There is no physical address saved for customized tables, use zero */
 | |
| 
 | |
| 	*address = 0;
 | |
| 	status = osl_read_table_from_file(table_filename, 0, table);
 | |
| 
 | |
| 	return (status);
 | |
| }
 |