xref: /haiku/src/system/boot/platform/efi/acpi.cpp (revision e81a954787e50e56a7f06f72705b7859b6ab06d1)
1 /*
2  * Copyright 2014, Jessica Hamilton, jessica.l.hamilton@gmail.com.
3  * Copyright 2011, Rene Gollent, rene@gollent.com.
4  * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
5  * Copyright 2007, Michael Lotz, mmlr@mlotz.ch
6  * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de.
7  * Distributed under the terms of the MIT License.
8  *
9  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
10  * Distributed under the terms of the NewOS License.
11 */
12 
13 
14 #include <string.h>
15 
16 #include <KernelExport.h>
17 #include <SupportDefs.h>
18 
19 #include <arch/x86/arch_acpi.h>
20 #include <boot/stage2.h>
21 #include <boot/platform.h>
22 #include <boot/stdio.h>
23 
24 #include "efi_platform.h"
25 #include "acpi.h"
26 #include "mmu.h"
27 
28 
29 #define TRACE_ACPI
30 #ifdef TRACE_ACPI
31 #	define TRACE(x) dprintf x
32 #else
33 #	define TRACE(x) ;
34 #endif
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 
73 static status_t
74 acpi_validate_rsdt(acpi_descriptor_header* rsdt)
75 {
76 	const char* data = (const char*)rsdt;
77 	unsigned char checksum = 0;
78 	for (uint32 i = 0; i < rsdt->length; i++)
79 		checksum += data[i];
80 
81 	return checksum == 0 ? B_OK : B_BAD_DATA;
82 }
83 
84 
85 static status_t
86 acpi_check_rsdt(acpi_rsdp* rsdp)
87 {
88 	if (acpi_validate_rsdp(rsdp) != B_OK)
89 		return B_BAD_DATA;
90 
91 	bool usingXsdt = false;
92 
93 	TRACE(("acpi: found rsdp at %p oem id: %.6s, rev %d\n",
94 		rsdp, rsdp->oem_id, rsdp->revision));
95 	TRACE(("acpi: rsdp points to rsdt at 0x%x\n", rsdp->rsdt_address));
96 
97 	uint32 length = 0;
98 	acpi_descriptor_header* rsdt = NULL;
99 	if (rsdp->revision > 0) {
100 		length = rsdp->xsdt_length;
101 		rsdt = (acpi_descriptor_header*)mmu_map_physical_memory(
102 			(uint32)rsdp->xsdt_address, rsdp->xsdt_length, kDefaultPageFlags);
103 		if (rsdt != NULL
104 			&& strncmp(rsdt->signature, ACPI_XSDT_SIGNATURE, 4) != 0) {
105 			mmu_free(rsdt, rsdp->xsdt_length);
106 			rsdt = NULL;
107 			TRACE(("acpi: invalid extended system description table\n"));
108 		} else
109 			usingXsdt = true;
110 	}
111 
112 	// if we're ACPI v1 or we fail to map the XSDT for some reason,
113 	// attempt to use the RSDT instead.
114 	if (rsdt == NULL) {
115 		// map and validate the root system description table
116 		rsdt = (acpi_descriptor_header*)mmu_map_physical_memory(
117 			rsdp->rsdt_address, sizeof(acpi_descriptor_header),
118 			kDefaultPageFlags);
119 		if (rsdt == NULL) {
120 			TRACE(("acpi: couldn't map rsdt header\n"));
121 			return B_ERROR;
122 		}
123 		if (strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) {
124 			mmu_free(rsdt, sizeof(acpi_descriptor_header));
125 			rsdt = NULL;
126 			TRACE(("acpi: invalid root system description table\n"));
127 			return B_ERROR;
128 		}
129 
130 		length = rsdt->length;
131 		// Map the whole table, not just the header
132 		TRACE(("acpi: rsdt length: %u\n", length));
133 		mmu_free(rsdt, sizeof(acpi_descriptor_header));
134 		rsdt = (acpi_descriptor_header*)mmu_map_physical_memory(
135 			rsdp->rsdt_address, length, kDefaultPageFlags);
136 	}
137 
138 	if (rsdt != NULL) {
139 		if (acpi_validate_rsdt(rsdt) != B_OK) {
140 			TRACE(("acpi: rsdt failed checksum validation\n"));
141 			mmu_free(rsdt, length);
142 			return B_ERROR;
143 		} else {
144 			if (usingXsdt)
145 				sAcpiXsdt = rsdt;
146 			else
147 				sAcpiRsdt = rsdt;
148 			TRACE(("acpi: found valid %s at %p\n",
149 				usingXsdt ? ACPI_XSDT_SIGNATURE : ACPI_RSDT_SIGNATURE,
150 				rsdt));
151 		}
152 	} else
153 		return B_ERROR;
154 
155 	return B_OK;
156 }
157 
158 template<typename PointerType>
159 acpi_descriptor_header*
160 acpi_find_table_generic(const char* signature, acpi_descriptor_header* acpiSdt)
161 {
162 	if (acpiSdt == NULL)
163 		return NULL;
164 
165 	if (sNumEntries == -1) {
166 		// if using the xsdt, our entries are 64 bits wide.
167 		sNumEntries = (acpiSdt->length
168 			- sizeof(acpi_descriptor_header))
169 				/ sizeof(PointerType);
170 	}
171 
172 	if (sNumEntries <= 0) {
173 		TRACE(("acpi: root system description table is empty\n"));
174 		return NULL;
175 	}
176 
177 	TRACE(("acpi: searching %d entries for table '%.4s'\n", sNumEntries,
178 		signature));
179 
180 	PointerType* pointer = (PointerType*)((uint8*)acpiSdt
181 		+ sizeof(acpi_descriptor_header));
182 
183 	acpi_descriptor_header* header = NULL;
184 	for (int32 j = 0; j < sNumEntries; j++, pointer++) {
185 		header = (acpi_descriptor_header*)
186 			mmu_map_physical_memory((uint32)*pointer,
187 				sizeof(acpi_descriptor_header), kDefaultPageFlags);
188 
189 		if (header == NULL
190 			|| strncmp(header->signature, signature, 4) != 0) {
191 			// not interesting for us
192 			TRACE(("acpi: Looking for '%.4s'. Skipping '%.4s'\n",
193 				signature, header != NULL ? header->signature : "null"));
194 
195 			if (header != NULL) {
196 				mmu_free(header, sizeof(acpi_descriptor_header));
197 				header = NULL;
198 			}
199 
200 			continue;
201 		}
202 
203 		TRACE(("acpi: Found '%.4s' @ %p\n", signature, pointer));
204 		break;
205 	}
206 
207 
208 	if (header == NULL)
209 		return NULL;
210 
211 	// Map the whole table, not just the header
212 	uint32 length = header->length;
213 	mmu_free(header, sizeof(acpi_descriptor_header));
214 
215 	return (acpi_descriptor_header*)mmu_map_physical_memory(
216 		(uint32)*pointer, length, kDefaultPageFlags);
217 }
218 
219 
220 acpi_descriptor_header*
221 acpi_find_table(const char* signature)
222 {
223 	if (sAcpiRsdt != NULL)
224 		return acpi_find_table_generic<uint32>(signature, sAcpiRsdt);
225 	else if (sAcpiXsdt != NULL)
226 		return acpi_find_table_generic<uint64>(signature, sAcpiXsdt);
227 
228 	return NULL;
229 }
230 
231 
232 void
233 acpi_init()
234 {
235 	EFI_GUID acpi = ACPI_20_TABLE_GUID;
236 	EFI_CONFIGURATION_TABLE *table = kSystemTable->ConfigurationTable;
237 	UINTN entries = kSystemTable->NumberOfTableEntries;
238 
239 	// Try to find the ACPI RSDP.
240 	for (uint32 i = 0; i < entries; i++) {
241 		acpi_rsdp *rsdp = NULL;
242 
243 		EFI_GUID vendor = table[i].VendorGuid;
244 
245 		if (vendor.Data1 == acpi.Data1
246 			&& vendor.Data2 == acpi.Data2
247 			&& vendor.Data3 == acpi.Data3
248 			&& strncmp((char *)vendor.Data4, (char *)acpi.Data4, 8) == 0) {
249 			rsdp = (acpi_rsdp *)(table[i].VendorTable);
250 			if (strncmp((char *)rsdp, ACPI_RSDP_SIGNATURE, 8) == 0)
251 				TRACE(("acpi_init: found ACPI RSDP signature at %p\n", rsdp));
252 
253 			if (rsdp != NULL && acpi_check_rsdt(rsdp) == B_OK) {
254 				gKernelArgs.arch_args.acpi_root = rsdp;
255 				break;
256 			}
257 		}
258 	}
259 }
260