xref: /haiku/src/system/boot/platform/bios_ia32/acpi.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 
29 static struct scan_spots_struct acpi_scan_spots[] = {
30 	{ 0x0, 0x400, 0x400 - 0x0 },
31 	{ 0xe0000, 0x100000, 0x100000 - 0xe0000 },
32 	{ 0, 0, 0 }
33 };
34 
35 static acpi_descriptor_header* sAcpiRsdt; // System Description Table
36 static acpi_descriptor_header* sAcpiXsdt; // Extended System Description Table
37 static int32 sNumEntries = -1;
38 
39 
40 static status_t
41 acpi_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 
70 static status_t
71 acpi_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 
82 static status_t
83 acpi_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 
151 template<typename PointerType>
152 acpi_descriptor_header*
153 acpi_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 
213 acpi_descriptor_header*
214 acpi_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 
225 void
226 acpi_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