xref: /haiku/src/system/boot/platform/bios_ia32/acpi.cpp (revision 8c78892580f132d10e624aef96f835df8d94bf19)
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 #define ERROR(x...) dprintf(x)
30 
31 static struct scan_spots_struct acpi_scan_spots[] = {
32 	{ 0x0, 0x400, 0x400 - 0x0 },
33 	{ 0xe0000, 0x100000, 0x100000 - 0xe0000 },
34 	{ 0, 0, 0 }
35 };
36 
37 static acpi_descriptor_header* sAcpiRsdt; // System Description Table
38 static acpi_descriptor_header* sAcpiXsdt; // Extended System Description Table
39 static int32 sNumEntries = -1;
40 
41 
42 static status_t
43 acpi_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 static status_t
73 acpi_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 
84 static status_t
85 acpi_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 	if (rsdp->revision > 0) {
99 		length = rsdp->xsdt_length;
100 		rsdt = (acpi_descriptor_header*)mmu_map_physical_memory(
101 			(uint32)rsdp->xsdt_address, rsdp->xsdt_length, kDefaultPageFlags);
102 		if (rsdt != NULL
103 			&& strncmp(rsdt->signature, ACPI_XSDT_SIGNATURE, 4) != 0) {
104 			mmu_free(rsdt, rsdp->xsdt_length);
105 			rsdt = NULL;
106 			TRACE(("acpi: invalid extended system description table\n"));
107 		} else
108 			usingXsdt = true;
109 	}
110 
111 	// if we're ACPI v1 or we fail to map the XSDT for some reason,
112 	// attempt to use the RSDT instead.
113 	if (rsdt == NULL) {
114 		// map and validate the root system description table
115 		rsdt = (acpi_descriptor_header*)mmu_map_physical_memory(
116 			rsdp->rsdt_address, sizeof(acpi_descriptor_header),
117 			kDefaultPageFlags);
118 		if (rsdt == NULL) {
119 			TRACE(("acpi: couldn't map rsdt header\n"));
120 			return B_ERROR;
121 		}
122 		if (strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) {
123 			mmu_free(rsdt, sizeof(acpi_descriptor_header));
124 			rsdt = NULL;
125 			TRACE(("acpi: invalid root system description table\n"));
126 			return B_ERROR;
127 		}
128 
129 		length = rsdt->length;
130 		// Map the whole table, not just the header
131 		TRACE(("acpi: rsdt length: %lu\n", length));
132 		mmu_free(rsdt, sizeof(acpi_descriptor_header));
133 		rsdt = (acpi_descriptor_header*)mmu_map_physical_memory(
134 			rsdp->rsdt_address, length, kDefaultPageFlags);
135 	}
136 
137 	if (rsdt != NULL) {
138 		if (acpi_validate_rsdt(rsdt) != B_OK) {
139 			ERROR("acpi: %.4s failed checksum validation\n", rsdt->signature);
140 		}
141 		if (usingXsdt)
142 			sAcpiXsdt = rsdt;
143 		else
144 			sAcpiRsdt = rsdt;
145 		TRACE(("acpi: found %.4s at %p\n", rsdt->signature, rsdt));
146 	} else
147 		return B_ERROR;
148 
149 	return B_OK;
150 }
151 
152 
153 template<typename PointerType>
154 acpi_descriptor_header*
155 acpi_find_table_generic(const char* signature, acpi_descriptor_header* acpiSdt)
156 {
157 	if (acpiSdt == NULL)
158 		return NULL;
159 
160 	if (sNumEntries == -1) {
161 		// if using the xsdt, our entries are 64 bits wide.
162 		sNumEntries = (acpiSdt->length
163 			- sizeof(acpi_descriptor_header))
164 				/ sizeof(PointerType);
165 	}
166 
167 	if (sNumEntries <= 0) {
168 		TRACE(("acpi: root system description table is empty\n"));
169 		return NULL;
170 	}
171 
172 	TRACE(("acpi: searching %ld entries for table '%.4s'\n", sNumEntries,
173 		signature));
174 
175 	PointerType* pointer = (PointerType*)((uint8*)acpiSdt
176 		+ sizeof(acpi_descriptor_header));
177 
178 	acpi_descriptor_header* header = NULL;
179 	for (int32 j = 0; j < sNumEntries; j++, pointer++) {
180 		header = (acpi_descriptor_header*)
181 			mmu_map_physical_memory((uint32)*pointer,
182 				sizeof(acpi_descriptor_header), kDefaultPageFlags);
183 
184 		if (header == NULL
185 			|| strncmp(header->signature, signature, 4) != 0) {
186 			// not interesting for us
187 			TRACE(("acpi: Looking for '%.4s'. Skipping '%.4s'\n",
188 				signature, header != NULL ? header->signature : "null"));
189 
190 			if (header != NULL) {
191 				mmu_free(header, sizeof(acpi_descriptor_header));
192 				header = NULL;
193 			}
194 
195 			continue;
196 		}
197 
198 		TRACE(("acpi: Found '%.4s' @ %p\n", signature, pointer));
199 		break;
200 	}
201 
202 
203 	if (header == NULL)
204 		return NULL;
205 
206 	// Map the whole table, not just the header
207 	uint32 length = header->length;
208 	mmu_free(header, sizeof(acpi_descriptor_header));
209 
210 	return (acpi_descriptor_header*)mmu_map_physical_memory(
211 		(uint32)*pointer, length, kDefaultPageFlags);
212 }
213 
214 
215 acpi_descriptor_header*
216 acpi_find_table(const char* signature)
217 {
218 	if (sAcpiRsdt != NULL)
219 		return acpi_find_table_generic<uint32>(signature, sAcpiRsdt);
220 	else if (sAcpiXsdt != NULL)
221 		return acpi_find_table_generic<uint64>(signature, sAcpiXsdt);
222 
223 	return NULL;
224 }
225 
226 
227 void
228 acpi_init()
229 {
230 	// Try to find the ACPI RSDP.
231 	for (int32 i = 0; acpi_scan_spots[i].length > 0; i++) {
232 		acpi_rsdp* rsdp = NULL;
233 
234 		TRACE(("acpi_init: entry base 0x%lx, limit 0x%lx\n",
235 			acpi_scan_spots[i].start, acpi_scan_spots[i].stop));
236 
237 		for (char* pointer = (char*)acpi_scan_spots[i].start;
238 		     (uint32)pointer < acpi_scan_spots[i].stop; pointer += 16) {
239 			if (strncmp(pointer, ACPI_RSDP_SIGNATURE, 8) == 0) {
240 				TRACE(("acpi_init: found ACPI RSDP signature at %p\n",
241 					pointer));
242 				rsdp = (acpi_rsdp*)pointer;
243 			}
244 		}
245 
246 		if (rsdp != NULL && acpi_check_rsdt(rsdp) == B_OK)
247 			break;
248 	}
249 }
250