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