ACPICA: Utilities: Add support to read table from files

After the new table reading utility functions are well tested, acpidump can
also switch to use the generic acpi_ut_read_table_xxx() APIs. Currently
this patch is no-op as acpidump does not link to the new APIs.

This patch is only useful for ACPICA applications, most of which are not
shipped in the Linux kernel.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
diff --git a/drivers/acpi/acpica/utfileio.c b/drivers/acpi/acpica/utfileio.c
new file mode 100644
index 0000000..c8f6359
--- /dev/null
+++ b/drivers/acpi/acpica/utfileio.c
@@ -0,0 +1,337 @@
+/*******************************************************************************
+ *
+ * Module Name: utfileio - simple file I/O routines
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, 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 <acpi/acpi.h>
+#include "accommon.h"
+#include "actables.h"
+#include "acapps.h"
+
+#ifdef ACPI_ASL_COMPILER
+#include "aslcompiler.h"
+#endif
+
+#define _COMPONENT          ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("utfileio")
+
+#ifdef ACPI_APPLICATION
+/* Local prototypes */
+static acpi_status
+acpi_ut_check_text_mode_corruption(u8 *table,
+				   u32 table_length, u32 file_length);
+
+static acpi_status
+acpi_ut_read_table(FILE * fp,
+		   struct acpi_table_header **table, u32 *table_length);
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ut_check_text_mode_corruption
+ *
+ * PARAMETERS:  table           - Table buffer
+ *              table_length    - Length of table from the table header
+ *              file_length     - Length of the file that contains the table
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Check table for text mode file corruption where all linefeed
+ *              characters (LF) have been replaced by carriage return linefeed
+ *              pairs (CR/LF).
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_check_text_mode_corruption(u8 *table, u32 table_length, u32 file_length)
+{
+	u32 i;
+	u32 pairs = 0;
+
+	if (table_length != file_length) {
+		ACPI_WARNING((AE_INFO,
+			      "File length (0x%X) is not the same as the table length (0x%X)",
+			      file_length, table_length));
+	}
+
+	/* Scan entire table to determine if each LF has been prefixed with a CR */
+
+	for (i = 1; i < file_length; i++) {
+		if (table[i] == 0x0A) {
+			if (table[i - 1] != 0x0D) {
+
+				/* The LF does not have a preceding CR, table not corrupted */
+
+				return (AE_OK);
+			} else {
+				/* Found a CR/LF pair */
+
+				pairs++;
+			}
+			i++;
+		}
+	}
+
+	if (!pairs) {
+		return (AE_OK);
+	}
+
+	/*
+	 * Entire table scanned, each CR is part of a CR/LF pair --
+	 * meaning that the table was treated as a text file somewhere.
+	 *
+	 * NOTE: We can't "fix" the table, because any existing CR/LF pairs in the
+	 * original table are left untouched by the text conversion process --
+	 * meaning that we cannot simply replace CR/LF pairs with LFs.
+	 */
+	acpi_os_printf("Table has been corrupted by text mode conversion\n");
+	acpi_os_printf("All LFs (%u) were changed to CR/LF pairs\n", pairs);
+	acpi_os_printf("Table cannot be repaired!\n");
+	return (AE_BAD_VALUE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ut_read_table
+ *
+ * PARAMETERS:  fp              - File that contains table
+ *              table           - Return value, buffer with table
+ *              table_length    - Return value, length of table
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Load the DSDT from the file pointer
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_read_table(FILE * fp,
+		   struct acpi_table_header **table, u32 *table_length)
+{
+	struct acpi_table_header table_header;
+	u32 actual;
+	acpi_status status;
+	u32 file_size;
+	u8 standard_header = TRUE;
+
+	/* Get the file size */
+
+	file_size = cm_get_file_size(fp);
+	if (file_size == ACPI_UINT32_MAX) {
+		return (AE_ERROR);
+	}
+
+	if (file_size < 4) {
+		return (AE_BAD_HEADER);
+	}
+
+	/* Read the signature */
+
+	if (fread(&table_header, 1, 4, fp) != 4) {
+		acpi_os_printf("Could not read the table signature\n");
+		return (AE_BAD_HEADER);
+	}
+
+	fseek(fp, 0, SEEK_SET);
+
+	/* The RSDP table does not have standard ACPI header */
+
+	if (ACPI_COMPARE_NAME(table_header.signature, "RSD ")) {
+		*table_length = file_size;
+		standard_header = FALSE;
+	} else {
+		/* Read the table header */
+
+		if (fread
+		    (&table_header, 1, sizeof(struct acpi_table_header),
+		     fp) != sizeof(struct acpi_table_header)) {
+			acpi_os_printf("Could not read the table header\n");
+			return (AE_BAD_HEADER);
+		}
+#if 0
+		/* Validate the table header/length */
+
+		status = acpi_tb_validate_table_header(&table_header);
+		if (ACPI_FAILURE(status)) {
+			acpi_os_printf("Table header is invalid!\n");
+			return (status);
+		}
+#endif
+
+		/* File size must be at least as long as the Header-specified length */
+
+		if (table_header.length > file_size) {
+			acpi_os_printf
+			    ("TableHeader length [0x%X] greater than the input file size [0x%X]\n",
+			     table_header.length, file_size);
+
+#ifdef ACPI_ASL_COMPILER
+			status = fl_check_for_ascii(fp, NULL, FALSE);
+			if (ACPI_SUCCESS(status)) {
+				acpi_os_printf
+				    ("File appears to be ASCII only, must be binary\n",
+				     table_header.length, file_size);
+			}
+#endif
+			return (AE_BAD_HEADER);
+		}
+#ifdef ACPI_OBSOLETE_CODE
+		/* We only support a limited number of table types */
+
+		if (!ACPI_COMPARE_NAME
+		    ((char *)table_header.signature, ACPI_SIG_DSDT)
+		    && !ACPI_COMPARE_NAME((char *)table_header.signature,
+					  ACPI_SIG_PSDT)
+		    && !ACPI_COMPARE_NAME((char *)table_header.signature,
+					  ACPI_SIG_SSDT)) {
+			acpi_os_printf
+			    ("Table signature [%4.4s] is invalid or not supported\n",
+			     (char *)table_header.signature);
+			ACPI_DUMP_BUFFER(&table_header,
+					 sizeof(struct acpi_table_header));
+			return (AE_ERROR);
+		}
+#endif
+
+		*table_length = table_header.length;
+	}
+
+	/* Allocate a buffer for the table */
+
+	*table = acpi_os_allocate((size_t) file_size);
+	if (!*table) {
+		acpi_os_printf
+		    ("Could not allocate memory for ACPI table %4.4s (size=0x%X)\n",
+		     table_header.signature, *table_length);
+		return (AE_NO_MEMORY);
+	}
+
+	/* Get the rest of the table */
+
+	fseek(fp, 0, SEEK_SET);
+	actual = fread(*table, 1, (size_t) file_size, fp);
+	if (actual == file_size) {
+		if (standard_header) {
+
+			/* Now validate the checksum */
+
+			status = acpi_tb_verify_checksum((void *)*table,
+							 ACPI_CAST_PTR(struct
+								       acpi_table_header,
+								       *table)->
+							 length);
+
+			if (status == AE_BAD_CHECKSUM) {
+				status =
+				    acpi_ut_check_text_mode_corruption((u8 *)
+								       *table,
+								       file_size,
+								       (*table)->
+								       length);
+				return (status);
+			}
+		}
+		return (AE_OK);
+	}
+
+	if (actual > 0) {
+		acpi_os_printf("Warning - reading table, asked for %X got %X\n",
+			       file_size, actual);
+		return (AE_OK);
+	}
+
+	acpi_os_printf("Error - could not read the table file\n");
+	acpi_os_free(*table);
+	*table = NULL;
+	*table_length = 0;
+	return (AE_ERROR);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ut_read_table_from_file
+ *
+ * PARAMETERS:  filename         - File where table is located
+ *              table            - Where a pointer to the table is returned
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Get an ACPI table from a file
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_read_table_from_file(char *filename, struct acpi_table_header ** table)
+{
+	FILE *file;
+	u32 file_size;
+	u32 table_length;
+	acpi_status status = AE_ERROR;
+
+	/* Open the file, get current size */
+
+	file = fopen(filename, "rb");
+	if (!file) {
+		perror("Could not open input file");
+		return (status);
+	}
+
+	file_size = cm_get_file_size(file);
+	if (file_size == ACPI_UINT32_MAX) {
+		goto exit;
+	}
+
+	/* Get the entire file */
+
+	fprintf(stderr,
+		"Loading Acpi table from file %10s - Length %.8u (%06X)\n",
+		filename, file_size, file_size);
+
+	status = acpi_ut_read_table(file, table, &table_length);
+	if (ACPI_FAILURE(status)) {
+		acpi_os_printf("Could not get table from the file\n");
+	}
+
+exit:
+	fclose(file);
+	return (status);
+}
+
+#endif