1/*
2 * Copyright 2011, Rene Gollent, rene@gollent.com.
3 * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
4 * Copyright 2007, Michael Lotz, mmlr@mlotz.ch
5 * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de.
6 * Distributed under the terms of the MIT License.
7 *
8 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
9 * Distributed under the terms of the NewOS License.
10*/
11
12
13#include "pci_acpi.h"
14
15#include <string.h>
16
17#include <KernelExport.h>
18
19#include <arch/x86/arch_acpi.h>
20
21
22//#define TRACE_ACPI
23#ifdef TRACE_ACPI
24#	define TRACE(x) dprintf x
25#else
26#	define TRACE(x) ;
27#endif
28
29static struct scan_spots_struct acpi_scan_spots[] = {
30	{ 0x0, 0x1000, 0x1000 },
31	{ 0x9f000, 0x10000, 0x1000 },
32	{ 0xe0000, 0x110000, 0x20000 },
33	{ 0xfd000, 0xfe000, 0x1000},
34	{ 0, 0, 0 }
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
72static status_t
73acpi_validate_rsdt(acpi_descriptor_header* rsdt)
74{
75	const char* data = (const char*)rsdt;
76	unsigned char checksum = 0;
77	for (uint32 i = 0; i < rsdt->length; i++)
78		checksum += data[i];
79
80	return checksum == 0 ? B_OK : B_BAD_DATA;
81}
82
83
84static status_t
85acpi_check_rsdt(acpi_rsdp* rsdp)
86{
87	if (acpi_validate_rsdp(rsdp) != B_OK)
88		return B_BAD_DATA;
89
90	bool usingXsdt = false;
91
92	TRACE(("acpi: found rsdp at %p oem id: %.6s, rev %d\n",
93		rsdp, rsdp->oem_id, rsdp->revision));
94	TRACE(("acpi: rsdp points to rsdt at 0x%lx\n", rsdp->rsdt_address));
95
96	uint32 length = 0;
97	acpi_descriptor_header* rsdt = NULL;
98	area_id rsdtArea = -1;
99	if (rsdp->revision > 0) {
100		length = rsdp->xsdt_length;
101		rsdtArea = map_physical_memory("rsdt acpi",
102			(uint32)rsdp->xsdt_address, rsdp->xsdt_length, B_ANY_KERNEL_ADDRESS,
103			B_KERNEL_READ_AREA, (void **)&rsdt);
104		if (rsdt != NULL
105			&& strncmp(rsdt->signature, ACPI_XSDT_SIGNATURE, 4) != 0) {
106			delete_area(rsdtArea);
107			rsdt = NULL;
108			TRACE(("acpi: invalid extended system description table\n"));
109		} else
110			usingXsdt = true;
111	}
112
113	// if we're ACPI v1 or we fail to map the XSDT for some reason,
114	// attempt to use the RSDT instead.
115	if (rsdt == NULL) {
116		// map and validate the root system description table
117		rsdtArea = map_physical_memory("rsdt acpi",
118			rsdp->rsdt_address, sizeof(acpi_descriptor_header),
119			B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA, (void **)&rsdt);
120		if (rsdt == NULL) {
121			TRACE(("acpi: couldn't map rsdt header\n"));
122			return B_ERROR;
123		}
124		if (strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) {
125			delete_area(rsdtArea);
126			rsdt = NULL;
127			TRACE(("acpi: invalid root system description table\n"));
128			return B_ERROR;
129		}
130
131		length = rsdt->length;
132		// Map the whole table, not just the header
133		TRACE(("acpi: rsdt length: %lu\n", length));
134		delete_area(rsdtArea);
135		rsdtArea = map_physical_memory("rsdt acpi",
136			rsdp->rsdt_address, length, B_ANY_KERNEL_ADDRESS,
137			B_KERNEL_READ_AREA, (void **)&rsdt);
138	}
139
140	if (rsdt != NULL) {
141		if (acpi_validate_rsdt(rsdt) != B_OK) {
142			TRACE(("acpi: rsdt failed checksum validation\n"));
143			delete_area(rsdtArea);
144			return B_ERROR;
145		} else {
146			if (usingXsdt)
147				sAcpiXsdt = rsdt;
148			else
149				sAcpiRsdt = rsdt;
150			TRACE(("acpi: found valid %s at %p\n",
151				usingXsdt ? ACPI_XSDT_SIGNATURE : ACPI_RSDT_SIGNATURE,
152				rsdt));
153		}
154	} else
155		return B_ERROR;
156
157	return B_OK;
158}
159
160
161template<typename PointerType>
162acpi_descriptor_header*
163acpi_find_table_generic(const char* signature, acpi_descriptor_header* acpiSdt)
164{
165	if (acpiSdt == NULL)
166		return NULL;
167
168	if (sNumEntries == -1) {
169		// if using the xsdt, our entries are 64 bits wide.
170		sNumEntries = (acpiSdt->length
171			- sizeof(acpi_descriptor_header))
172				/ sizeof(PointerType);
173	}
174
175	if (sNumEntries <= 0) {
176		TRACE(("acpi: root system description table is empty\n"));
177		return NULL;
178	}
179
180	TRACE(("acpi: searching %ld entries for table '%.4s'\n", sNumEntries,
181		signature));
182
183	PointerType* pointer = (PointerType*)((uint8*)acpiSdt
184		+ sizeof(acpi_descriptor_header));
185
186	acpi_descriptor_header* header = NULL;
187	area_id headerArea = -1;
188	for (int32 j = 0; j < sNumEntries; j++, pointer++) {
189		headerArea = map_physical_memory("acpi header", (uint32)*pointer,
190				sizeof(acpi_descriptor_header), B_ANY_KERNEL_ADDRESS,
191			B_KERNEL_READ_AREA, (void **)&header);
192
193		if (header == NULL
194			|| strncmp(header->signature, signature, 4) != 0) {
195			// not interesting for us
196			TRACE(("acpi: Looking for '%.4s'. Skipping '%.4s'\n",
197				signature, header != NULL ? header->signature : "null"));
198
199			if (header != NULL) {
200				delete_area(headerArea);
201				header = NULL;
202			}
203
204			continue;
205		}
206
207		TRACE(("acpi: Found '%.4s' @ %p\n", signature, pointer));
208		break;
209	}
210
211
212	if (header == NULL)
213		return NULL;
214
215	// Map the whole table, not just the header
216	uint32 length = header->length;
217	delete_area(headerArea);
218
219	headerArea = map_physical_memory("acpi table",
220		(uint32)*pointer, length, B_ANY_KERNEL_ADDRESS,
221			B_KERNEL_READ_AREA, (void **)&header);
222	return header;
223}
224
225
226void*
227acpi_find_table(const char* signature)
228{
229	if (sAcpiRsdt != NULL)
230		return acpi_find_table_generic<uint32>(signature, sAcpiRsdt);
231	else if (sAcpiXsdt != NULL)
232		return acpi_find_table_generic<uint64>(signature, sAcpiXsdt);
233
234	return NULL;
235}
236
237
238void
239acpi_init()
240{
241	// Try to find the ACPI RSDP.
242	for (int32 i = 0; acpi_scan_spots[i].length > 0; i++) {
243		acpi_rsdp* rsdp = NULL;
244
245		TRACE(("acpi_init: entry base 0x%lx, limit 0x%lx\n",
246			acpi_scan_spots[i].start, acpi_scan_spots[i].stop));
247
248		char* start = NULL;
249		area_id rsdpArea = map_physical_memory("acpi rsdp",
250			acpi_scan_spots[i].start, acpi_scan_spots[i].length,
251			B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA, (void **)&start);
252		if (rsdpArea < B_OK) {
253			TRACE(("acpi_init: couldn't map %s\n", strerror(rsdpArea)));
254			break;
255		}
256		for (char *pointer = start;
257			(addr_t)pointer < (addr_t)start + acpi_scan_spots[i].length;
258			pointer += 16) {
259			if (strncmp(pointer, ACPI_RSDP_SIGNATURE, 8) == 0) {
260				TRACE(("acpi_init: found ACPI RSDP signature at %p\n",
261					pointer));
262				rsdp = (acpi_rsdp*)pointer;
263			}
264		}
265
266		if (rsdp != NULL && acpi_check_rsdt(rsdp) == B_OK) {
267			delete_area(rsdpArea);
268			break;
269		}
270		delete_area(rsdpArea);
271	}
272
273}
274