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