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