xref: /dragonfly/sys/platform/pc64/acpica/acpi_sdt.c (revision 20c2db9a)
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: unsupported XSDT revision %d\n",
208 			xsdt->xsdt_hdr.sdth_rev);
209 		goto back;
210 	}
211 
212 	nent = (xsdt->xsdt_hdr.sdth_len - sizeof(xsdt->xsdt_hdr)) /
213 	       sizeof(xsdt->xsdt_ents[0]);
214 	for (i = 0; i < nent; ++i) {
215 		struct acpi_sdth *sdth;
216 
217 		if (xsdt->xsdt_ents[i] == 0)
218 			continue;
219 
220 		sdth = sdt_sdth_map(xsdt->xsdt_ents[i]);
221 		if (sdth != NULL) {
222 			int ret;
223 
224 			ret = memcmp(sdth->sdth_sig, sig, ACPI_SDTH_SIGLEN);
225 			sdt_sdth_unmap(sdth);
226 
227 			if (ret == 0) {
228 				sdt_paddr = xsdt->xsdt_ents[i];
229 				break;
230 			}
231 		}
232 	}
233 back:
234 	sdt_sdth_unmap(&xsdt->xsdt_hdr);
235 	return sdt_paddr;
236 }
237 
238 static vm_paddr_t
239 sdt_search_rsdt(vm_paddr_t rsdt_paddr, const uint8_t *sig)
240 {
241 	struct acpi_rsdt *rsdt;
242 	vm_paddr_t sdt_paddr = 0;
243 	int i, nent;
244 
245 	if (rsdt_paddr == 0) {
246 		kprintf("sdt_search_rsdt: RSDT paddr == 0\n");
247 		return 0;
248 	}
249 
250 	rsdt = sdt_sdth_map(rsdt_paddr);
251 	if (rsdt == NULL) {
252 		kprintf("sdt_search_rsdt: can't map RSDT\n");
253 		return 0;
254 	}
255 
256 	if (memcmp(rsdt->rsdt_hdr.sdth_sig, ACPI_RSDT_SIG,
257 		   ACPI_SDTH_SIGLEN) != 0) {
258 		kprintf("sdt_search_rsdt: not RSDT\n");
259 		goto back;
260 	}
261 
262 	if (rsdt->rsdt_hdr.sdth_rev != 1) {
263 		kprintf("sdt_search_rsdt: unsupported RSDT revision %d\n",
264 			rsdt->rsdt_hdr.sdth_rev);
265 		goto back;
266 	}
267 
268 	nent = (rsdt->rsdt_hdr.sdth_len - sizeof(rsdt->rsdt_hdr)) /
269 	       sizeof(rsdt->rsdt_ents[0]);
270 	for (i = 0; i < nent; ++i) {
271 		struct acpi_sdth *sdth;
272 
273 		if (rsdt->rsdt_ents[i] == 0)
274 			continue;
275 
276 		sdth = sdt_sdth_map(rsdt->rsdt_ents[i]);
277 		if (sdth != NULL) {
278 			int ret;
279 
280 			ret = memcmp(sdth->sdth_sig, sig, ACPI_SDTH_SIGLEN);
281 			sdt_sdth_unmap(sdth);
282 
283 			if (ret == 0) {
284 				sdt_paddr = rsdt->rsdt_ents[i];
285 				break;
286 			}
287 		}
288 	}
289 back:
290 	sdt_sdth_unmap(&rsdt->rsdt_hdr);
291 	return sdt_paddr;
292 }
293 
294 vm_paddr_t
295 sdt_search(const uint8_t *sig)
296 {
297 	if (sdt_search_func == NULL)
298 		return 0;
299 	return sdt_search_func(sdt_search_paddr, sig);
300 }
301