1/******************************************************************************
2 *
3 * Module Name: oswintbl - Windows OSL for obtaining ACPI tables
4 *
5 *****************************************************************************/
6
7/*
8 * Copyright (C) 2000 - 2023, Intel Corp.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions, and the following disclaimer,
16 *    without modification.
17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18 *    substantially similar to the "NO WARRANTY" disclaimer below
19 *    ("Disclaimer") and any redistribution must be conditioned upon
20 *    including a substantially similar Disclaimer requirement for further
21 *    binary redistribution.
22 * 3. Neither the names of the above-listed copyright holders nor the names
23 *    of any contributors may be used to endorse or promote products derived
24 *    from this software without specific prior written permission.
25 *
26 * Alternatively, this software may be distributed under the terms of the
27 * GNU General Public License ("GPL") version 2 as published by the Free
28 * Software Foundation.
29 *
30 * NO WARRANTY
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41 * POSSIBILITY OF SUCH DAMAGES.
42 */
43
44#include "acpi.h"
45#include "accommon.h"
46#include "acutils.h"
47#include <stdio.h>
48
49#ifdef WIN32
50#pragma warning(disable:4115)   /* warning C4115: (caused by rpcasync.h) */
51#include <windows.h>
52
53#elif WIN64
54#include <windowsx.h>
55#endif
56
57#define _COMPONENT          ACPI_OS_SERVICES
58        ACPI_MODULE_NAME    ("oswintbl")
59
60/* Local prototypes */
61
62static char *
63WindowsFormatException (
64    LONG                WinStatus);
65
66/* Globals */
67
68#define LOCAL_BUFFER_SIZE           64
69
70static char             KeyBuffer[LOCAL_BUFFER_SIZE];
71static char             ErrorBuffer[LOCAL_BUFFER_SIZE];
72
73/*
74 * List of table signatures reported by EnumSystemFirmwareTables ()
75 */
76UINT32                  *Gbl_AvailableTableSignatures;
77UINT32                  Gbl_TableCount = 0;
78UINT32                  Gbl_SsdtInstance = 0;
79
80BOOLEAN                 Gbl_TableListInitialized = FALSE;
81
82static ACPI_STATUS
83OslTableInitialize (
84    void);
85
86
87/******************************************************************************
88 *
89 * FUNCTION:    WindowsFormatException
90 *
91 * PARAMETERS:  WinStatus       - Status from a Windows system call
92 *
93 * RETURN:      Formatted (ascii) exception code. Front-end to Windows
94 *              FormatMessage interface.
95 *
96 * DESCRIPTION: Decode a windows exception
97 *
98 *****************************************************************************/
99
100static char *
101WindowsFormatException (
102    LONG                WinStatus)
103{
104
105    ErrorBuffer[0] = 0;
106    FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, WinStatus, 0,
107        ErrorBuffer, LOCAL_BUFFER_SIZE, NULL);
108
109    return (ErrorBuffer);
110}
111
112
113/******************************************************************************
114 *
115 * FUNCTION:    AcpiOsGetTableByAddress
116 *
117 * PARAMETERS:  Address         - Physical address of the ACPI table
118 *              Table           - Where a pointer to the table is returned
119 *
120 * RETURN:      Status; Table buffer is returned if AE_OK.
121 *              AE_NOT_FOUND: A valid table was not found at the address
122 *
123 * DESCRIPTION: Get an ACPI table via a physical memory address.
124 *
125 * NOTE:        Cannot be implemented without a Windows device driver.
126 *
127 *****************************************************************************/
128
129ACPI_STATUS
130AcpiOsGetTableByAddress (
131    ACPI_PHYSICAL_ADDRESS   Address,
132    ACPI_TABLE_HEADER       **Table)
133{
134
135    fprintf (stderr, "Get table by address is not supported on Windows\n");
136    return (AE_SUPPORT);
137}
138
139
140/******************************************************************************
141 *
142 * FUNCTION:    AcpiOsGetTableByIndex
143 *
144 * PARAMETERS:  Index           - Which table to get
145 *              Table           - Where a pointer to the table is returned
146 *              Instance        - Where a pointer to the table instance no. is
147 *                                returned
148 *              Address         - Where the table physical address is returned
149 *
150 * RETURN:      Status; Table buffer and physical address returned if AE_OK.
151 *              AE_LIMIT: Index is beyond valid limit
152 *
153 * DESCRIPTION: Get an ACPI table via an index value (0 through n). Returns
154 *              AE_LIMIT when an invalid index is reached. Index is not
155 *              necessarily an index into the RSDT/XSDT.
156 *              SSDT tables are obtained from the Windows registry. All other
157 *              tables are obtained through GetSystemFirmwareTable ().
158 *
159 * NOTE:        Cannot get the physical address from the windows registry;
160 *              zero is returned instead.
161 *
162 *****************************************************************************/
163
164ACPI_STATUS
165AcpiOsGetTableByIndex (
166    UINT32                  Index,
167    ACPI_TABLE_HEADER       **Table,
168    UINT32                  *Instance,
169    ACPI_PHYSICAL_ADDRESS   *Address)
170{
171    ACPI_STATUS             Status;
172    char                    *Signature;
173    UINT32                  CurrentInstance;
174
175
176    /* Enumerate all ACPI table signatures on first invocation of this function */
177
178    Status = OslTableInitialize ();
179    if (ACPI_FAILURE (Status))
180    {
181        return (Status);
182    }
183
184    /* Validate Index */
185
186    if (Index < Gbl_TableCount)
187    {
188        Signature = malloc (ACPI_NAMESEG_SIZE + 1);
189        if (!Signature)
190        {
191            return (AE_NO_MEMORY);
192        }
193
194        Signature = memmove (Signature, &Gbl_AvailableTableSignatures[Index], ACPI_NAMESEG_SIZE);
195    }
196    else
197    {
198        return (AE_LIMIT);
199    }
200
201    if (ACPI_COMPARE_NAMESEG (Signature, ACPI_SIG_SSDT))
202    {
203        CurrentInstance = Gbl_SsdtInstance;
204        Gbl_SsdtInstance++;
205    }
206    else
207    {
208        CurrentInstance = 0;
209    }
210
211    Status = AcpiOsGetTableByName (Signature, CurrentInstance, Table, Address);
212    if (ACPI_SUCCESS (Status))
213    {
214        *Instance = CurrentInstance;
215    }
216    else if (Status == AE_NOT_FOUND &&
217        ACPI_COMPARE_NAMESEG (Signature, ACPI_SIG_SSDT))
218    {
219        /* Treat SSDTs that are not found as invalid index. */
220        Status = AE_LIMIT;
221    }
222
223    free (Signature);
224    return (Status);
225}
226
227/******************************************************************************
228 *
229 * FUNCTION:    OslTableInitialize
230 *
231 * PARAMETERS:  None
232 *
233 * RETURN:      Status
234 *
235 * DESCRIPTION: Initialize ACPI table data. Enumerate all ACPI table signatures
236 *              and save them to a global list.
237 *
238 *****************************************************************************/
239static ACPI_STATUS
240OslTableInitialize (
241    void)
242{
243    UINT32                  ResultSize;
244    UINT32                  DataSize;
245
246    if (Gbl_TableListInitialized)
247    {
248        return (AE_OK);
249    }
250
251    /*
252     * ACPI table signatures are always 4 characters. Therefore, the data size
253     * buffer should be a multiple of 4
254     */
255    DataSize = EnumSystemFirmwareTables ('ACPI', NULL, 0);
256    if (DataSize % ACPI_NAMESEG_SIZE)
257    {
258        return (AE_ERROR);
259    }
260
261    /*
262     * EnumSystemFirmwareTables () does not report the DSDT or XSDT. Work around this
263     * by adding these entries manually.
264     */
265    Gbl_TableCount = 2 + DataSize / ACPI_NAMESEG_SIZE;
266    Gbl_AvailableTableSignatures = malloc (Gbl_TableCount * ACPI_NAMESEG_SIZE);
267    if (!Gbl_AvailableTableSignatures)
268    {
269        return (AE_NO_MEMORY);
270    }
271
272    ResultSize = EnumSystemFirmwareTables ('ACPI', Gbl_AvailableTableSignatures, DataSize);
273    if (ResultSize > DataSize)
274    {
275        return (AE_ERROR);
276    }
277
278    /* Insert the DSDT and XSDT tables signatures */
279
280    Gbl_AvailableTableSignatures [Gbl_TableCount - 1] = 'TDSD';
281    Gbl_AvailableTableSignatures [Gbl_TableCount - 2] = 'TDSX';
282
283    Gbl_TableListInitialized = TRUE;
284    return (AE_OK);
285}
286
287
288/******************************************************************************
289 *
290 * FUNCTION:    WindowsGetTableFromRegistry
291 *
292 * PARAMETERS:  Signature       - ACPI Signature for desired table. Must be
293 *                                a null terminated 4-character string.
294 *              Instance        - For SSDTs (0...n). Use 0 otherwise.
295 *              Table           - Where a pointer to the table is returned
296 *              Address         - Where the table physical address is returned
297 *
298 * RETURN:      Status; Table buffer and physical address returned if AE_OK.
299 *              AE_LIMIT: Instance is beyond valid limit
300 *              AE_NOT_FOUND: A table with the signature was not found
301 *
302 * DESCRIPTION: Get an ACPI table via a table signature (4 ASCII characters).
303 *              Returns AE_LIMIT when an invalid instance is reached.
304 *              Table is obtained from the Windows registry.
305 *
306 * NOTE:        Assumes the input signature is uppercase.
307 *              Cannot get the physical address from the windows registry;
308 *              zero is returned instead.
309 *
310 *****************************************************************************/
311
312static ACPI_STATUS
313WindowsGetTableFromRegistry (
314    char                    *Signature,
315    UINT32                  Instance,
316    ACPI_TABLE_HEADER       **Table,
317    ACPI_PHYSICAL_ADDRESS   *Address)
318{
319    HKEY                    Handle = NULL;
320    LONG                    WinStatus;
321    ULONG                   Type;
322    ULONG                   NameSize;
323    ULONG                   DataSize;
324    HKEY                    SubKey;
325    ULONG                   i;
326    ACPI_TABLE_HEADER       *ReturnTable;
327    ACPI_STATUS             Status = AE_OK;
328
329
330    /* Get a handle to the table key */
331
332    while (1)
333    {
334        strcpy(KeyBuffer, "HARDWARE\\ACPI\\");
335        if (AcpiUtSafeStrcat(KeyBuffer, sizeof(KeyBuffer), Signature))
336        {
337            return (AE_BUFFER_OVERFLOW);
338        }
339
340        /*
341         * Windows stores SSDT at SSDT, SSD1, ..., SSD9, SSDA, ..., SSDS, SSDT,
342         * SSDU, ..., SSDY. If the first (0th) and the 29th tables have the same
343         * OEM ID, Table ID and Revision, then the 29th entry will overwrite the
344         * first entry... Let's hope that we do not have that many entries.
345         */
346        if (Instance > 0 && ACPI_COMPARE_NAMESEG(Signature, ACPI_SIG_SSDT))
347        {
348            if (Instance < 10)
349            {
350                KeyBuffer[strlen(KeyBuffer) - 1] = '0' + (char)Instance;
351            }
352            else if (Instance < 29)
353            {
354                KeyBuffer[strlen(KeyBuffer) - 1] = 'A' + (char)(Instance - 10);
355            }
356            else
357            {
358                return (AE_LIMIT);
359            }
360        }
361
362        WinStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KeyBuffer,
363            0L, KEY_READ, &Handle);
364
365        if (WinStatus != ERROR_SUCCESS)
366        {
367            /*
368             * Somewhere along the way, MS changed the registry entry for
369             * the FADT from
370             * HARDWARE/ACPI/FACP  to
371             * HARDWARE/ACPI/FADT.
372             *
373             * This code allows for both.
374             */
375            if (ACPI_COMPARE_NAMESEG(Signature, "FACP"))
376            {
377                Signature = "FADT";
378            }
379            else if (ACPI_COMPARE_NAMESEG(Signature, "XSDT"))
380            {
381                Signature = "RSDT";
382            }
383            else if (ACPI_COMPARE_NAMESEG(Signature, ACPI_SIG_SSDT))
384            {
385                /*
386                 * SSDT may not be present on older Windows versions, but it is
387                 * also possible that the index is not found.
388                 */
389                return (AE_NOT_FOUND);
390            }
391            else
392            {
393                fprintf(stderr,
394                    "Could not find %s in registry at %s: %s (WinStatus=0x%X)\n",
395                    Signature, KeyBuffer, WindowsFormatException(WinStatus), WinStatus);
396                return (AE_NOT_FOUND);
397            }
398        }
399        else
400        {
401            break;
402        }
403    }
404
405    /* Actual data for the table is down a couple levels */
406
407    for (i = 0; ;)
408    {
409        WinStatus = RegEnumKey(Handle, i, KeyBuffer, sizeof(KeyBuffer));
410        i++;
411        if (WinStatus == ERROR_NO_MORE_ITEMS)
412        {
413            break;
414        }
415
416        WinStatus = RegOpenKey(Handle, KeyBuffer, &SubKey);
417        if (WinStatus != ERROR_SUCCESS)
418        {
419            fprintf(stderr, "Could not open %s entry: %s\n",
420                Signature, WindowsFormatException(WinStatus));
421            Status = AE_ERROR;
422            goto Cleanup;
423        }
424
425        RegCloseKey(Handle);
426        Handle = SubKey;
427        i = 0;
428    }
429
430    /* Find the (binary) table entry */
431
432    for (i = 0; ; i++)
433    {
434        NameSize = sizeof(KeyBuffer);
435        WinStatus = RegEnumValue(Handle, i, KeyBuffer, &NameSize, NULL,
436            &Type, NULL, 0);
437        if (WinStatus != ERROR_SUCCESS)
438        {
439            fprintf(stderr, "Could not get %s registry entry: %s\n",
440                Signature, WindowsFormatException(WinStatus));
441            Status = AE_ERROR;
442            goto Cleanup;
443        }
444
445        if (Type == REG_BINARY)
446        {
447            break;
448        }
449    }
450
451    /* Get the size of the table */
452
453    WinStatus = RegQueryValueEx(Handle, KeyBuffer, NULL, NULL,
454        NULL, &DataSize);
455    if (WinStatus != ERROR_SUCCESS)
456    {
457        fprintf(stderr, "Could not read the %s table size: %s\n",
458            Signature, WindowsFormatException(WinStatus));
459        Status = AE_ERROR;
460        goto Cleanup;
461    }
462
463    /* Allocate a new buffer for the table */
464
465    ReturnTable = malloc(DataSize);
466    if (!ReturnTable)
467    {
468        Status = AE_NO_MEMORY;
469        goto Cleanup;
470    }
471
472    /* Get the actual table from the registry */
473
474    WinStatus = RegQueryValueEx(Handle, KeyBuffer, NULL, NULL,
475        (UCHAR *)ReturnTable, &DataSize);
476
477    if (WinStatus != ERROR_SUCCESS)
478    {
479        fprintf(stderr, "Could not read %s data: %s\n",
480            Signature, WindowsFormatException(WinStatus));
481        free(ReturnTable);
482        Status = AE_ERROR;
483        goto Cleanup;
484    }
485
486    *Table = ReturnTable;
487    *Address = 0;
488
489Cleanup:
490    RegCloseKey(Handle);
491    return (Status);
492}
493
494
495/******************************************************************************
496 *
497 * FUNCTION:    AcpiOsGetTableByName
498 *
499 * PARAMETERS:  Signature       - ACPI Signature for desired table. Must be
500 *                                a null terminated 4-character string.
501 *              Instance        - For SSDTs (0...n). Use 0 otherwise.
502 *              Table           - Where a pointer to the table is returned
503 *              Address         - Where the table physical address is returned
504 *
505 * RETURN:      Status; Table buffer and physical address returned if AE_OK.
506 *              AE_LIMIT: Instance is beyond valid limit
507 *              AE_NOT_FOUND: A table with the signature was not found
508 *
509 * DESCRIPTION: Get an ACPI table via a table signature (4 ASCII characters).
510 *              Returns AE_LIMIT when an invalid instance is reached.
511 *              Table is obtained from the Windows registry.
512 *
513 * NOTE:        Assumes the input signature is uppercase.
514 *              Cannot get the physical address from the windows registry;
515 *              zero is returned instead.
516 *
517 *****************************************************************************/
518
519ACPI_STATUS
520AcpiOsGetTableByName(
521    char                    *Signature,
522    UINT32                  Instance,
523    ACPI_TABLE_HEADER       **Table,
524    ACPI_PHYSICAL_ADDRESS   *Address)
525{
526    LONG                    Result;
527    ACPI_STATUS             Status = AE_OK;
528    UINT32                  DataSize;
529    ACPI_TABLE_HEADER       *ReturnTable;
530    UINT32                  UIntSignature = 0;
531
532
533    /* Multiple instances are only supported for SSDT tables. */
534
535    if (Instance > 0 && !ACPI_COMPARE_NAMESEG (Signature, ACPI_SIG_SSDT))
536    {
537        return (AE_LIMIT);
538    }
539
540    if (ACPI_COMPARE_NAMESEG (Signature, ACPI_SIG_SSDT))
541    {
542        Status = WindowsGetTableFromRegistry ("SSDT", Instance, Table, Address);
543        return (Status);
544    }
545
546    /* GetSystemFirmwareTable requires the table signature to be UINT32 */
547
548    UIntSignature = *ACPI_CAST_PTR (UINT32, Signature);
549    DataSize = GetSystemFirmwareTable('ACPI', UIntSignature, NULL, 0);
550    if (!DataSize)
551    {
552        fprintf(stderr, "The table signature %s does not exist.", Signature);
553        return (AE_ERROR);
554    }
555
556    ReturnTable = malloc(DataSize);
557    if (!ReturnTable)
558    {
559        return (AE_NO_MEMORY);
560    }
561
562    Result = GetSystemFirmwareTable('ACPI', UIntSignature, ReturnTable, DataSize);
563    if (Result > (LONG) DataSize)
564    {
565        /* Clean up */
566
567        fprintf (stderr, "Could not read %s data\n", Signature);
568        free (ReturnTable);
569        return (AE_ERROR);
570    }
571
572    *Table = ReturnTable;
573    return (Status);
574}
575
576
577/* These are here for acpidump only, so we don't need to link oswinxf */
578
579#ifdef ACPI_DUMP_APP
580/******************************************************************************
581 *
582 * FUNCTION:    AcpiOsMapMemory
583 *
584 * PARAMETERS:  Where               - Physical address of memory to be mapped
585 *              Length              - How much memory to map
586 *
587 * RETURN:      Pointer to mapped memory. Null on error.
588 *
589 * DESCRIPTION: Map physical memory into caller's address space
590 *
591 *****************************************************************************/
592
593void *
594AcpiOsMapMemory (
595    ACPI_PHYSICAL_ADDRESS   Where,
596    ACPI_SIZE               Length)
597{
598
599    return (ACPI_TO_POINTER ((ACPI_SIZE) Where));
600}
601
602
603/******************************************************************************
604 *
605 * FUNCTION:    AcpiOsUnmapMemory
606 *
607 * PARAMETERS:  Where               - Logical address of memory to be unmapped
608 *              Length              - How much memory to unmap
609 *
610 * RETURN:      None.
611 *
612 * DESCRIPTION: Delete a previously created mapping. Where and Length must
613 *              correspond to a previous mapping exactly.
614 *
615 *****************************************************************************/
616
617void
618AcpiOsUnmapMemory (
619    void                    *Where,
620    ACPI_SIZE               Length)
621{
622
623    return;
624}
625#endif
626