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