xref: /dragonfly/sys/platform/pc64/acpica/acpi_sdt.c (revision dadd6466)
1 /*
2  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Sepherosa Ziehau <sepherosa@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/systm.h>
38 
39 #include <machine/pmap.h>
40 
41 #include "acpi_sdt.h"
42 #include "acpi_sdt_var.h"
43 
44 #define SDT_VPRINTF(fmt, arg...) \
45 do { \
46 	if (bootverbose) \
47 		kprintf("ACPI SDT: " fmt , ##arg); \
48 } while (0)
49 
50 #define ACPI_RSDP_EBDA_MAPSZ	1024
51 #define ACPI_RSDP_BIOS_MAPSZ	0x20000
52 #define ACPI_RSDP_BIOS_MAPADDR	0xe0000
53 
54 #define ACPI_RSDP_ALIGN		16
55 
56 #define ACPI_RSDP_SIGLEN	8
57 #define ACPI_RSDP_SIG		"RSD PTR "
58 
59 /* Root System Description Pointer */
60 struct acpi_rsdp {
61 	uint8_t			rsdp_sig[ACPI_RSDP_SIGLEN];
62 	uint8_t			rsdp_cksum;
63 	uint8_t			rsdp_oem_id[6];
64 	uint8_t			rsdp_rev;
65 	uint32_t		rsdp_rsdt;
66 	uint32_t		rsdp_len;
67 	uint64_t		rsdp_xsdt;
68 	uint8_t			rsdp_ext_cksum;
69 	uint8_t			rsdp_rsvd[3];
70 } __packed;
71 
72 /* Extended System Description Table */
73 struct acpi_xsdt {
74 	struct acpi_sdth	xsdt_hdr;
75 	uint64_t		xsdt_ents[1];
76 } __packed;
77 
78 /* Root System Description Table */
79 struct acpi_rsdt {
80 	struct acpi_sdth	rsdt_hdr;
81 	uint32_t		rsdt_ents[1];
82 } __packed;
83 
84 typedef	vm_paddr_t		(*sdt_search_t)(vm_paddr_t, const uint8_t *);
85 
86 static const struct acpi_rsdp	*sdt_rsdp_search(const uint8_t *, int);
87 static vm_paddr_t		sdt_search_xsdt(vm_paddr_t, const uint8_t *);
88 static vm_paddr_t		sdt_search_rsdt(vm_paddr_t, const uint8_t *);
89 
90 extern u_long			ebda_addr;
91 
92 static sdt_search_t		sdt_search_func;
93 static vm_paddr_t		sdt_search_paddr;
94 
95 static void
96 sdt_probe(void)
97 {
98 	const struct acpi_rsdp *rsdp;
99 	vm_size_t mapsz;
100 	uint8_t *ptr;
101 
102 	if (ebda_addr != 0) {
103 		mapsz = ACPI_RSDP_EBDA_MAPSZ;
104 		ptr = pmap_mapdev(ebda_addr, mapsz);
105 
106 		rsdp = sdt_rsdp_search(ptr, mapsz);
107 		if (rsdp == NULL) {
108 			SDT_VPRINTF("RSDP not in EBDA\n");
109 			pmap_unmapdev((vm_offset_t)ptr, mapsz);
110 
111 			ptr = NULL;
112 			mapsz = 0;
113 		} else {
114 			SDT_VPRINTF("RSDP in EBDA\n");
115 			goto found_rsdp;
116 		}
117 	}
118 
119 	mapsz = ACPI_RSDP_BIOS_MAPSZ;
120 	ptr = pmap_mapdev(ACPI_RSDP_BIOS_MAPADDR, mapsz);
121 
122 	rsdp = sdt_rsdp_search(ptr, mapsz);
123 	if (rsdp == NULL) {
124 		kprintf("sdt_probe: no RSDP\n");
125 		pmap_unmapdev((vm_offset_t)ptr, mapsz);
126 		return;
127 	} else {
128 		SDT_VPRINTF("RSDP in BIOS mem\n");
129 	}
130 
131 found_rsdp:
132 	if (rsdp->rsdp_rev != 2) {
133 		sdt_search_func = sdt_search_rsdt;
134 		sdt_search_paddr = rsdp->rsdp_rsdt;
135 	} else {
136 		sdt_search_func = sdt_search_xsdt;
137 		sdt_search_paddr = rsdp->rsdp_xsdt;
138 	}
139 	pmap_unmapdev((vm_offset_t)ptr, mapsz);
140 }
141 SYSINIT(sdt_probe, SI_BOOT2_PRESMP, SI_ORDER_FIRST, sdt_probe, 0);
142 
143 static const struct acpi_rsdp *
144 sdt_rsdp_search(const uint8_t *target, int size)
145 {
146 	const struct acpi_rsdp *rsdp;
147 	int i;
148 
149 	KKASSERT(size > sizeof(*rsdp));
150 
151 	for (i = 0; i < size - sizeof(*rsdp); i += ACPI_RSDP_ALIGN) {
152 		rsdp = (const struct acpi_rsdp *)&target[i];
153 		if (memcmp(rsdp->rsdp_sig, ACPI_RSDP_SIG,
154 			   ACPI_RSDP_SIGLEN) == 0)
155 			return rsdp;
156 	}
157 	return NULL;
158 }
159 
160 void *
161 sdt_sdth_map(vm_paddr_t paddr)
162 {
163 	struct acpi_sdth *sdth;
164 	vm_size_t mapsz;
165 
166 	sdth = pmap_mapdev(paddr, sizeof(*sdth));
167 	mapsz = sdth->sdth_len;
168 	pmap_unmapdev((vm_offset_t)sdth, sizeof(*sdth));
169 
170 	if (mapsz < sizeof(*sdth))
171 		return NULL;
172 
173 	return pmap_mapdev(paddr, mapsz);
174 }
175 
176 void
177 sdt_sdth_unmap(struct acpi_sdth *sdth)
178 {
179 	pmap_unmapdev((vm_offset_t)sdth, sdth->sdth_len);
180 }
181 
182 static vm_paddr_t
183 sdt_search_xsdt(vm_paddr_t xsdt_paddr, const uint8_t *sig)
184 {
185 	struct acpi_xsdt *xsdt;
186 	vm_paddr_t sdt_paddr = 0;
187 	int i, nent;
188 
189 	if (xsdt_paddr == 0) {
190 		kprintf("sdt_search_xsdt: XSDT paddr == 0\n");
191 		return 0;
192 	}
193 
194 	xsdt = sdt_sdth_map(xsdt_paddr);
195 	if (xsdt == NULL) {
196 		kprintf("sdt_search_xsdt: can't map XSDT\n");
197 		return 0;
198 	}
199 
200 	if (memcmp(xsdt->xsdt_hdr.sdth_sig, ACPI_XSDT_SIG,
201 		   ACPI_SDTH_SIGLEN) != 0) {
202 		kprintf("sdt_search_xsdt: not XSDT\n");
203 		goto back;
204 	}
205 
206 	if (xsdt->xsdt_hdr.sdth_rev != 1) {
207 		kprintf("sdt_search_xsdt: unknown XSDT revision %d\n",
208 			xsdt->xsdt_hdr.sdth_rev);
209 	}
210 
211 	if (xsdt->xsdt_hdr.sdth_len < sizeof(xsdt->xsdt_hdr)) {
212 		kprintf("sdt_search_xsdt: invalid XSDT length %u\n",
213 			xsdt->xsdt_hdr.sdth_len);
214 		goto back;
215 	}
216 
217 	nent = (xsdt->xsdt_hdr.sdth_len - sizeof(xsdt->xsdt_hdr)) /
218 	       sizeof(xsdt->xsdt_ents[0]);
219 	for (i = 0; i < nent; ++i) {
220 		struct acpi_sdth *sdth;
221 
222 		if (xsdt->xsdt_ents[i] == 0)
223 			continue;
224 
225 		sdth = sdt_sdth_map(xsdt->xsdt_ents[i]);
226 		if (sdth != NULL) {
227 			int ret;
228 
229 			ret = memcmp(sdth->sdth_sig, sig, ACPI_SDTH_SIGLEN);
230 			sdt_sdth_unmap(sdth);
231 
232 			if (ret == 0) {
233 				sdt_paddr = xsdt->xsdt_ents[i];
234 				break;
235 			}
236 		}
237 	}
238 back:
239 	sdt_sdth_unmap(&xsdt->xsdt_hdr);
240 	return sdt_paddr;
241 }
242 
243 static vm_paddr_t
244 sdt_search_rsdt(vm_paddr_t rsdt_paddr, const uint8_t *sig)
245 {
246 	struct acpi_rsdt *rsdt;
247 	vm_paddr_t sdt_paddr = 0;
248 	int i, nent;
249 
250 	if (rsdt_paddr == 0) {
251 		kprintf("sdt_search_rsdt: RSDT paddr == 0\n");
252 		return 0;
253 	}
254 
255 	rsdt = sdt_sdth_map(rsdt_paddr);
256 	if (rsdt == NULL) {
257 		kprintf("sdt_search_rsdt: can't map RSDT\n");
258 		return 0;
259 	}
260 
261 	if (memcmp(rsdt->rsdt_hdr.sdth_sig, ACPI_RSDT_SIG,
262 		   ACPI_SDTH_SIGLEN) != 0) {
263 		kprintf("sdt_search_rsdt: not RSDT\n");
264 		goto back;
265 	}
266 
267 	if (rsdt->rsdt_hdr.sdth_rev != 1) {
268 		kprintf("sdt_search_rsdt: unknown RSDT revision %d\n",
269 			rsdt->rsdt_hdr.sdth_rev);
270 	}
271 
272 	if (rsdt->rsdt_hdr.sdth_len < sizeof(rsdt->rsdt_hdr)) {
273 		kprintf("sdt_search_rsdt: invalid RSDT length %u\n",
274 			rsdt->rsdt_hdr.sdth_len);
275 		goto back;
276 	}
277 
278 	nent = (rsdt->rsdt_hdr.sdth_len - sizeof(rsdt->rsdt_hdr)) /
279 	       sizeof(rsdt->rsdt_ents[0]);
280 	for (i = 0; i < nent; ++i) {
281 		struct acpi_sdth *sdth;
282 
283 		if (rsdt->rsdt_ents[i] == 0)
284 			continue;
285 
286 		sdth = sdt_sdth_map(rsdt->rsdt_ents[i]);
287 		if (sdth != NULL) {
288 			int ret;
289 
290 			ret = memcmp(sdth->sdth_sig, sig, ACPI_SDTH_SIGLEN);
291 			sdt_sdth_unmap(sdth);
292 
293 			if (ret == 0) {
294 				sdt_paddr = rsdt->rsdt_ents[i];
295 				break;
296 			}
297 		}
298 	}
299 back:
300 	sdt_sdth_unmap(&rsdt->rsdt_hdr);
301 	return sdt_paddr;
302 }
303 
304 vm_paddr_t
305 sdt_search(const uint8_t *sig)
306 {
307 	if (sdt_search_func == NULL)
308 		return 0;
309 	return sdt_search_func(sdt_search_paddr, sig);
310 }
311