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