]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - tools/tools/ncpus/acpi.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / tools / tools / ncpus / acpi.c
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 }