acpi: Be lenient about unknown revision of FADT, MADT, XSDT and RSDT
[dragonfly.git] / sys / platform / pc32 / acpica / acpi_sdt.c
1 /*
2  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Sepherosa Ziehau <sepherosa@gmail.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/systm.h>
38
39 #include <machine/pmap.h>
40
41 #include "acpi_sdt.h"
42 #include "acpi_sdt_var.h"
43
44 #define SDT_VPRINTF(fmt, arg...) \
45 do { \
46         if (bootverbose) \
47                 kprintf("ACPI SDT: " fmt , ##arg); \
48 } while (0)
49
50 #define ACPI_RSDP_EBDA_MAPSZ    1024
51 #define ACPI_RSDP_BIOS_MAPSZ    0x20000
52 #define ACPI_RSDP_BIOS_MAPADDR  0xe0000
53
54 #define ACPI_RSDP_ALIGN         16
55
56 #define ACPI_RSDP_SIGLEN        8
57 #define ACPI_RSDP_SIG           "RSD PTR "
58
59 /* Root System Description Pointer */
60 struct acpi_rsdp {
61         uint8_t                 rsdp_sig[ACPI_RSDP_SIGLEN];
62         uint8_t                 rsdp_cksum;
63         uint8_t                 rsdp_oem_id[6];
64         uint8_t                 rsdp_rev;
65         uint32_t                rsdp_rsdt;
66         uint32_t                rsdp_len;
67         uint64_t                rsdp_xsdt;
68         uint8_t                 rsdp_ext_cksum;
69         uint8_t                 rsdp_rsvd[3];
70 } __packed;
71
72 /* Extended System Description Table */
73 struct acpi_xsdt {
74         struct acpi_sdth        xsdt_hdr;
75         uint64_t                xsdt_ents[1];
76 } __packed;
77
78 /* Root System Description Table */
79 struct acpi_rsdt {
80         struct acpi_sdth        rsdt_hdr;
81         uint32_t                rsdt_ents[1];
82 } __packed;
83
84 typedef vm_paddr_t              (*sdt_search_t)(vm_paddr_t, const uint8_t *);
85
86 static const struct acpi_rsdp   *sdt_rsdp_search(const uint8_t *, int);
87 static vm_paddr_t               sdt_search_xsdt(vm_paddr_t, const uint8_t *);
88 static vm_paddr_t               sdt_search_rsdt(vm_paddr_t, const uint8_t *);
89
90 extern u_long                   ebda_addr;
91
92 static sdt_search_t             sdt_search_func;
93 static vm_paddr_t               sdt_search_paddr;
94
95 static void
96 sdt_probe(void)
97 {
98         const struct acpi_rsdp *rsdp;
99         vm_size_t mapsz;
100         uint8_t *ptr;
101
102         if (ebda_addr != 0) {
103                 mapsz = ACPI_RSDP_EBDA_MAPSZ;
104                 ptr = pmap_mapdev(ebda_addr, mapsz);
105
106                 rsdp = sdt_rsdp_search(ptr, mapsz);
107                 if (rsdp == NULL) {
108                         SDT_VPRINTF("RSDP not in EBDA\n");
109                         pmap_unmapdev((vm_offset_t)ptr, mapsz);
110
111                         ptr = NULL;
112                         mapsz = 0;
113                 } else {
114                         SDT_VPRINTF("RSDP in EBDA\n");
115                         goto found_rsdp;
116                 }
117         }
118
119         mapsz = ACPI_RSDP_BIOS_MAPSZ;
120         ptr = pmap_mapdev(ACPI_RSDP_BIOS_MAPADDR, mapsz);
121
122         rsdp = sdt_rsdp_search(ptr, mapsz);
123         if (rsdp == NULL) {
124                 kprintf("sdt_probe: no RSDP\n");
125                 pmap_unmapdev((vm_offset_t)ptr, mapsz);
126                 return;
127         } else {
128                 SDT_VPRINTF("RSDP in BIOS mem\n");
129         }
130
131 found_rsdp:
132         if (rsdp->rsdp_rev != 2) {
133                 sdt_search_func = sdt_search_rsdt;
134                 sdt_search_paddr = rsdp->rsdp_rsdt;
135         } else {
136                 sdt_search_func = sdt_search_xsdt;
137                 sdt_search_paddr = rsdp->rsdp_xsdt;
138         }
139         pmap_unmapdev((vm_offset_t)ptr, mapsz);
140 }
141 SYSINIT(sdt_probe, SI_BOOT2_PRESMP, SI_ORDER_FIRST, sdt_probe, 0);
142
143 static const struct acpi_rsdp *
144 sdt_rsdp_search(const uint8_t *target, int size)
145 {
146         const struct acpi_rsdp *rsdp;
147         int i;
148
149         KKASSERT(size > sizeof(*rsdp));
150
151         for (i = 0; i < size - sizeof(*rsdp); i += ACPI_RSDP_ALIGN) {
152                 rsdp = (const struct acpi_rsdp *)&target[i];
153                 if (memcmp(rsdp->rsdp_sig, ACPI_RSDP_SIG,
154                            ACPI_RSDP_SIGLEN) == 0)
155                         return rsdp;
156         }
157         return NULL;
158 }
159
160 void *
161 sdt_sdth_map(vm_paddr_t paddr)
162 {
163         struct acpi_sdth *sdth;
164         vm_size_t mapsz;
165
166         sdth = pmap_mapdev(paddr, sizeof(*sdth));
167         mapsz = sdth->sdth_len;
168         pmap_unmapdev((vm_offset_t)sdth, sizeof(*sdth));
169
170         if (mapsz < sizeof(*sdth))
171                 return NULL;
172
173         return pmap_mapdev(paddr, mapsz);
174 }
175
176 void
177 sdt_sdth_unmap(struct acpi_sdth *sdth)
178 {
179         pmap_unmapdev((vm_offset_t)sdth, sdth->sdth_len);
180 }
181
182 static vm_paddr_t
183 sdt_search_xsdt(vm_paddr_t xsdt_paddr, const uint8_t *sig)
184 {
185         struct acpi_xsdt *xsdt;
186         vm_paddr_t sdt_paddr = 0;
187         int i, nent;
188
189         if (xsdt_paddr == 0) {
190                 kprintf("sdt_search_xsdt: XSDT paddr == 0\n");
191                 return 0;
192         }
193
194         xsdt = sdt_sdth_map(xsdt_paddr);
195         if (xsdt == NULL) {
196                 kprintf("sdt_search_xsdt: can't map XSDT\n");
197                 return 0;
198         }
199
200         if (memcmp(xsdt->xsdt_hdr.sdth_sig, ACPI_XSDT_SIG,
201                    ACPI_SDTH_SIGLEN) != 0) {
202                 kprintf("sdt_search_xsdt: not XSDT\n");
203                 goto back;
204         }
205
206         if (xsdt->xsdt_hdr.sdth_rev != 1) {
207                 kprintf("sdt_search_xsdt: unknown XSDT revision %d\n",
208                         xsdt->xsdt_hdr.sdth_rev);
209         }
210
211         if (xsdt->xsdt_hdr.sdth_len < sizeof(xsdt->xsdt_hdr)) {
212                 kprintf("sdt_search_xsdt: invalid XSDT length %u\n",
213                         xsdt->xsdt_hdr.sdth_len);
214                 goto back;
215         }
216
217         nent = (xsdt->xsdt_hdr.sdth_len - sizeof(xsdt->xsdt_hdr)) /
218                sizeof(xsdt->xsdt_ents[0]);
219         for (i = 0; i < nent; ++i) {
220                 struct acpi_sdth *sdth;
221
222                 if (xsdt->xsdt_ents[i] == 0)
223                         continue;
224
225                 sdth = sdt_sdth_map(xsdt->xsdt_ents[i]);
226                 if (sdth != NULL) {
227                         int ret;
228
229                         ret = memcmp(sdth->sdth_sig, sig, ACPI_SDTH_SIGLEN);
230                         sdt_sdth_unmap(sdth);
231
232                         if (ret == 0) {
233                                 sdt_paddr = xsdt->xsdt_ents[i];
234                                 break;
235                         }
236                 }
237         }
238 back:
239         sdt_sdth_unmap(&xsdt->xsdt_hdr);
240         return sdt_paddr;
241 }
242
243 static vm_paddr_t
244 sdt_search_rsdt(vm_paddr_t rsdt_paddr, const uint8_t *sig)
245 {
246         struct acpi_rsdt *rsdt;
247         vm_paddr_t sdt_paddr = 0;
248         int i, nent;
249
250         if (rsdt_paddr == 0) {
251                 kprintf("sdt_search_rsdt: RSDT paddr == 0\n");
252                 return 0;
253         }
254
255         rsdt = sdt_sdth_map(rsdt_paddr);
256         if (rsdt == NULL) {
257                 kprintf("sdt_search_rsdt: can't map RSDT\n");
258                 return 0;
259         }
260
261         if (memcmp(rsdt->rsdt_hdr.sdth_sig, ACPI_RSDT_SIG,
262                    ACPI_SDTH_SIGLEN) != 0) {
263                 kprintf("sdt_search_rsdt: not RSDT\n");
264                 goto back;
265         }
266
267         if (rsdt->rsdt_hdr.sdth_rev != 1) {
268                 kprintf("sdt_search_rsdt: unknown RSDT revision %d\n",
269                         rsdt->rsdt_hdr.sdth_rev);
270         }
271
272         if (rsdt->rsdt_hdr.sdth_len < sizeof(rsdt->rsdt_hdr)) {
273                 kprintf("sdt_search_rsdt: invalid RSDT length %u\n",
274                         rsdt->rsdt_hdr.sdth_len);
275                 goto back;
276         }
277
278         nent = (rsdt->rsdt_hdr.sdth_len - sizeof(rsdt->rsdt_hdr)) /
279                sizeof(rsdt->rsdt_ents[0]);
280         for (i = 0; i < nent; ++i) {
281                 struct acpi_sdth *sdth;
282
283                 if (rsdt->rsdt_ents[i] == 0)
284                         continue;
285
286                 sdth = sdt_sdth_map(rsdt->rsdt_ents[i]);
287                 if (sdth != NULL) {
288                         int ret;
289
290                         ret = memcmp(sdth->sdth_sig, sig, ACPI_SDTH_SIGLEN);
291                         sdt_sdth_unmap(sdth);
292
293                         if (ret == 0) {
294                                 sdt_paddr = rsdt->rsdt_ents[i];
295                                 break;
296                         }
297                 }
298         }
299 back:
300         sdt_sdth_unmap(&rsdt->rsdt_hdr);
301         return sdt_paddr;
302 }
303
304 vm_paddr_t
305 sdt_search(const uint8_t *sig)
306 {
307         if (sdt_search_func == NULL)
308                 return 0;
309         return sdt_search_func(sdt_search_paddr, sig);
310 }