1/*
2 * Copyright 2014, Jessica Hamilton, jessica.l.hamilton@gmail.com.
3 * Copyright 2011, Rene Gollent, rene@gollent.com.
4 * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
5 * Copyright 2007, Michael Lotz, mmlr@mlotz.ch
6 * Copyright 2004-2005, Axel D��rfler, axeld@pinc-software.de.
7 * Distributed under the terms of the MIT License.
8 *
9 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
10 * Distributed under the terms of the NewOS License.
11*/
12
13
14#include <string.h>
15
16#include <KernelExport.h>
17#include <SupportDefs.h>
18
19#include <arch/x86/arch_acpi.h>
20#include <boot/stage2.h>
21#include <boot/platform.h>
22#include <boot/stdio.h>
23
24#include "efi_platform.h"
25#include "acpi.h"
26#include "mmu.h"
27
28
29#define TRACE_ACPI
30#ifdef TRACE_ACPI
31#	define TRACE(x) dprintf x
32#else
33#	define TRACE(x) ;
34#endif
35
36
37static acpi_descriptor_header* sAcpiRsdt; // System Description Table
38static acpi_descriptor_header* sAcpiXsdt; // Extended System Description Table
39static int32 sNumEntries = -1;
40
41
42static status_t
43acpi_validate_rsdp(acpi_rsdp* rsdp)
44{
45	const char* data = (const char*)rsdp;
46	unsigned char checksum = 0;
47	for (uint32 i = 0; i < sizeof(acpi_rsdp_legacy); i++)
48		checksum += data[i];
49
50	if ((checksum & 0xff) != 0) {
51		TRACE(("acpi: rsdp failed basic checksum\n"));
52		return B_BAD_DATA;
53	}
54
55	// for ACPI 2.0+ we need to also validate the extended checksum
56	if (rsdp->revision > 0) {
57		for (uint32 i = sizeof(acpi_rsdp_legacy);
58			i < sizeof(acpi_rsdp_extended); i++) {
59				checksum += data[i];
60		}
61
62		if ((checksum & 0xff) != 0) {
63			TRACE(("acpi: rsdp failed extended checksum\n"));
64			return B_BAD_DATA;
65		}
66	}
67
68	return B_OK;
69}
70
71
72
73static status_t
74acpi_validate_rsdt(acpi_descriptor_header* rsdt)
75{
76	const char* data = (const char*)rsdt;
77	unsigned char checksum = 0;
78	for (uint32 i = 0; i < rsdt->length; i++)
79		checksum += data[i];
80
81	return checksum == 0 ? B_OK : B_BAD_DATA;
82}
83
84
85static status_t
86acpi_check_rsdt(acpi_rsdp* rsdp)
87{
88	if (acpi_validate_rsdp(rsdp) != B_OK)
89		return B_BAD_DATA;
90
91	bool usingXsdt = false;
92
93	TRACE(("acpi: found rsdp at %p oem id: %.6s, rev %d\n",
94		rsdp, rsdp->oem_id, rsdp->revision));
95	TRACE(("acpi: rsdp points to rsdt at 0x%x\n", rsdp->rsdt_address));
96
97	uint32 length = 0;
98	acpi_descriptor_header* rsdt = NULL;
99	if (rsdp->revision > 0) {
100		length = rsdp->xsdt_length;
101		rsdt = (acpi_descriptor_header*)mmu_map_physical_memory(
102			(uint32)rsdp->xsdt_address, rsdp->xsdt_length, kDefaultPageFlags);
103		if (rsdt != NULL
104			&& strncmp(rsdt->signature, ACPI_XSDT_SIGNATURE, 4) != 0) {
105			mmu_free(rsdt, rsdp->xsdt_length);
106			rsdt = NULL;
107			TRACE(("acpi: invalid extended system description table\n"));
108		} else
109			usingXsdt = true;
110	}
111
112	// if we're ACPI v1 or we fail to map the XSDT for some reason,
113	// attempt to use the RSDT instead.
114	if (rsdt == NULL) {
115		// map and validate the root system description table
116		rsdt = (acpi_descriptor_header*)mmu_map_physical_memory(
117			rsdp->rsdt_address, sizeof(acpi_descriptor_header),
118			kDefaultPageFlags);
119		if (rsdt == NULL) {
120			TRACE(("acpi: couldn't map rsdt header\n"));
121			return B_ERROR;
122		}
123		if (strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) {
124			mmu_free(rsdt, sizeof(acpi_descriptor_header));
125			rsdt = NULL;
126			TRACE(("acpi: invalid root system description table\n"));
127			return B_ERROR;
128		}
129
130		length = rsdt->length;
131		// Map the whole table, not just the header
132		TRACE(("acpi: rsdt length: %u\n", length));
133		mmu_free(rsdt, sizeof(acpi_descriptor_header));
134		rsdt = (acpi_descriptor_header*)mmu_map_physical_memory(
135			rsdp->rsdt_address, length, kDefaultPageFlags);
136	}
137
138	if (rsdt != NULL) {
139		if (acpi_validate_rsdt(rsdt) != B_OK) {
140			TRACE(("acpi: rsdt failed checksum validation\n"));
141			mmu_free(rsdt, length);
142			return B_ERROR;
143		} else {
144			if (usingXsdt)
145				sAcpiXsdt = rsdt;
146			else
147				sAcpiRsdt = rsdt;
148			TRACE(("acpi: found valid %s at %p\n",
149				usingXsdt ? ACPI_XSDT_SIGNATURE : ACPI_RSDT_SIGNATURE,
150				rsdt));
151		}
152	} else
153		return B_ERROR;
154
155	return B_OK;
156}
157
158template<typename PointerType>
159acpi_descriptor_header*
160acpi_find_table_generic(const char* signature, acpi_descriptor_header* acpiSdt)
161{
162	if (acpiSdt == NULL)
163		return NULL;
164
165	if (sNumEntries == -1) {
166		// if using the xsdt, our entries are 64 bits wide.
167		sNumEntries = (acpiSdt->length
168			- sizeof(acpi_descriptor_header))
169				/ sizeof(PointerType);
170	}
171
172	if (sNumEntries <= 0) {
173		TRACE(("acpi: root system description table is empty\n"));
174		return NULL;
175	}
176
177	TRACE(("acpi: searching %d entries for table '%.4s'\n", sNumEntries,
178		signature));
179
180	PointerType* pointer = (PointerType*)((uint8*)acpiSdt
181		+ sizeof(acpi_descriptor_header));
182
183	acpi_descriptor_header* header = NULL;
184	for (int32 j = 0; j < sNumEntries; j++, pointer++) {
185		header = (acpi_descriptor_header*)
186			mmu_map_physical_memory((uint32)*pointer,
187				sizeof(acpi_descriptor_header), kDefaultPageFlags);
188
189		if (header == NULL
190			|| strncmp(header->signature, signature, 4) != 0) {
191			// not interesting for us
192			TRACE(("acpi: Looking for '%.4s'. Skipping '%.4s'\n",
193				signature, header != NULL ? header->signature : "null"));
194
195			if (header != NULL) {
196				mmu_free(header, sizeof(acpi_descriptor_header));
197				header = NULL;
198			}
199
200			continue;
201		}
202
203		TRACE(("acpi: Found '%.4s' @ %p\n", signature, pointer));
204		break;
205	}
206
207
208	if (header == NULL)
209		return NULL;
210
211	// Map the whole table, not just the header
212	uint32 length = header->length;
213	mmu_free(header, sizeof(acpi_descriptor_header));
214
215	return (acpi_descriptor_header*)mmu_map_physical_memory(
216		(uint32)*pointer, length, kDefaultPageFlags);
217}
218
219
220acpi_descriptor_header*
221acpi_find_table(const char* signature)
222{
223	if (sAcpiRsdt != NULL)
224		return acpi_find_table_generic<uint32>(signature, sAcpiRsdt);
225	else if (sAcpiXsdt != NULL)
226		return acpi_find_table_generic<uint64>(signature, sAcpiXsdt);
227
228	return NULL;
229}
230
231
232void
233acpi_init()
234{
235	EFI_GUID acpi = ACPI_20_TABLE_GUID;
236	EFI_CONFIGURATION_TABLE *table = kSystemTable->ConfigurationTable;
237	UINTN entries = kSystemTable->NumberOfTableEntries;
238
239	// Try to find the ACPI RSDP.
240	for (uint32 i = 0; i < entries; i++) {
241		acpi_rsdp *rsdp = NULL;
242
243		EFI_GUID vendor = table[i].VendorGuid;
244
245		if (vendor.Data1 == acpi.Data1
246			&& vendor.Data2 == acpi.Data2
247			&& vendor.Data3 == acpi.Data3
248			&& strncmp((char *)vendor.Data4, (char *)acpi.Data4, 8) == 0) {
249			rsdp = (acpi_rsdp *)(table[i].VendorTable);
250			if (strncmp((char *)rsdp, ACPI_RSDP_SIGNATURE, 8) == 0)
251				TRACE(("acpi_init: found ACPI RSDP signature at %p\n", rsdp));
252
253			if (rsdp != NULL && acpi_check_rsdt(rsdp) == B_OK) {
254				gKernelArgs.arch_args.acpi_root = rsdp;
255				break;
256			}
257		}
258	}
259}
260