xref: /netbsd-src/sys/external/bsd/acpica/dist/os_specific/service_layers/oswintbl.c (revision 046a29855e04359424fd074e8313af6b6be8cfb6)
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 
62 static char *
63 WindowsFormatException (
64     LONG                WinStatus);
65 
66 /* Globals */
67 
68 #define LOCAL_BUFFER_SIZE           64
69 
70 static char             KeyBuffer[LOCAL_BUFFER_SIZE];
71 static char             ErrorBuffer[LOCAL_BUFFER_SIZE];
72 
73 /*
74  * List of table signatures reported by EnumSystemFirmwareTables ()
75  */
76 UINT32                  *Gbl_AvailableTableSignatures;
77 UINT32                  Gbl_TableCount = 0;
78 UINT32                  Gbl_SsdtInstance = 0;
79 
80 BOOLEAN                 Gbl_TableListInitialized = FALSE;
81 
82 static ACPI_STATUS
83 OslTableInitialize (
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 
100 static char *
WindowsFormatException(LONG WinStatus)101 WindowsFormatException (
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 
129 ACPI_STATUS
AcpiOsGetTableByAddress(ACPI_PHYSICAL_ADDRESS Address,ACPI_TABLE_HEADER ** Table)130 AcpiOsGetTableByAddress (
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 
164 ACPI_STATUS
AcpiOsGetTableByIndex(UINT32 Index,ACPI_TABLE_HEADER ** Table,UINT32 * Instance,ACPI_PHYSICAL_ADDRESS * Address)165 AcpiOsGetTableByIndex (
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  *****************************************************************************/
239 static ACPI_STATUS
OslTableInitialize(void)240 OslTableInitialize (
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 
312 static ACPI_STATUS
WindowsGetTableFromRegistry(char * Signature,UINT32 Instance,ACPI_TABLE_HEADER ** Table,ACPI_PHYSICAL_ADDRESS * Address)313 WindowsGetTableFromRegistry (
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 
489 Cleanup:
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 
519 ACPI_STATUS
AcpiOsGetTableByName(char * Signature,UINT32 Instance,ACPI_TABLE_HEADER ** Table,ACPI_PHYSICAL_ADDRESS * Address)520 AcpiOsGetTableByName(
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 
593 void *
AcpiOsMapMemory(ACPI_PHYSICAL_ADDRESS Where,ACPI_SIZE Length)594 AcpiOsMapMemory (
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 
617 void
AcpiOsUnmapMemory(void * Where,ACPI_SIZE Length)618 AcpiOsUnmapMemory (
619     void                    *Where,
620     ACPI_SIZE               Length)
621 {
622 
623     return;
624 }
625 #endif
626