acfileio.c revision 306536
157429Smarkm/******************************************************************************
257429Smarkm *
357429Smarkm * Module Name: acfileio - Get ACPI tables from file
457429Smarkm *
557429Smarkm *****************************************************************************/
665674Skris
765674Skris/*
865674Skris * Copyright (C) 2000 - 2016, Intel Corp.
965674Skris * All rights reserved.
1065674Skris *
1157429Smarkm * Redistribution and use in source and binary forms, with or without
1276262Sgreen * modification, are permitted provided that the following conditions
1376262Sgreen * are met:
1476262Sgreen * 1. Redistributions of source code must retain the above copyright
1565674Skris *    notice, this list of conditions, and the following disclaimer,
1665674Skris *    without modification.
1765674Skris * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1865674Skris *    substantially similar to the "NO WARRANTY" disclaimer below
1965674Skris *    ("Disclaimer") and any redistribution must be conditioned upon
2065674Skris *    including a substantially similar Disclaimer requirement for further
2165674Skris *    binary redistribution.
2265674Skris * 3. Neither the names of the above-listed copyright holders nor the names
2365674Skris *    of any contributors may be used to endorse or promote products derived
2465674Skris *    from this software without specific prior written permission.
2565674Skris *
2665674Skris * Alternatively, this software may be distributed under the terms of the
2765674Skris * GNU General Public License ("GPL") version 2 as published by the Free
2865674Skris * Software Foundation.
2965674Skris *
3065674Skris * NO WARRANTY
3165674Skris * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3265674Skris * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3365674Skris * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
3465674Skris * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3565674Skris * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36263970Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3799050Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38263970Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3957429Smarkm * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
4057429Smarkm * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
4157429Smarkm * POSSIBILITY OF SUCH DAMAGES.
4257429Smarkm */
4376262Sgreen
4457429Smarkm#include <contrib/dev/acpica/include/acpi.h>
4557429Smarkm#include <contrib/dev/acpica/include/accommon.h>
46113911Sdes#include <contrib/dev/acpica/include/acapps.h>
47181111Sdes#include <contrib/dev/acpica/include/actables.h>
4857429Smarkm#include <contrib/dev/acpica/include/acutils.h>
49181111Sdes#include <errno.h>
50204917Sdes
51263970Sdes#define _COMPONENT          ACPI_UTILITIES
5257429Smarkm        ACPI_MODULE_NAME    ("acfileio")
5357429Smarkm
5457429Smarkm
5557429Smarkm/* Local prototypes */
5692559Sdes
5757429Smarkmstatic ACPI_STATUS
5865674SkrisAcGetOneTableFromFile (
59113911Sdes    char                    *Filename,
6060576Skris    FILE                    *File,
6157429Smarkm    UINT8                   GetOnlyAmlTables,
62157019Sdes    ACPI_TABLE_HEADER       **Table);
6357429Smarkm
64181111Sdesstatic ACPI_STATUS
65181111SdesAcCheckTextModeCorruption (
66181111Sdes    ACPI_TABLE_HEADER       *Table);
67181111Sdes
68181111Sdes
6958585Skris/*******************************************************************************
7057429Smarkm *
7157429Smarkm * FUNCTION:    AcGetAllTablesFromFile
72157019Sdes *
7392559Sdes * PARAMETERS:  Filename            - Table filename
7499050Sdes *              GetOnlyAmlTables    - TRUE if the tables must be AML tables
7557429Smarkm *              ReturnListHead      - Where table list is returned
7658585Skris *
7758585Skris * RETURN:      Status
7857429Smarkm *
7957429Smarkm * DESCRIPTION: Get all ACPI tables from within a single file.
8057429Smarkm *
8157429Smarkm ******************************************************************************/
82126277Sdes
83126277SdesACPI_STATUS
84157019SdesAcGetAllTablesFromFile (
85157019Sdes    char                    *Filename,
8657429Smarkm    UINT8                   GetOnlyAmlTables,
8757429Smarkm    ACPI_NEW_TABLE_DESC     **ReturnListHead)
8857429Smarkm{
8976262Sgreen    ACPI_NEW_TABLE_DESC     *ListHead = NULL;
90162856Sdes    ACPI_NEW_TABLE_DESC     *ListTail = NULL;
9176262Sgreen    ACPI_NEW_TABLE_DESC     *TableDesc;
9257429Smarkm    FILE                    *File;
9357429Smarkm    ACPI_TABLE_HEADER       *Table = NULL;
9457429Smarkm    UINT32                  FileSize;
95126277Sdes    ACPI_STATUS             Status = AE_OK;
96126277Sdes
97126277Sdes
98126277Sdes    File = fopen (Filename, "rb");
99126277Sdes    if (!File)
100126277Sdes    {
101126277Sdes        perror ("Could not open input file");
102126277Sdes        if (errno == ENOENT)
10357429Smarkm        {
10476262Sgreen            return (AE_NOT_EXIST);
105181111Sdes        }
106181111Sdes
107181111Sdes        return (AE_ERROR);
108181111Sdes    }
109181111Sdes
110181111Sdes    /* Get the file size */
111181111Sdes
112181111Sdes    FileSize = CmGetFileSize (File);
113181111Sdes    if (FileSize == ACPI_UINT32_MAX)
114181111Sdes    {
115181111Sdes        Status = AE_ERROR;
116181111Sdes        goto ErrorExit;
117181111Sdes    }
118181111Sdes
119247485Sdes    fprintf (stderr,
120247485Sdes        "Input file %s, Length 0x%X (%u) bytes\n",
121181111Sdes        Filename, FileSize, FileSize);
122181111Sdes
123181111Sdes    /* We must have at least one ACPI table header */
124181111Sdes
125181111Sdes    if (FileSize < sizeof (ACPI_TABLE_HEADER))
126204917Sdes    {
127204917Sdes        Status = AE_BAD_HEADER;
128204917Sdes        goto ErrorExit;
129204917Sdes    }
130204917Sdes
131204917Sdes    /* Check for an non-binary file */
132204917Sdes
133204917Sdes    if (!AcIsFileBinary (File))
134204917Sdes    {
135126277Sdes        fprintf (stderr,
136126277Sdes            "    %s: File does not appear to contain a valid AML table\n",
137126277Sdes            Filename);
138126277Sdes        return (AE_TYPE);
139126277Sdes    }
140126277Sdes
14157429Smarkm    /* Read all tables within the file */
14258585Skris
143204917Sdes    while (ACPI_SUCCESS (Status))
144204917Sdes    {
14558585Skris        /* Get one entire ACPI table */
14658585Skris
147113911Sdes        Status = AcGetOneTableFromFile (
148113911Sdes            Filename, File, GetOnlyAmlTables, &Table);
149113911Sdes
15069591Sgreen        if (Status == AE_CTRL_TERMINATE)
151263970Sdes        {
152263970Sdes            Status = AE_OK;
153263970Sdes            break;
154263970Sdes        }
15576262Sgreen        else if (Status == AE_TYPE)
156263970Sdes        {
157181111Sdes            return (AE_OK);
15858585Skris        }
15958585Skris        else if (ACPI_FAILURE (Status))
16057811Skris        {
16157429Smarkm            goto ErrorExit;
16257429Smarkm        }
16357429Smarkm
16457429Smarkm        /* Print table header for iASL/disassembler only */
165106130Sdes
16658585Skris#ifdef ACPI_ASL_COMPILER
16758585Skris
16858585Skris            AcpiTbPrintTableHeader (0, Table);
16957429Smarkm#endif
17092559Sdes
17157429Smarkm        /* Allocate and link a table descriptor */
17257429Smarkm
17357429Smarkm        TableDesc = AcpiOsAllocate (sizeof (ACPI_NEW_TABLE_DESC));
17492559Sdes        TableDesc->Table = Table;
17592559Sdes        TableDesc->Next = NULL;
17692559Sdes
17792559Sdes        /* Link at the end of the local table list */
178221420Sdes
179263970Sdes        if (!ListHead)
180263970Sdes        {
181221420Sdes            ListHead = TableDesc;
182181111Sdes            ListTail = TableDesc;
18392559Sdes        }
18476262Sgreen        else
18576262Sgreen        {
18657429Smarkm            ListTail->Next = TableDesc;
18757429Smarkm            ListTail = TableDesc;
18857429Smarkm        }
189113911Sdes    }
190113911Sdes
19157429Smarkm    /* Add the local table list to the end of the global list */
19257429Smarkm
19357429Smarkm    if (*ReturnListHead)
19458585Skris    {
19558585Skris        ListTail = *ReturnListHead;
196162856Sdes        while (ListTail->Next)
19757429Smarkm        {
19857429Smarkm            ListTail = ListTail->Next;
19957429Smarkm        }
20057429Smarkm
20176262Sgreen        ListTail->Next = ListHead;
20276262Sgreen    }
20358585Skris    else
204126277Sdes    {
20557429Smarkm        *ReturnListHead = ListHead;
20657429Smarkm    }
20758585Skris
20858585SkrisErrorExit:
20992559Sdes    fclose(File);
21092559Sdes    return (Status);
21192559Sdes}
21292559Sdes
213126277Sdes
214126277Sdes/*******************************************************************************
21557429Smarkm *
21657429Smarkm * FUNCTION:    AcGetOneTableFromFile
21757429Smarkm *
21892559Sdes * PARAMETERS:  Filename            - File where table is located
219157019Sdes *              File                - Open FILE pointer to Filename
220157019Sdes *              GetOnlyAmlTables    - TRUE if the tables must be AML tables.
221157019Sdes *              ReturnTable         - Where a pointer to the table is returned
222157019Sdes *
223157019Sdes * RETURN:      Status
224157019Sdes *
22557429Smarkm * DESCRIPTION: Read the next ACPI table from a file. Implements support
22658585Skris *              for multiple tables within a single file. File must already
22758585Skris *              be open.
22858585Skris *
22957429Smarkm * Note: Loading an RSDP is not supported.
230181111Sdes *
231181111Sdes ******************************************************************************/
232181111Sdes
233181111Sdesstatic ACPI_STATUS
234181111SdesAcGetOneTableFromFile (
235181111Sdes    char                    *Filename,
236181111Sdes    FILE                    *File,
237181111Sdes    UINT8                   GetOnlyAmlTables,
238181111Sdes    ACPI_TABLE_HEADER       **ReturnTable)
23992559Sdes{
24092559Sdes    ACPI_STATUS             Status = AE_OK;
24192559Sdes    ACPI_TABLE_HEADER       TableHeader;
24292559Sdes    ACPI_TABLE_HEADER       *Table;
24392559Sdes    INT32                   Count;
24492559Sdes    long                    TableOffset;
24565674Skris
24665674Skris
24765674Skris    *ReturnTable = NULL;
24865674Skris
24965674Skris    /* Get the table header to examine signature and length */
25065674Skris
25165674Skris    TableOffset = ftell (File);
25265674Skris    Count = fread (&TableHeader, 1, sizeof (ACPI_TABLE_HEADER), File);
25365674Skris    if (Count != sizeof (ACPI_TABLE_HEADER))
25465674Skris    {
25565674Skris        return (AE_CTRL_TERMINATE);
25665674Skris    }
25765674Skris
25865674Skris    /* Validate the table signature/header (limited ASCII chars) */
25965674Skris
26065674Skris    Status = AcValidateTableHeader (File, TableOffset);
26192559Sdes    if (ACPI_FAILURE (Status))
262113911Sdes    {
26392559Sdes        return (Status);
26492559Sdes    }
26592559Sdes
26692559Sdes    if (GetOnlyAmlTables)
26792559Sdes    {
268157019Sdes        /* Table must be an AML table (DSDT/SSDT) or FADT */
26992559Sdes
27092559Sdes        if (!ACPI_COMPARE_NAME (TableHeader.Signature, ACPI_SIG_FADT) &&
27192559Sdes            !AcpiUtIsAmlTable (&TableHeader))
27292559Sdes        {
27392559Sdes            fprintf (stderr,
27492559Sdes                "    %s: Table [%4.4s] is not an AML table - ignoring\n",
27592559Sdes                Filename, TableHeader.Signature);
27692559Sdes
27757429Smarkm            return (AE_TYPE);
278157019Sdes        }
279157019Sdes    }
280204917Sdes
281157019Sdes    /* Allocate a buffer for the entire table */
282157019Sdes
283157019Sdes    Table = AcpiOsAllocate ((size_t) TableHeader.Length);
28498706Sdes    if (!Table)
285263970Sdes    {
286157019Sdes        return (AE_NO_MEMORY);
287157019Sdes    }
288157019Sdes
289157019Sdes    /* Read the entire ACPI table, including header */
290157019Sdes
291157019Sdes    fseek (File, TableOffset, SEEK_SET);
292157019Sdes
293157019Sdes    Count = fread (Table, 1, TableHeader.Length, File);
294157019Sdes    if (Count != (INT32) TableHeader.Length)
295157019Sdes    {
296157019Sdes        Status = AE_ERROR;
297157019Sdes        goto ErrorExit;
298157019Sdes    }
299157019Sdes
300157019Sdes    /* Validate the checksum (just issue a warning) */
301157019Sdes
302157019Sdes    Status = AcpiTbVerifyChecksum (Table, TableHeader.Length);
303157019Sdes    if (ACPI_FAILURE (Status))
304157019Sdes    {
305157019Sdes        Status = AcCheckTextModeCorruption (Table);
306157019Sdes        if (ACPI_FAILURE (Status))
307157019Sdes        {
308157019Sdes            goto ErrorExit;
309157019Sdes        }
310157019Sdes    }
311157019Sdes
312157019Sdes    *ReturnTable = Table;
313157019Sdes    return (AE_OK);
314157019Sdes
315157019Sdes
316157019SdesErrorExit:
317157019Sdes    AcpiOsFree (Table);
318157019Sdes    return (Status);
319157019Sdes}
320157019Sdes
321157019Sdes
322157019Sdes/*******************************************************************************
323157019Sdes *
324251135Sdes * FUNCTION:    AcIsFileBinary
325247485Sdes *
326157019Sdes * PARAMETERS:  File                - Open input file
327157019Sdes *
328157019Sdes * RETURN:      TRUE if file appears to be binary
329157019Sdes *
330157019Sdes * DESCRIPTION: Scan a file for any non-ASCII bytes.
331157019Sdes *
332157019Sdes * Note: Maintains current file position.
333157019Sdes *
334157019Sdes ******************************************************************************/
335157019Sdes
336157019SdesBOOLEAN
337157019SdesAcIsFileBinary (
338157019Sdes    FILE                    *File)
339157019Sdes{
340157019Sdes    UINT8                   Byte;
341157019Sdes    BOOLEAN                 IsBinary = FALSE;
342157019Sdes    long                    FileOffset;
343157019Sdes
344157019Sdes
345157019Sdes    /* Scan entire file for any non-ASCII bytes */
346157019Sdes
347157019Sdes    FileOffset = ftell (File);
348157019Sdes    while (fread (&Byte, 1, 1, File) == 1)
349157019Sdes    {
350157019Sdes        if (!isprint (Byte) && !isspace (Byte))
351181111Sdes        {
352181111Sdes            IsBinary = TRUE;
353181111Sdes            goto Exit;
354157019Sdes        }
355157019Sdes    }
356157019Sdes
357157019SdesExit:
358157019Sdes    fseek (File, FileOffset, SEEK_SET);
359157019Sdes    return (IsBinary);
360157019Sdes}
361157019Sdes
362157019Sdes
363157019Sdes/*******************************************************************************
364157019Sdes *
365157019Sdes * FUNCTION:    AcValidateTableHeader
366157019Sdes *
367157019Sdes * PARAMETERS:  File                - Open input file
368162856Sdes *
369157019Sdes * RETURN:      Status
370157019Sdes *
371157019Sdes * DESCRIPTION: Determine if a file seems to contain one or more binary ACPI
372157019Sdes *              tables, via the
373157019Sdes *              following checks on what would be the table header:
374157019Sdes *              1) File must be at least as long as an ACPI_TABLE_HEADER
375157019Sdes *              2) There must be enough room in the file to hold entire table
376157019Sdes *              3) Signature, OemId, OemTableId, AslCompilerId must be ASCII
37757429Smarkm *
37857429Smarkm * Note: There can be multiple definition blocks per file, so we cannot
37957429Smarkm * expect/compare the file size to be equal to the table length. 12/2015.
38057429Smarkm *
38157429Smarkm * Note: Maintains current file position.
38257429Smarkm *
38357429Smarkm ******************************************************************************/
38460576Skris
38557429SmarkmACPI_STATUS
38657429SmarkmAcValidateTableHeader (
387149753Sdes    FILE                    *File,
38857429Smarkm    long                    TableOffset)
38960576Skris{
39057429Smarkm    ACPI_TABLE_HEADER       TableHeader;
39157429Smarkm    size_t                  Actual;
39257429Smarkm    long                    OriginalOffset;
39357429Smarkm    UINT32                  FileSize;
39457429Smarkm    UINT32                  i;
39599050Sdes
39699050Sdes
39799050Sdes    ACPI_FUNCTION_TRACE ("AcValidateTableHeader");
39857429Smarkm
39957429Smarkm
40057429Smarkm    /* Read a potential table header */
40157429Smarkm
40257429Smarkm    OriginalOffset = ftell (File);
40357429Smarkm    fseek (File, TableOffset, SEEK_SET);
404126277Sdes
405149753Sdes    Actual = fread (&TableHeader, 1, sizeof (ACPI_TABLE_HEADER), File);
406126277Sdes    fseek (File, OriginalOffset, SEEK_SET);
407106130Sdes
408106130Sdes    if (Actual < sizeof (ACPI_TABLE_HEADER))
409106130Sdes    {
410106130Sdes        return (AE_ERROR);
41157429Smarkm    }
41257429Smarkm
41357429Smarkm    /* Validate the signature (limited ASCII chars) */
41457429Smarkm
415149753Sdes    if (!AcpiUtValidNameseg (TableHeader.Signature))
41657429Smarkm    {
41757811Skris        fprintf (stderr, "Invalid table signature: 0x%8.8X\n",
41857429Smarkm            *ACPI_CAST_PTR (UINT32, TableHeader.Signature));
41999050Sdes        return (AE_BAD_SIGNATURE);
42099050Sdes    }
42157952Skris
42257429Smarkm    /* Validate table length against bytes remaining in the file */
42357429Smarkm
424162856Sdes    FileSize = CmGetFileSize (File);
425162856Sdes    if (TableHeader.Length > (UINT32) (FileSize - TableOffset))
426162856Sdes    {
427162856Sdes        fprintf (stderr, "Table [%4.4s] is too long for file - "
42857429Smarkm            "needs: 0x%.2X, remaining in file: 0x%.2X\n",
42957429Smarkm            TableHeader.Signature, TableHeader.Length,
43057429Smarkm            (UINT32) (FileSize - TableOffset));
431162856Sdes        return (AE_BAD_HEADER);
432162856Sdes    }
433162856Sdes
434162856Sdes    /*
435162856Sdes     * These fields must be ASCII: OemId, OemTableId, AslCompilerId.
436162856Sdes     * We allow a NULL terminator in OemId and OemTableId.
437162856Sdes     */
438162856Sdes    for (i = 0; i < ACPI_NAME_SIZE; i++)
439162856Sdes    {
440162856Sdes        if (!ACPI_IS_ASCII ((UINT8) TableHeader.AslCompilerId[i]))
441162856Sdes        {
442162856Sdes            goto BadCharacters;
443162856Sdes        }
444162856Sdes    }
445162856Sdes
446162856Sdes    for (i = 0; (i < ACPI_OEM_ID_SIZE) && (TableHeader.OemId[i]); i++)
447162856Sdes    {
448162856Sdes        if (!ACPI_IS_ASCII ((UINT8) TableHeader.OemId[i]))
449162856Sdes        {
450162856Sdes            goto BadCharacters;
451162856Sdes        }
452162856Sdes    }
453162856Sdes
454162856Sdes    for (i = 0; (i < ACPI_OEM_TABLE_ID_SIZE) && (TableHeader.OemTableId[i]); i++)
455162856Sdes    {
456162856Sdes        if (!ACPI_IS_ASCII ((UINT8) TableHeader.OemTableId[i]))
457162856Sdes        {
458162856Sdes            goto BadCharacters;
459162856Sdes        }
460162856Sdes    }
461162856Sdes
462162856Sdes    return (AE_OK);
463162856Sdes
464162856Sdes
465162856SdesBadCharacters:
466162856Sdes
467162856Sdes    ACPI_WARNING ((AE_INFO,
468162856Sdes        "Table header for [%4.4s] has invalid ASCII character(s)",
469162856Sdes        TableHeader.Signature));
470162856Sdes    return (AE_OK);
471162856Sdes}
472162856Sdes
47357429Smarkm
47492559Sdes/*******************************************************************************
475247485Sdes *
476162856Sdes * FUNCTION:    AcCheckTextModeCorruption
477162856Sdes *
478247485Sdes * PARAMETERS:  Table           - Table buffer starting with table header
479247485Sdes *
480247485Sdes * RETURN:      Status
48158585Skris *
48257429Smarkm * DESCRIPTION: Check table for text mode file corruption where all linefeed
48357429Smarkm *              characters (LF) have been replaced by carriage return linefeed
48457429Smarkm *              pairs (CR/LF).
48558585Skris *
486162856Sdes ******************************************************************************/
487162856Sdes
488162856Sdesstatic ACPI_STATUS
489162856SdesAcCheckTextModeCorruption (
490162856Sdes    ACPI_TABLE_HEADER       *Table)
491162856Sdes{
492106130Sdes    UINT32                  i;
493162856Sdes    UINT32                  Pairs = 0;
49476262Sgreen    UINT8                   *Buffer = ACPI_CAST_PTR (UINT8, Table);
49557429Smarkm
49657429Smarkm
49776262Sgreen    /* Scan entire table to determine if each LF has been prefixed with a CR */
498221420Sdes
499221420Sdes    for (i = 1; i < Table->Length; i++)
500221420Sdes    {
501263970Sdes        if (Buffer[i] == 0x0A)
50276262Sgreen        {
50376262Sgreen            if (Buffer[i - 1] != 0x0D)
50476262Sgreen            {
50557429Smarkm                /* The LF does not have a preceding CR, table not corrupted */
50657429Smarkm
507147005Sdes                return (AE_OK);
508147005Sdes            }
509147005Sdes            else
51058585Skris            {
51176262Sgreen                /* Found a CR/LF pair */
512162856Sdes
513221420Sdes                Pairs++;
514263970Sdes            }
51576262Sgreen
51676262Sgreen            i++;
51757429Smarkm        }
51857429Smarkm    }
51998706Sdes
52098706Sdes    if (!Pairs)
52198706Sdes    {
52298706Sdes        return (AE_OK);
52365674Skris    }
52458585Skris
52558585Skris    /*
52692559Sdes     * Entire table scanned, each CR is part of a CR/LF pair --
52792559Sdes     * meaning that the table was treated as a text file somewhere.
52857429Smarkm     *
529204917Sdes     * NOTE: We can't "fix" the table, because any existing CR/LF pairs in the
530204917Sdes     * original table are left untouched by the text conversion process --
531204917Sdes     * meaning that we cannot simply replace CR/LF pairs with LFs.
532204917Sdes     */
533204917Sdes    AcpiOsPrintf ("Table has been corrupted by text mode conversion\n");
534204917Sdes    AcpiOsPrintf ("All LFs (%u) were changed to CR/LF pairs\n", Pairs);
535204917Sdes    AcpiOsPrintf ("Table cannot be repaired!\n");
53657429Smarkm
53757429Smarkm    return (AE_BAD_VALUE);
53858585Skris}
53958585Skris