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
49static void	acpi_handle_apic(struct ACPIsdt *sdp);
50static struct ACPIsdt *acpi_map_sdt(vm_offset_t pa);
51static void	acpi_handle_rsdt(struct ACPIsdt *rsdp);
52static struct acpi_user_mapping *acpi_user_find_mapping(vm_offset_t, size_t);
53static 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. */
56static int addr_size;
57
58static int ncpu;
59
60int acpi_detect(void);
61
62static void
63acpi_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
100static int
101acpi_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
114static struct ACPIsdt *
115acpi_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
124static void
125acpi_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
157static char	machdep_acpi_root[] = "machdep.acpi_root";
158static int      acpi_mem_fd = -1;
159
160struct 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
167LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist;
168
169static void
170acpi_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
181static struct acpi_user_mapping *
182acpi_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
208static void *
209acpi_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
217static struct ACPIrsdp *
218acpi_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
246static const char *
247devstate(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
263static int
264acpi0_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
274static int
275acpi0_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
289int
290acpi_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