xref: /haiku/src/system/boot/platform/efi/acpi.cpp (revision 97f11716bfaa0f385eb0e28a52bf56a5023b9e99)
1 /*
2  * Copyright 2020 Haiku, Inc. All rights reserved.
3  * Copyright 2014, Jessica Hamilton, jessica.l.hamilton@gmail.com.
4  * Copyright 2011, Rene Gollent, rene@gollent.com.
5  * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
6  * Copyright 2007, Michael Lotz, mmlr@mlotz.ch
7  * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de.
8  * Distributed under the terms of the MIT License.
9  *
10  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
11  * Distributed under the terms of the NewOS License.
12 */
13 
14 
15 #include <string.h>
16 
17 #include <KernelExport.h>
18 #include <SupportDefs.h>
19 
20 #include <arch_acpi.h>
21 #include <boot/stage2.h>
22 #include <boot/platform.h>
23 #include <boot/stdio.h>
24 
25 #include "efi_platform.h"
26 #include "acpi.h"
27 #include "mmu.h"
28 
29 
30 #define TRACE_ACPI
31 #ifdef TRACE_ACPI
32 #	define TRACE(x) dprintf x
33 #else
34 #	define TRACE(x) ;
35 #endif
36 
37 
38 static acpi_descriptor_header* sAcpiRsdt; // System Description Table
39 static acpi_descriptor_header* sAcpiXsdt; // Extended System Description Table
40 static int32 sNumEntries = -1;
41 
42 
43 static status_t
acpi_validate_rsdp(acpi_rsdp * rsdp)44 acpi_validate_rsdp(acpi_rsdp* rsdp)
45 {
46 	const char* data = (const char*)rsdp;
47 	unsigned char checksum = 0;
48 	for (uint32 i = 0; i < sizeof(acpi_rsdp_legacy); i++)
49 		checksum += data[i];
50 
51 	if ((checksum & 0xff) != 0) {
52 		TRACE(("acpi: rsdp failed basic checksum\n"));
53 		return B_BAD_DATA;
54 	}
55 
56 	// for ACPI 2.0+ we need to also validate the extended checksum
57 	if (rsdp->revision > 0) {
58 		for (uint32 i = sizeof(acpi_rsdp_legacy);
59 			i < sizeof(acpi_rsdp_extended); i++) {
60 				checksum += data[i];
61 		}
62 
63 		if ((checksum & 0xff) != 0) {
64 			TRACE(("acpi: rsdp failed extended checksum\n"));
65 			return B_BAD_DATA;
66 		}
67 	}
68 
69 	return B_OK;
70 }
71 
72 
73 static status_t
acpi_validate_rsdt(acpi_descriptor_header * rsdt)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
acpi_check_rsdt(acpi_rsdp * rsdp)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%" B_PRIx32 "\n", rsdp->rsdt_address));
96 
97 	acpi_descriptor_header* rsdt = NULL;
98 	if (rsdp->revision > 0) {
99 		rsdt = (acpi_descriptor_header*)(addr_t)rsdp->xsdt_address;
100 		if (rsdt != NULL
101 			&& strncmp(rsdt->signature, ACPI_XSDT_SIGNATURE, 4) != 0) {
102 			rsdt = NULL;
103 			TRACE(("acpi: invalid extended system description table\n"));
104 		} else
105 			usingXsdt = true;
106 	}
107 
108 	// if we're ACPI v1 or we fail to map the XSDT for some reason,
109 	// attempt to use the RSDT instead.
110 	if (rsdt == NULL) {
111 		// validate the root system description table
112 		rsdt = (acpi_descriptor_header*)(addr_t)rsdp->rsdt_address;
113 		if (rsdt == NULL) {
114 			TRACE(("acpi: couldn't map rsdt header\n"));
115 			return B_ERROR;
116 		}
117 		if (strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) {
118 			rsdt = NULL;
119 			TRACE(("acpi: invalid root system description table\n"));
120 			return B_ERROR;
121 		}
122 
123 		TRACE(("acpi: rsdt length: %" B_PRIu32 "\n", rsdt->length));
124 	}
125 
126 	if (rsdt != NULL) {
127 		if (acpi_validate_rsdt(rsdt) != B_OK) {
128 			TRACE(("acpi: rsdt failed checksum validation\n"));
129 			return B_ERROR;
130 		} else {
131 			if (usingXsdt)
132 				sAcpiXsdt = rsdt;
133 			else
134 				sAcpiRsdt = rsdt;
135 			TRACE(("acpi: found valid %s at %p\n",
136 				usingXsdt ? ACPI_XSDT_SIGNATURE : ACPI_RSDT_SIGNATURE,
137 				rsdt));
138 		}
139 	} else
140 		return B_ERROR;
141 
142 	return B_OK;
143 }
144 
145 template<typename PointerType>
146 acpi_descriptor_header*
acpi_find_table_generic(const char * signature,acpi_descriptor_header * acpiSdt)147 acpi_find_table_generic(const char* signature, acpi_descriptor_header* acpiSdt)
148 {
149 	if (acpiSdt == NULL)
150 		return NULL;
151 
152 	if (sNumEntries == -1) {
153 		// if using the xsdt, our entries are 64 bits wide.
154 		sNumEntries = (acpiSdt->length - sizeof(acpi_descriptor_header))
155 			/ sizeof(PointerType);
156 	}
157 
158 	if (sNumEntries <= 0) {
159 		TRACE(("acpi: root system description table is empty\n"));
160 		return NULL;
161 	}
162 
163 	TRACE(("acpi: searching %" B_PRId32 " entries for table '%.4s'\n", sNumEntries,
164 		signature));
165 
166 	PointerType* pointer = (PointerType*)((uint8*)acpiSdt
167 		+ sizeof(acpi_descriptor_header));
168 
169 	acpi_descriptor_header* header = NULL;
170 	for (int32 j = 0; j < sNumEntries; j++, pointer++) {
171 		header = (acpi_descriptor_header*)(addr_t)*pointer;
172 		if (header != NULL && strncmp(header->signature, signature, 4) == 0) {
173 			TRACE(("acpi: Found '%.4s' @ %p\n", signature, pointer));
174 			return header;
175 		}
176 
177 		TRACE(("acpi: Looking for '%.4s'. Skipping '%.4s'\n",
178 			signature, header != NULL ? header->signature : "null"));
179 		header = NULL;
180 		continue;
181 	}
182 
183 	return NULL;
184 }
185 
186 
187 acpi_descriptor_header*
acpi_find_table(const char * signature)188 acpi_find_table(const char* signature)
189 {
190 	if (sAcpiRsdt != NULL)
191 		return acpi_find_table_generic<uint32>(signature, sAcpiRsdt);
192 	else if (sAcpiXsdt != NULL)
193 		return acpi_find_table_generic<uint64>(signature, sAcpiXsdt);
194 
195 	return NULL;
196 }
197 
198 
199 void __attribute__((weak))
arch_handle_acpi()200 arch_handle_acpi()
201 {
202 }
203 
204 
205 void
acpi_init()206 acpi_init()
207 {
208 	efi_guid acpi = ACPI_20_TABLE_GUID;
209 	efi_configuration_table *table = kSystemTable->ConfigurationTable;
210 	size_t entries = kSystemTable->NumberOfTableEntries;
211 
212 	// Try to find the ACPI RSDP.
213 	for (uint32 i = 0; i < entries; i++) {
214 		if (!table[i].VendorGuid.equals(acpi))
215 			continue;
216 
217 		acpi_rsdp *rsdp = (acpi_rsdp *)(table[i].VendorTable);
218 		if (strncmp((char *)rsdp, ACPI_RSDP_SIGNATURE, 8) == 0)
219 			TRACE(("acpi_init: found ACPI RSDP signature at %p\n", rsdp));
220 
221 		if (rsdp != NULL && acpi_check_rsdt(rsdp) == B_OK) {
222 			gKernelArgs.arch_args.acpi_root = rsdp;
223 			arch_handle_acpi();
224 			break;
225 		}
226 	}
227 }
228