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