xref: /illumos-gate/usr/src/cmd/acpi/acpidump/osillumostbl.c (revision 55a13001fbd9772352bc050632ef966a249dc73b)
1 /*
2  *
3  * Module Name: osillumostbl - illumos OSL for obtaining ACPI tables
4  * This file is derived from the Intel oslinuxtbl source file.
5  *
6  */
7 
8 /*
9  * Copyright (C) 2000 - 2016, Intel Corp.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions, and the following disclaimer,
17  *    without modification.
18  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
19  *    substantially similar to the "NO WARRANTY" disclaimer below
20  *    ("Disclaimer") and any redistribution must be conditioned upon
21  *    including a substantially similar Disclaimer requirement for further
22  *    binary redistribution.
23  * 3. Neither the names of the above-listed copyright holders nor the names
24  *    of any contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * Alternatively, this software may be distributed under the terms of the
28  * GNU General Public License ("GPL") version 2 as published by the Free
29  * Software Foundation.
30  *
31  * NO WARRANTY
32  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
35  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
36  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
41  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42  * POSSIBILITY OF SUCH DAMAGES.
43  */
44 
45 /*
46  * Copyright 2016 Joyent, Inc.
47  */
48 
49 #include <stdarg.h>
50 #include <string.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include "acpidump.h"
54 
55 #define	_COMPONENT	ACPI_OS_SERVICES
56     ACPI_MODULE_NAME("osillumostbl")
57 
58 /* List of information about obtained ACPI tables */
59 
60 typedef struct osl_table_info
61 {
62 	struct osl_table_info	*Next;
63 	UINT32			Instance;
64 	char			Signature[ACPI_NAME_SIZE];
65 } OSL_TABLE_INFO;
66 
67 /* Local prototypes */
68 static ACPI_STATUS
69 OslTableInitialize(void);
70 static ACPI_STATUS OslTableNameFromFile(char *, char *, UINT32 *);
71 static ACPI_STATUS OslAddTableToList(char *);
72 static ACPI_STATUS OslMapTable(ACPI_SIZE, char *, ACPI_TABLE_HEADER **);
73 static void OslUnmapTable(ACPI_TABLE_HEADER *);
74 static ACPI_STATUS OslLoadRsdp(void);
75 static ACPI_STATUS OslListBiosTables(void);
76 static ACPI_STATUS OslGetBiosTable(char *, UINT32, ACPI_TABLE_HEADER **,
77     ACPI_PHYSICAL_ADDRESS *);
78 static ACPI_STATUS OslGetLastStatus(ACPI_STATUS);
79 
80 static int pagesize;
81 
82 /* Initialization flags */
83 UINT8			Gbl_TableListInitialized = FALSE;
84 
85 /* Local copies of main ACPI tables */
86 ACPI_TABLE_RSDP		Gbl_Rsdp;
87 ACPI_TABLE_FADT		*Gbl_Fadt = NULL;
88 ACPI_TABLE_RSDT		*Gbl_Rsdt = NULL;
89 ACPI_TABLE_XSDT		*Gbl_Xsdt = NULL;
90 
91 /* Table addresses */
92 ACPI_PHYSICAL_ADDRESS	Gbl_FadtAddress = 0;
93 ACPI_PHYSICAL_ADDRESS	Gbl_RsdpAddress = 0;
94 
95 /* Revision of RSD PTR */
96 UINT8			Gbl_Revision = 0;
97 
98 OSL_TABLE_INFO		*Gbl_TableListHead = NULL;
99 UINT32			Gbl_TableCount = 0;
100 
101 /*
102  *
103  * FUNCTION:    OslGetLastStatus
104  *
105  * PARAMETERS:  DefaultStatus   - Default error status to return
106  *
107  * RETURN:      Status; Converted from errno.
108  *
109  * DESCRIPTION: Get last errno and conver it to ACPI_STATUS.
110  *
111  */
112 static ACPI_STATUS
113 OslGetLastStatus(ACPI_STATUS DefaultStatus)
114 {
115 	switch (errno) {
116 	case EACCES:
117 	case EPERM:
118 		return (AE_ACCESS);
119 
120 	case ENOENT:
121 		return (AE_NOT_FOUND);
122 
123 	case ENOMEM:
124 		return (AE_NO_MEMORY);
125 
126 	default:
127 		return (DefaultStatus);
128 	}
129 }
130 
131 /*
132  *
133  * FUNCTION:    AcpiOsGetTableByAddress
134  *
135  * PARAMETERS:  Address         - Physical address of the ACPI table
136  *              Table           - Where a pointer to the table is returned
137  *
138  * RETURN:      Status; Table buffer is returned if AE_OK.
139  *              AE_NOT_FOUND: A valid table was not found at the address
140  *
141  * DESCRIPTION: Get an ACPI table via a physical memory address.
142  *
143  */
144 ACPI_STATUS
145 AcpiOsGetTableByAddress(ACPI_PHYSICAL_ADDRESS Address,
146     ACPI_TABLE_HEADER **Table)
147 {
148 	UINT32			TableLength;
149 	ACPI_TABLE_HEADER	*MappedTable;
150 	ACPI_TABLE_HEADER	*LocalTable = NULL;
151 	ACPI_STATUS		Status = AE_OK;
152 
153 	/*
154 	 * Get main ACPI tables from memory on first invocation of this
155 	 * function
156 	 */
157 	Status = OslTableInitialize();
158 	if (ACPI_FAILURE(Status)) {
159 		return (Status);
160 	}
161 
162 	/* Map the table and validate it */
163 
164 	Status = OslMapTable(Address, NULL, &MappedTable);
165 	if (ACPI_FAILURE(Status)) {
166 		return (Status);
167 	}
168 
169 	/* Copy table to local buffer and return it */
170 
171 	TableLength = ApGetTableLength(MappedTable);
172 	if (TableLength == 0) {
173 		Status = AE_BAD_HEADER;
174 		goto Exit;
175 	}
176 
177 	LocalTable = calloc(1, TableLength);
178 	if (!LocalTable) {
179 		Status = AE_NO_MEMORY;
180 		goto Exit;
181 	}
182 
183 	memcpy(LocalTable, MappedTable, TableLength);
184 
185 Exit:
186 	OslUnmapTable(MappedTable);
187 	*Table = LocalTable;
188 	return (Status);
189 }
190 
191 /*
192  *
193  * FUNCTION:    AcpiOsGetTableByName
194  *
195  * PARAMETERS:  Signature       - ACPI Signature for desired table. Must be
196  *                                a null terminated 4-character string.
197  *              Instance        - Multiple table support for SSDT/UEFI (0...n)
198  *                                Must be 0 for other tables.
199  *              Table           - Where a pointer to the table is returned
200  *              Address         - Where the table physical address is returned
201  *
202  * RETURN:      Status; Table buffer and physical address returned if AE_OK.
203  *              AE_LIMIT: Instance is beyond valid limit
204  *              AE_NOT_FOUND: A table with the signature was not found
205  *
206  * NOTE:        Assumes the input signature is uppercase.
207  *
208  */
209 ACPI_STATUS
210 AcpiOsGetTableByName(char *Signature, UINT32 Instance,
211     ACPI_TABLE_HEADER **Table, ACPI_PHYSICAL_ADDRESS *Address)
212 {
213 	ACPI_STATUS	Status;
214 
215 	/*
216 	 * Get main ACPI tables from memory on first invocation of this
217 	 * function
218 	 */
219 	Status = OslTableInitialize();
220 	if (ACPI_FAILURE(Status)) {
221 		return (Status);
222 	}
223 
224 	/* attempt to extract it from the RSDT/XSDT */
225 	Status = OslGetBiosTable(Signature, Instance, Table, Address);
226 
227 	return (Status);
228 }
229 
230 /*
231  *
232  * FUNCTION:    OslAddTableToList
233  *
234  * PARAMETERS:  Signature       - Table signature
235  *
236  * RETURN:      Status; Successfully added if AE_OK.
237  *              AE_NO_MEMORY: Memory allocation error
238  *
239  * DESCRIPTION: Insert a table structure into OSL table list.
240  *
241  */
242 static ACPI_STATUS
243 OslAddTableToList(char *Signature)
244 {
245 	OSL_TABLE_INFO	*NewInfo;
246 	OSL_TABLE_INFO	*Next;
247 	UINT32		NextInstance = 0;
248 	UINT32		Instance = 0;
249 	BOOLEAN		Found = FALSE;
250 
251 	NewInfo = calloc(1, sizeof (OSL_TABLE_INFO));
252 	if (NewInfo == NULL) {
253 		return (AE_NO_MEMORY);
254 	}
255 
256 	ACPI_MOVE_NAME(NewInfo->Signature, Signature);
257 
258 	if (!Gbl_TableListHead) {
259 		Gbl_TableListHead = NewInfo;
260 	} else {
261 		Next = Gbl_TableListHead;
262 
263 		while (1) {
264 			if (ACPI_COMPARE_NAME(Next->Signature, Signature)) {
265 				if (Next->Instance == 0) {
266 					Found = TRUE;
267 				}
268 				if (Next->Instance >= NextInstance) {
269 					NextInstance = Next->Instance + 1;
270 				}
271 			}
272 
273 			if (!Next->Next) {
274 				break;
275 			}
276 			Next = Next->Next;
277 		}
278 		Next->Next = NewInfo;
279 	}
280 
281 	if (Found) {
282 		Instance = NextInstance;
283 	}
284 
285 	NewInfo->Instance = Instance;
286 	Gbl_TableCount++;
287 
288 	return (AE_OK);
289 }
290 
291 /*
292  *
293  * FUNCTION:    AcpiOsGetTableByIndex
294  *
295  * PARAMETERS:  Index           - Which table to get
296  *              Table           - Where a pointer to the table is returned
297  *              Instance        - Where a pointer to the table instance no. is
298  *                                returned
299  *              Address         - Where the table physical address is returned
300  *
301  * RETURN:      Status; Table buffer and physical address returned if AE_OK.
302  *              AE_LIMIT: Index is beyond valid limit
303  *
304  * DESCRIPTION: Get an ACPI table via an index value (0 through n). Returns
305  *              AE_LIMIT when an invalid index is reached. Index is not
306  *              necessarily an index into the RSDT/XSDT.
307  *
308  */
309 ACPI_STATUS
310 AcpiOsGetTableByIndex(UINT32 Index, ACPI_TABLE_HEADER **Table,
311     UINT32 *Instance, ACPI_PHYSICAL_ADDRESS *Address)
312 {
313 	OSL_TABLE_INFO	*Info;
314 	ACPI_STATUS	Status;
315 	UINT32		i;
316 
317 	/*
318 	 * Get main ACPI tables from memory on first invocation of this
319 	 * function.
320 	 */
321 
322 	Status = OslTableInitialize();
323 	if (ACPI_FAILURE(Status)) {
324 		return (Status);
325 	}
326 
327 	/* Validate Index */
328 
329 	if (Index >= Gbl_TableCount) {
330 		return (AE_LIMIT);
331 	}
332 
333 	/* Point to the table list entry specified by the Index argument */
334 
335 	Info = Gbl_TableListHead;
336 	for (i = 0; i < Index; i++) {
337 		Info = Info->Next;
338 	}
339 
340 	/* Now we can just get the table via the signature */
341 
342 	Status = AcpiOsGetTableByName(Info->Signature, Info->Instance,
343 	    Table, Address);
344 
345 	if (ACPI_SUCCESS(Status)) {
346 		*Instance = Info->Instance;
347 	}
348 	return (Status);
349 }
350 
351 /*
352  *
353  * FUNCTION:    OslLoadRsdp
354  *
355  * PARAMETERS:  None
356  *
357  * RETURN:      Status
358  *
359  * DESCRIPTION: Scan and load RSDP.
360  * See the find_rsdp() function in usr/src/uts/i86pc/os/fakebop.c, which is how
361  * the kernel finds the RSDP. That algorithm matches AcpiFindRootPointer().
362  * The code here is derived from AcpiFindRootPointer, except that we will try
363  * the BIOS if the EBDA fails, and we will copy the table if found.
364  */
365 static ACPI_STATUS
366 OslLoadRsdp(void)
367 {
368 	UINT8			*mapp;
369 	ACPI_TABLE_HEADER	*tblp;
370 	ACPI_SIZE		mapsize;
371 	ACPI_PHYSICAL_ADDRESS	physaddr;
372 
373 	/* 1a) Get the location of the Extended BIOS Data Area (EBDA) */
374 	mapp = AcpiOsMapMemory((ACPI_PHYSICAL_ADDRESS)ACPI_EBDA_PTR_LOCATION,
375 	    ACPI_EBDA_PTR_LENGTH);
376 	if (mapp == NULL)
377 		goto try_bios;
378 
379 	ACPI_MOVE_16_TO_32(&physaddr, mapp);
380 
381 	/* Convert segment part to physical address */
382 	physaddr <<= 4;
383 	AcpiOsUnmapMemory(mapp, ACPI_EBDA_PTR_LENGTH);
384 
385 	/* EBDA present? */
386 	if (physaddr <= 0x400)
387 		goto try_bios;
388 
389 	/*
390 	 * 1b) Search EBDA paragraphs (EBDA is required to be a minimum of 1K
391 	 * length)
392 	 */
393 	mapp = AcpiOsMapMemory(physaddr, ACPI_EBDA_WINDOW_SIZE);
394 	if (mapp == NULL) {
395 		(void) fprintf(stderr, "EBDA (0x%p) found, but is not "
396 		    "mappable\n", physaddr);
397 		goto try_bios;
398 	}
399 
400 	tblp = ACPI_CAST_PTR(ACPI_TABLE_HEADER,
401 	    AcpiTbScanMemoryForRsdp(mapp, ACPI_EBDA_WINDOW_SIZE));
402 	if (tblp != NULL) {
403 		physaddr += (ACPI_PHYSICAL_ADDRESS) ACPI_PTR_DIFF(tblp, mapp);
404 		Gbl_RsdpAddress = physaddr;
405 		memcpy(&Gbl_Rsdp, tblp, sizeof (ACPI_TABLE_RSDP));
406 		AcpiOsUnmapMemory(mapp, ACPI_EBDA_WINDOW_SIZE);
407 
408 		return (AE_OK);
409 	}
410 	AcpiOsUnmapMemory(mapp, ACPI_EBDA_WINDOW_SIZE);
411 
412 try_bios:
413 	/* Try to get RSDP from BIOS memory */
414 	if (Gbl_RsdpBase != NULL) {
415 		physaddr = Gbl_RsdpBase;
416 		mapsize = sizeof (ACPI_TABLE_RSDP);
417 	} else {
418 		physaddr = ACPI_HI_RSDP_WINDOW_BASE;
419 		mapsize = ACPI_HI_RSDP_WINDOW_SIZE;
420 	}
421 
422 	mapp = AcpiOsMapMemory(physaddr, mapsize);
423 	if (mapp == NULL)
424 		return (OslGetLastStatus(AE_BAD_ADDRESS));
425 
426 	/* Search low memory for the RSDP */
427 	tblp = ACPI_CAST_PTR(ACPI_TABLE_HEADER,
428 	    AcpiTbScanMemoryForRsdp(mapp, mapsize));
429 	if (tblp == NULL) {
430 		AcpiOsUnmapMemory(mapp, mapsize);
431 		return (AE_NOT_FOUND);
432 	}
433 
434 	physaddr += (ACPI_PHYSICAL_ADDRESS) ACPI_PTR_DIFF(tblp, mapp);
435 	Gbl_RsdpAddress = physaddr;
436 	memcpy(&Gbl_Rsdp, tblp, sizeof (ACPI_TABLE_RSDP));
437 	AcpiOsUnmapMemory(mapp, mapsize);
438 
439 	return (AE_OK);
440 }
441 
442 /*
443  *
444  * FUNCTION:    OslCanUseXsdt
445  *
446  * PARAMETERS:  None
447  *
448  * RETURN:      TRUE if XSDT is allowed to be used.
449  *
450  * DESCRIPTION: This function collects logic that can be used to determine if
451  *              XSDT should be used instead of RSDT.
452  *
453  */
454 static BOOLEAN
455 OslCanUseXsdt(void)
456 {
457 	if (Gbl_Revision && !AcpiGbl_DoNotUseXsdt) {
458 		return (TRUE);
459 	} else {
460 		return (FALSE);
461 	}
462 }
463 
464 /*
465  *
466  * FUNCTION:    OslTableInitialize
467  *
468  * PARAMETERS:  None
469  *
470  * RETURN:      Status
471  *
472  * DESCRIPTION: Initialize ACPI table data. Get and store main ACPI tables to
473  *              local variables. Main ACPI tables include RSDT, FADT, RSDT,
474  *              and/or XSDT.
475  *
476  */
477 static ACPI_STATUS
478 OslTableInitialize(void)
479 {
480 	ACPI_STATUS		Status;
481 	ACPI_PHYSICAL_ADDRESS	Address;
482 
483 	if (Gbl_TableListInitialized) {
484 		return (AE_OK);
485 	}
486 
487 	/* Get RSDP from memory */
488 
489 	Status = OslLoadRsdp();
490 	if (ACPI_FAILURE(Status)) {
491 		return (Status);
492 	}
493 
494 	/* Get XSDT from memory */
495 
496 	if (Gbl_Rsdp.Revision && !Gbl_DoNotDumpXsdt) {
497 		if (Gbl_Xsdt) {
498 			free(Gbl_Xsdt);
499 			Gbl_Xsdt = NULL;
500 		}
501 
502 		Gbl_Revision = 2;
503 		Status = OslGetBiosTable(ACPI_SIG_XSDT, 0,
504 		    ACPI_CAST_PTR(ACPI_TABLE_HEADER *, &Gbl_Xsdt), &Address);
505 		if (ACPI_FAILURE(Status)) {
506 			return (Status);
507 		}
508 	}
509 
510 	/* Get RSDT from memory */
511 
512 	if (Gbl_Rsdp.RsdtPhysicalAddress) {
513 		if (Gbl_Rsdt) {
514 			free(Gbl_Rsdt);
515 			Gbl_Rsdt = NULL;
516 		}
517 
518 		Status = OslGetBiosTable(ACPI_SIG_RSDT, 0,
519 		    ACPI_CAST_PTR(ACPI_TABLE_HEADER *, &Gbl_Rsdt), &Address);
520 		if (ACPI_FAILURE(Status)) {
521 			return (Status);
522 		}
523 	}
524 
525 	/* Get FADT from memory */
526 
527 	if (Gbl_Fadt) {
528 		free(Gbl_Fadt);
529 		Gbl_Fadt = NULL;
530 	}
531 
532 	Status = OslGetBiosTable(ACPI_SIG_FADT, 0,
533 	    ACPI_CAST_PTR(ACPI_TABLE_HEADER *, &Gbl_Fadt), &Gbl_FadtAddress);
534 	if (ACPI_FAILURE(Status)) {
535 		return (Status);
536 	}
537 
538 	/* Add mandatory tables to global table list first */
539 
540 	Status = OslAddTableToList(ACPI_RSDP_NAME);
541 	if (ACPI_FAILURE(Status)) {
542 		return (Status);
543 	}
544 
545 	Status = OslAddTableToList(ACPI_SIG_RSDT);
546 	if (ACPI_FAILURE(Status)) {
547 		return (Status);
548 	}
549 
550 	if (Gbl_Revision == 2) {
551 		Status = OslAddTableToList(ACPI_SIG_XSDT);
552 		if (ACPI_FAILURE(Status)) {
553 			return (Status);
554 		}
555 	}
556 
557 	Status = OslAddTableToList(ACPI_SIG_DSDT);
558 	if (ACPI_FAILURE(Status)) {
559 		return (Status);
560 	}
561 
562 	Status = OslAddTableToList(ACPI_SIG_FACS);
563 	if (ACPI_FAILURE(Status)) {
564 		return (Status);
565 	}
566 
567 	/* Add all tables found in the memory */
568 
569 	Status = OslListBiosTables();
570 	if (ACPI_FAILURE(Status)) {
571 		return (Status);
572 	}
573 
574 	Gbl_TableListInitialized = TRUE;
575 	return (AE_OK);
576 }
577 
578 
579 /*
580  *
581  * FUNCTION:    OslListBiosTables
582  *
583  * PARAMETERS:  None
584  *
585  * RETURN:      Status; Table list is initialized if AE_OK.
586  *
587  * DESCRIPTION: Add ACPI tables to the table list from memory.
588  */
589 static ACPI_STATUS
590 OslListBiosTables(void)
591 {
592 	ACPI_TABLE_HEADER	*MappedTable = NULL;
593 	UINT8			*TableData;
594 	UINT32			NumberOfTables;
595 	UINT8			ItemSize;
596 	ACPI_PHYSICAL_ADDRESS	TableAddress = 0;
597 	ACPI_STATUS		Status = AE_OK;
598 	UINT32			i;
599 
600 	if (OslCanUseXsdt()) {
601 		ItemSize = sizeof (UINT64);
602 		TableData = ACPI_CAST8(Gbl_Xsdt) + sizeof (ACPI_TABLE_HEADER);
603 		NumberOfTables = (UINT32)
604 		    ((Gbl_Xsdt->Header.Length - sizeof (ACPI_TABLE_HEADER))
605 		    / ItemSize);
606 
607 	} else {
608 		/* Use RSDT if XSDT is not available */
609 		ItemSize = sizeof (UINT32);
610 		TableData = ACPI_CAST8(Gbl_Rsdt) + sizeof (ACPI_TABLE_HEADER);
611 		NumberOfTables = (UINT32)
612 		    ((Gbl_Rsdt->Header.Length - sizeof (ACPI_TABLE_HEADER))
613 		    / ItemSize);
614 	}
615 
616 	/* Search RSDT/XSDT for the requested table */
617 
618 	for (i = 0; i < NumberOfTables; ++i, TableData += ItemSize) {
619 		if (OslCanUseXsdt()) {
620 			TableAddress =
621 			    (ACPI_PHYSICAL_ADDRESS) (*ACPI_CAST64(TableData));
622 		} else {
623 			TableAddress =
624 			    (ACPI_PHYSICAL_ADDRESS) (*ACPI_CAST32(TableData));
625 		}
626 
627 		/* Skip NULL entries in RSDT/XSDT */
628 		if (TableAddress == NULL) {
629 			continue;
630 		}
631 
632 		Status = OslMapTable(TableAddress, NULL, &MappedTable);
633 		if (ACPI_FAILURE(Status)) {
634 			return (Status);
635 		}
636 
637 		OslAddTableToList(MappedTable->Signature);
638 		OslUnmapTable(MappedTable);
639 	}
640 
641 	return (AE_OK);
642 }
643 
644 /*
645  *
646  * FUNCTION:    OslGetBiosTable
647  *
648  * PARAMETERS:  Signature       - ACPI Signature for common table. Must be
649  *                                a null terminated 4-character string.
650  *              Instance        - Multiple table support for SSDT/UEFI (0...n)
651  *                                Must be 0 for other tables.
652  *              Table           - Where a pointer to the table is returned
653  *              Address         - Where the table physical address is returned
654  *
655  * RETURN:      Status; Table buffer and physical address returned if AE_OK.
656  *              AE_LIMIT: Instance is beyond valid limit
657  *              AE_NOT_FOUND: A table with the signature was not found
658  *
659  * DESCRIPTION: Get a BIOS provided ACPI table
660  *
661  * NOTE:        Assumes the input signature is uppercase.
662  *
663  */
664 static ACPI_STATUS
665 OslGetBiosTable(char *Signature, UINT32 Instance, ACPI_TABLE_HEADER **Table,
666     ACPI_PHYSICAL_ADDRESS *Address)
667 {
668 	ACPI_TABLE_HEADER	*LocalTable = NULL;
669 	ACPI_TABLE_HEADER	*MappedTable = NULL;
670 	UINT8			*TableData;
671 	UINT8			NumberOfTables;
672 	UINT8			ItemSize;
673 	UINT32			CurrentInstance = 0;
674 	ACPI_PHYSICAL_ADDRESS	TableAddress = 0;
675 	UINT32			TableLength = 0;
676 	ACPI_STATUS		Status = AE_OK;
677 	UINT32			i;
678 
679 	/* Handle special tables whose addresses are not in RSDT/XSDT */
680 
681 	if (ACPI_COMPARE_NAME(Signature, ACPI_RSDP_NAME) ||
682 	    ACPI_COMPARE_NAME(Signature, ACPI_SIG_RSDT) ||
683 	    ACPI_COMPARE_NAME(Signature, ACPI_SIG_XSDT) ||
684 	    ACPI_COMPARE_NAME(Signature, ACPI_SIG_DSDT) ||
685 	    ACPI_COMPARE_NAME(Signature, ACPI_SIG_FACS)) {
686 		if (Instance > 0) {
687 			return (AE_LIMIT);
688 		}
689 
690 		/*
691 		 * Get the appropriate address, either 32-bit or 64-bit. Be very
692 		 * careful about the FADT length and validate table addresses.
693 		 * Note: The 64-bit addresses have priority.
694 		 */
695 		if (ACPI_COMPARE_NAME(Signature, ACPI_SIG_DSDT)) {
696 			if ((Gbl_Fadt->Header.Length >= MIN_FADT_FOR_XDSDT) &&
697 			    Gbl_Fadt->XDsdt) {
698 				TableAddress =
699 				    (ACPI_PHYSICAL_ADDRESS) Gbl_Fadt->XDsdt;
700 
701 			} else if (Gbl_Fadt->Header.Length >=
702 			    MIN_FADT_FOR_DSDT && Gbl_Fadt->Dsdt) {
703 				TableAddress =
704 				    (ACPI_PHYSICAL_ADDRESS) Gbl_Fadt->Dsdt;
705 			}
706 
707 		} else if (ACPI_COMPARE_NAME(Signature, ACPI_SIG_FACS)) {
708 			if ((Gbl_Fadt->Header.Length >= MIN_FADT_FOR_XFACS) &&
709 			    Gbl_Fadt->XFacs) {
710 				TableAddress =
711 				    (ACPI_PHYSICAL_ADDRESS) Gbl_Fadt->XFacs;
712 
713 			} else if (Gbl_Fadt->Header.Length >=
714 			    MIN_FADT_FOR_FACS && Gbl_Fadt->Facs) {
715 				TableAddress =
716 				    (ACPI_PHYSICAL_ADDRESS) Gbl_Fadt->Facs;
717 			}
718 
719 		} else if (ACPI_COMPARE_NAME(Signature, ACPI_SIG_XSDT)) {
720 			if (!Gbl_Revision) {
721 				return (AE_BAD_SIGNATURE);
722 			}
723 			TableAddress = (ACPI_PHYSICAL_ADDRESS)
724 			    Gbl_Rsdp.XsdtPhysicalAddress;
725 
726 		} else if (ACPI_COMPARE_NAME(Signature, ACPI_SIG_RSDT)) {
727 			TableAddress = (ACPI_PHYSICAL_ADDRESS)
728 			    Gbl_Rsdp.RsdtPhysicalAddress;
729 
730 		} else {
731 			TableAddress = (ACPI_PHYSICAL_ADDRESS) Gbl_RsdpAddress;
732 			Signature = ACPI_SIG_RSDP;
733 		}
734 
735 		/* Now we can get the requested special table */
736 
737 		Status = OslMapTable(TableAddress, Signature, &MappedTable);
738 		if (ACPI_FAILURE(Status)) {
739 			return (Status);
740 		}
741 
742 		TableLength = ApGetTableLength(MappedTable);
743 
744 	} else {
745 		/* Case for a normal ACPI table */
746 		if (OslCanUseXsdt()) {
747 			ItemSize = sizeof (UINT64);
748 			TableData = ACPI_CAST8(Gbl_Xsdt) +
749 			    sizeof (ACPI_TABLE_HEADER);
750 			NumberOfTables = (UINT8) ((Gbl_Xsdt->Header.Length -
751 			    sizeof (ACPI_TABLE_HEADER))
752 			    / ItemSize);
753 
754 		} else {
755 			/* Use RSDT if XSDT is not available */
756 			ItemSize = sizeof (UINT32);
757 			TableData = ACPI_CAST8(Gbl_Rsdt) +
758 			    sizeof (ACPI_TABLE_HEADER);
759 			NumberOfTables = (UINT8) ((Gbl_Rsdt->Header.Length -
760 			    sizeof (ACPI_TABLE_HEADER))
761 			    / ItemSize);
762 		}
763 
764 		/* Search RSDT/XSDT for the requested table */
765 
766 		for (i = 0; i < NumberOfTables; ++i, TableData += ItemSize) {
767 			if (OslCanUseXsdt()) {
768 				TableAddress = (ACPI_PHYSICAL_ADDRESS)
769 				    (*ACPI_CAST64(TableData));
770 			} else {
771 				TableAddress = (ACPI_PHYSICAL_ADDRESS)
772 				    (*ACPI_CAST32(TableData));
773 			}
774 
775 			/* Skip NULL entries in RSDT/XSDT */
776 
777 			if (TableAddress == NULL) {
778 				continue;
779 			}
780 
781 			Status = OslMapTable(TableAddress, NULL, &MappedTable);
782 			if (ACPI_FAILURE(Status)) {
783 				return (Status);
784 			}
785 			TableLength = MappedTable->Length;
786 
787 			/* Does this table match the requested signature? */
788 
789 			if (!ACPI_COMPARE_NAME(MappedTable->Signature,
790 			    Signature)) {
791 				OslUnmapTable(MappedTable);
792 				MappedTable = NULL;
793 				continue;
794 			}
795 
796 			/* Match table instance (for SSDT/UEFI tables) */
797 
798 			if (CurrentInstance != Instance) {
799 				OslUnmapTable(MappedTable);
800 				MappedTable = NULL;
801 				CurrentInstance++;
802 				continue;
803 			}
804 
805 			break;
806 		}
807 	}
808 
809 	if (MappedTable == NULL) {
810 		return (AE_LIMIT);
811 	}
812 
813 	if (TableLength == 0) {
814 		Status = AE_BAD_HEADER;
815 		goto Exit;
816 	}
817 
818 	/* Copy table to local buffer and return it */
819 
820 	LocalTable = calloc(1, TableLength);
821 	if (LocalTable == NULL) {
822 		Status = AE_NO_MEMORY;
823 		goto Exit;
824 	}
825 
826 	memcpy(LocalTable, MappedTable, TableLength);
827 	*Address = TableAddress;
828 	*Table = LocalTable;
829 
830 Exit:
831 	OslUnmapTable(MappedTable);
832 	return (Status);
833 }
834 
835 /*
836  *
837  * FUNCTION:    OslMapTable
838  *
839  * PARAMETERS:  Address             - Address of the table in memory
840  *              Signature           - Optional ACPI Signature for desired table.
841  *                                    Null terminated 4-character string.
842  *              Table               - Where a pointer to the mapped table is
843  *                                    returned
844  *
845  * RETURN:      Status; Mapped table is returned if AE_OK.
846  *              AE_NOT_FOUND: A valid table was not found at the address
847  *
848  * DESCRIPTION: Map entire ACPI table into caller's address space.
849  *
850  */
851 static ACPI_STATUS
852 OslMapTable(ACPI_SIZE Address, char *Signature, ACPI_TABLE_HEADER **Table)
853 {
854 	ACPI_TABLE_HEADER	*MappedTable;
855 	UINT32			Length;
856 
857 	if (Address == NULL) {
858 		return (AE_BAD_ADDRESS);
859 	}
860 
861 	/*
862 	 * Map the header so we can get the table length.
863 	 * Use sizeof (ACPI_TABLE_HEADER) as:
864 	 * 1. it is bigger than 24 to include RSDP->Length
865 	 * 2. it is smaller than sizeof (ACPI_TABLE_RSDP)
866 	 */
867 	MappedTable = AcpiOsMapMemory(Address, sizeof (ACPI_TABLE_HEADER));
868 	if (MappedTable == NULL) {
869 		(void) fprintf(stderr, "Could not map table header at "
870 		    "0x%8.8X%8.8X\n", ACPI_FORMAT_UINT64(Address));
871 		return (OslGetLastStatus(AE_BAD_ADDRESS));
872 	}
873 
874 	/* If specified, signature must match */
875 
876 	if (Signature != NULL) {
877 		if (ACPI_VALIDATE_RSDP_SIG(Signature)) {
878 			if (!ACPI_VALIDATE_RSDP_SIG(MappedTable->Signature)) {
879 				AcpiOsUnmapMemory(MappedTable,
880 				    sizeof (ACPI_TABLE_HEADER));
881 				return (AE_BAD_SIGNATURE);
882 			}
883 		} else if (!ACPI_COMPARE_NAME(Signature,
884 		    MappedTable->Signature)) {
885 			AcpiOsUnmapMemory(MappedTable,
886 			    sizeof (ACPI_TABLE_HEADER));
887 			return (AE_BAD_SIGNATURE);
888 		}
889 	}
890 
891 	/* Map the entire table */
892 
893 	Length = ApGetTableLength(MappedTable);
894 	AcpiOsUnmapMemory(MappedTable, sizeof (ACPI_TABLE_HEADER));
895 	if (Length == 0) {
896 		return (AE_BAD_HEADER);
897 	}
898 
899 	MappedTable = AcpiOsMapMemory(Address, Length);
900 	if (MappedTable == NULL) {
901 		(void) fprintf(stderr, "Could not map table at 0x%8.8X%8.8X "
902 		    "length %8.8X\n", ACPI_FORMAT_UINT64(Address), Length);
903 		return (OslGetLastStatus(AE_INVALID_TABLE_LENGTH));
904 	}
905 
906 	(void) ApIsValidChecksum(MappedTable);
907 
908 	*Table = MappedTable;
909 	return (AE_OK);
910 }
911 
912 
913 /*
914  *
915  * FUNCTION:    OslUnmapTable
916  *
917  * PARAMETERS:  Table               - A pointer to the mapped table
918  *
919  * RETURN:      None
920  *
921  * DESCRIPTION: Unmap entire ACPI table.
922  *
923  */
924 static void
925 OslUnmapTable(ACPI_TABLE_HEADER *Table)
926 {
927 	if (Table != NULL) {
928 		AcpiOsUnmapMemory(Table, ApGetTableLength(Table));
929 	}
930 }
931 
932 /*
933  *
934  * FUNCTION:    OslTableNameFromFile
935  *
936  * PARAMETERS:  Filename            - File that contains the desired table
937  *              Signature           - Pointer to 4-character buffer to store
938  *                                    extracted table signature.
939  *              Instance            - Pointer to integer to store extracted
940  *                                    table instance number.
941  *
942  * RETURN:      Status; Table name is extracted if AE_OK.
943  *
944  * DESCRIPTION: Extract table signature and instance number from a table file
945  *              name.
946  *
947  */
948 static ACPI_STATUS
949 OslTableNameFromFile(char *Filename, char *Signature, UINT32 *Instance)
950 {
951 	/* Ignore meaningless files */
952 
953 	if (strlen(Filename) < ACPI_NAME_SIZE) {
954 		return (AE_BAD_SIGNATURE);
955 	}
956 
957 	/* Extract instance number */
958 
959 	if (isdigit((int)Filename[ACPI_NAME_SIZE])) {
960 		sscanf(&Filename[ACPI_NAME_SIZE], "%u", Instance);
961 	} else if (strlen(Filename) != ACPI_NAME_SIZE) {
962 		return (AE_BAD_SIGNATURE);
963 	} else {
964 		*Instance = 0;
965 	}
966 
967 	/* Extract signature */
968 
969 	ACPI_MOVE_NAME(Signature, Filename);
970 	return (AE_OK);
971 }
972 
973 UINT32
974 CmGetFileSize(ACPI_FILE File)
975 {
976 	int fd;
977 	struct stat sb;
978 
979 	fd = fileno(File);
980 	if (fstat(fd, &sb) != 0)
981 		return (ACPI_UINT32_MAX);
982 	return ((UINT32)sb.st_size);
983 }
984 
985 void *
986 AcpiOsAllocateZeroed(ACPI_SIZE Size)
987 {
988 	return (calloc(1, Size));
989 }
990 
991 void
992 AcpiOsFree(void *p)
993 {
994 	free(p);
995 }
996 
997 ACPI_FILE
998 AcpiOsOpenFile(const char *Path, UINT8 Modes)
999 {
1000 	char mode[3];
1001 
1002 	bzero(mode, sizeof (mode));
1003 	if ((Modes & ACPI_FILE_READING) != 0)
1004 		(void) strlcat(mode, "r", sizeof (mode));
1005 
1006 	if ((Modes & ACPI_FILE_WRITING) != 0)
1007 		(void) strlcat(mode, "w", sizeof (mode));
1008 
1009 	return (fopen(Path, mode));
1010 }
1011 
1012 void
1013 AcpiOsCloseFile(ACPI_FILE File)
1014 {
1015 	fclose(File);
1016 }
1017 
1018 int
1019 AcpiOsReadFile(ACPI_FILE File, void *Buffer, ACPI_SIZE Size, ACPI_SIZE Count)
1020 {
1021 	return (fread(Buffer, Size, Count, File));
1022 }
1023 
1024 void *
1025 AcpiOsMapMemory(ACPI_PHYSICAL_ADDRESS Where, ACPI_SIZE Length)
1026 {
1027 	int fd;
1028 	void *p;
1029 	ulong_t offset;
1030 
1031 	if ((fd = open("/dev/xsvc", O_RDONLY)) < 0)
1032 		return (NULL);
1033 
1034 	if (pagesize == 0) {
1035 		pagesize = getpagesize();
1036 	}
1037 
1038 	offset = Where % pagesize;
1039 	p = mmap(NULL, Length + offset, PROT_READ, MAP_SHARED | MAP_NORESERVE,
1040 	    fd, Where - offset);
1041 
1042 	(void) close(fd);
1043 
1044 	if (p == MAP_FAILED)
1045 		return (NULL);
1046 	p = (char *)p + offset;
1047 	return (p);
1048 }
1049 
1050 void
1051 AcpiOsUnmapMemory(void *LogicalAddress, ACPI_SIZE Size)
1052 {
1053 	ulong_t offset;
1054 	void *p;
1055 
1056 	offset = (ulong_t)LogicalAddress % pagesize;
1057 	p = (void *)((char *)LogicalAddress - offset);
1058 
1059 	(void) munmap(p, Size + offset);
1060 }
1061