xref: /freebsd/tools/tools/ncpus/acpi.c (revision c697fb7f)
1 /*-
2  * Copyright (c) 1998 Doug Rabson
3  * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  *	$FreeBSD$
28  */
29 
30 #include <sys/param.h>
31 #include <sys/endian.h>
32 #include <sys/mman.h>
33 #include <sys/queue.h>
34 #include <sys/stat.h>
35 #include <sys/sysctl.h>
36 #include <sys/wait.h>
37 #include <assert.h>
38 #include <err.h>
39 #include <fcntl.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <paths.h>
45 #include <devinfo.h>
46 
47 #include "acpidump.h"
48 
49 static void	acpi_handle_apic(struct ACPIsdt *sdp);
50 static struct ACPIsdt *acpi_map_sdt(vm_offset_t pa);
51 static void	acpi_handle_rsdt(struct ACPIsdt *rsdp);
52 static struct acpi_user_mapping *acpi_user_find_mapping(vm_offset_t, size_t);
53 static void *	acpi_map_physical(vm_offset_t, size_t);
54 
55 /* Size of an address. 32-bit for ACPI 1.0, 64-bit for ACPI 2.0 and up. */
56 static int addr_size;
57 
58 static int ncpu;
59 
60 int acpi_detect(void);
61 
62 static void
63 acpi_handle_apic(struct ACPIsdt *sdp)
64 {
65 	struct MADTbody *madtp;
66 	struct MADT_APIC *mp;
67 	struct MADT_local_apic *apic;
68 	struct MADT_local_sapic *sapic;
69 
70 	madtp = (struct MADTbody *) sdp->body;
71 	mp = (struct MADT_APIC *)madtp->body;
72 	while (((uintptr_t)mp) - ((uintptr_t)sdp) < sdp->len) {
73 		switch (mp->type) {
74 		case ACPI_MADT_APIC_TYPE_LOCAL_APIC:
75 			apic = &mp->body.local_apic;
76 			warnx("MADT: Found CPU APIC ID %d %s",
77 			    apic->cpu_id,
78 			    apic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED ?
79 				"enabled" : "disabled");
80 			if (apic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED)
81 				ncpu++;
82 			break;
83 		case ACPI_MADT_APIC_TYPE_LOCAL_SAPIC:
84 			sapic = &mp->body.local_sapic;
85 			warnx("MADT: Found CPU SAPIC ID %d %s",
86 			    sapic->cpu_id,
87 			    sapic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED ?
88 				"enabled" : "disabled");
89 			/* XXX is enable flag the same? */
90 			if (sapic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED)
91 				ncpu++;
92 			break;
93 		default:
94 			break;
95 		}
96 		mp = (struct MADT_APIC *) ((char *)mp + mp->len);
97 	}
98 }
99 
100 static int
101 acpi_checksum(void *p, size_t length)
102 {
103 	u_int8_t *bp;
104 	u_int8_t sum;
105 
106 	bp = p;
107 	sum = 0;
108 	while (length--)
109 		sum += *bp++;
110 
111 	return (sum);
112 }
113 
114 static struct ACPIsdt *
115 acpi_map_sdt(vm_offset_t pa)
116 {
117 	struct	ACPIsdt *sp;
118 
119 	sp = acpi_map_physical(pa, sizeof(struct ACPIsdt));
120 	sp = acpi_map_physical(pa, sp->len);
121 	return (sp);
122 }
123 
124 static void
125 acpi_handle_rsdt(struct ACPIsdt *rsdp)
126 {
127 	struct ACPIsdt *sdp;
128 	vm_offset_t addr;
129 	int entries, i;
130 
131 	entries = (rsdp->len - SIZEOF_SDT_HDR) / addr_size;
132 	for (i = 0; i < entries; i++) {
133 		switch (addr_size) {
134 		case 4:
135 			addr = le32dec((char*)rsdp->body + i * addr_size);
136 			break;
137 		case 8:
138 			addr = le64dec((char*)rsdp->body + i * addr_size);
139 			break;
140 		default:
141 			assert((addr = 0));
142 		}
143 
144 		sdp = (struct ACPIsdt *)acpi_map_sdt(addr);
145 		if (acpi_checksum(sdp, sdp->len)) {
146 #if 0
147 			warnx("RSDT entry %d (sig %.4s) has bad checksum", i,
148 			    sdp->signature);
149 #endif
150 			continue;
151 		}
152 		if (!memcmp(sdp->signature, "APIC", 4))
153 			acpi_handle_apic(sdp);
154 	}
155 }
156 
157 static char	machdep_acpi_root[] = "machdep.acpi_root";
158 static int      acpi_mem_fd = -1;
159 
160 struct acpi_user_mapping {
161 	LIST_ENTRY(acpi_user_mapping) link;
162 	vm_offset_t     pa;
163 	caddr_t         va;
164 	size_t          size;
165 };
166 
167 LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist;
168 
169 static void
170 acpi_user_init(void)
171 {
172 
173 	if (acpi_mem_fd == -1) {
174 		acpi_mem_fd = open(_PATH_MEM, O_RDONLY);
175 		if (acpi_mem_fd == -1)
176 			err(1, "opening " _PATH_MEM);
177 		LIST_INIT(&maplist);
178 	}
179 }
180 
181 static struct acpi_user_mapping *
182 acpi_user_find_mapping(vm_offset_t pa, size_t size)
183 {
184 	struct	acpi_user_mapping *map;
185 
186 	/* First search for an existing mapping */
187 	for (map = LIST_FIRST(&maplist); map; map = LIST_NEXT(map, link)) {
188 		if (map->pa <= pa && map->size >= pa + size - map->pa)
189 			return (map);
190 	}
191 
192 	/* Then create a new one */
193 	size = round_page(pa + size) - trunc_page(pa);
194 	pa = trunc_page(pa);
195 	map = malloc(sizeof(struct acpi_user_mapping));
196 	if (!map)
197 		errx(1, "out of memory");
198 	map->pa = pa;
199 	map->va = mmap(0, size, PROT_READ, MAP_SHARED, acpi_mem_fd, pa);
200 	map->size = size;
201 	if ((intptr_t) map->va == -1)
202 		err(1, "can't map address");
203 	LIST_INSERT_HEAD(&maplist, map, link);
204 
205 	return (map);
206 }
207 
208 static void *
209 acpi_map_physical(vm_offset_t pa, size_t size)
210 {
211 	struct	acpi_user_mapping *map;
212 
213 	map = acpi_user_find_mapping(pa, size);
214 	return (map->va + (pa - map->pa));
215 }
216 
217 static struct ACPIrsdp *
218 acpi_get_rsdp(u_long addr)
219 {
220 	struct ACPIrsdp rsdp;
221 	size_t len;
222 
223 	/* Read in the table signature and check it. */
224 	pread(acpi_mem_fd, &rsdp, 8, addr);
225 	if (memcmp(rsdp.signature, "RSD PTR ", 8))
226 		return (NULL);
227 
228 	/* Read the entire table. */
229 	pread(acpi_mem_fd, &rsdp, sizeof(rsdp), addr);
230 
231 	/* Run the checksum only over the version 1 header. */
232 	if (acpi_checksum(&rsdp, 20))
233 		return (NULL);
234 
235 	/* If the revision is 0, assume a version 1 length. */
236 	if (rsdp.revision == 0)
237 		len = 20;
238 	else
239 		len = rsdp.length;
240 
241 	/* XXX Should handle ACPI 2.0 RSDP extended checksum here. */
242 
243 	return (acpi_map_physical(addr, len));
244 }
245 
246 static const char *
247 devstate(devinfo_state_t state)
248 {
249 	switch (state) {
250 	case DS_NOTPRESENT:
251 		return "not-present";
252 	case DS_ALIVE:
253 		return "alive";
254 	case DS_ATTACHED:
255 		return "attached";
256 	case DS_BUSY:
257 		return "busy";
258 	default:
259 		return "unknown-state";
260 	}
261 }
262 
263 static int
264 acpi0_check(struct devinfo_dev *dd, void *arg)
265 {
266 	printf("%s: %s %s\n", __func__, dd->dd_name, devstate(dd->dd_state));
267 	/* NB: device must be present AND attached */
268 	if (strcmp(dd->dd_name, "acpi0") == 0)
269 		return (dd->dd_state == DS_ATTACHED ||
270 			dd->dd_state == DS_BUSY);
271 	return devinfo_foreach_device_child(dd, acpi0_check, arg);
272 }
273 
274 static int
275 acpi0_present(void)
276 {
277 	struct devinfo_dev *root;
278 	int found;
279 
280 	found = 0;
281 	devinfo_init();
282 	root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE);
283 	if (root != NULL)
284 		found = devinfo_foreach_device_child(root, acpi0_check, NULL);
285 	devinfo_free();
286 	return found;
287 }
288 
289 int
290 acpi_detect(void)
291 {
292 	struct ACPIrsdp *rp;
293 	struct ACPIsdt *rsdp;
294 	u_long addr;
295 	size_t len;
296 
297 	if (!acpi0_present()) {
298 		warnx("no acpi0 device located");
299 		return -1;
300 	}
301 
302 	acpi_user_init();
303 
304 	/* Attempt to use sysctl to find RSD PTR record. */
305 	len = sizeof(addr);
306 	if (sysctlbyname(machdep_acpi_root, &addr, &len, NULL, 0) != 0) {
307 		warnx("cannot find ACPI information");
308 		return -1;
309 	}
310 	rp = acpi_get_rsdp(addr);
311 	if (rp == NULL) {
312 		warnx("cannot find ACPI information: sysctl %s does not point to RSDP",
313 			machdep_acpi_root);
314 		return -1;
315 	}
316 	if (rp->revision < 2) {
317 		rsdp = (struct ACPIsdt *)acpi_map_sdt(rp->rsdt_addr);
318 		if (memcmp(rsdp->signature, "RSDT", 4) != 0 ||
319 		    acpi_checksum(rsdp, rsdp->len) != 0)
320 			errx(1, "RSDT is corrupted");
321 		addr_size = sizeof(uint32_t);
322 	} else {
323 		rsdp = (struct ACPIsdt *)acpi_map_sdt(rp->xsdt_addr);
324 		if (memcmp(rsdp->signature, "XSDT", 4) != 0 ||
325 		    acpi_checksum(rsdp, rsdp->len) != 0)
326 			errx(1, "XSDT is corrupted");
327 		addr_size = sizeof(uint64_t);
328 	}
329 	ncpu = 0;
330 	acpi_handle_rsdt(rsdp);
331 	return (ncpu == 0 ? 1 : ncpu);
332 }
333