xref: /haiku/src/system/boot/platform/bios_ia32/acpi.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 
30 static struct scan_spots_struct acpi_scan_spots[] = {
31 	{ 0x0, 0x400, 0x400 - 0x0 },
32 	{ 0xe0000, 0x100000, 0x100000 - 0xe0000 },
33 	{ 0, 0, 0 }
34 };
35 
36 static acpi_descriptor_header* sAcpiRsdt; // System Description Table
37 static acpi_descriptor_header* sAcpiXsdt; // Extended System Description Table
38 static int32 sNumEntries = -1;
39 
40 
41 static status_t
42 acpi_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 
71 static status_t
72 acpi_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 
83 static status_t
84 acpi_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 
157 template<typename PointerType>
158 acpi_descriptor_header*
159 acpi_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 
219 acpi_descriptor_header*
220 acpi_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 
231 void
232 acpi_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