kernel/acpi: Reduce code duplication with ACPICA.
[dragonfly.git] / sys / platform / pc64 / 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 <contrib/dev/acpica/source/include/acpi.h>
42
43 #include "acpi_sdt_var.h"
44
45 #define SDT_VPRINTF(fmt, arg...) \
46 do { \
47         if (bootverbose) \
48                 kprintf("ACPI SDT: " fmt , ##arg); \
49 } while (0)
50
51 typedef vm_paddr_t              (*sdt_search_t)(vm_paddr_t, const uint8_t *);
52
53 static const ACPI_TABLE_RSDP    *sdt_rsdp_search(const uint8_t *, int);
54 static vm_paddr_t               sdt_search_xsdt(vm_paddr_t, const uint8_t *);
55 static vm_paddr_t               sdt_search_rsdt(vm_paddr_t, const uint8_t *);
56
57 extern u_long                   ebda_addr;
58
59 static sdt_search_t             sdt_search_func;
60 static vm_paddr_t               sdt_search_paddr;
61
62 static void
63 sdt_probe(void)
64 {
65         const ACPI_TABLE_RSDP *rsdp;
66         vm_size_t mapsz;
67         uint8_t *ptr;
68
69         if (ebda_addr != 0) {
70                 mapsz = ACPI_EBDA_WINDOW_SIZE;
71                 ptr = pmap_mapdev(ebda_addr, mapsz);
72
73                 rsdp = sdt_rsdp_search(ptr, mapsz);
74                 if (rsdp == NULL) {
75                         SDT_VPRINTF("RSDP not in EBDA\n");
76                         pmap_unmapdev((vm_offset_t)ptr, mapsz);
77
78                         ptr = NULL;
79                         mapsz = 0;
80                 } else {
81                         SDT_VPRINTF("RSDP in EBDA\n");
82                         goto found_rsdp;
83                 }
84         }
85
86         mapsz = ACPI_HI_RSDP_WINDOW_SIZE;
87         ptr = pmap_mapdev(ACPI_HI_RSDP_WINDOW_BASE, mapsz);
88
89         rsdp = sdt_rsdp_search(ptr, mapsz);
90         if (rsdp == NULL) {
91                 kprintf("sdt_probe: no RSDP\n");
92                 pmap_unmapdev((vm_offset_t)ptr, mapsz);
93                 return;
94         } else {
95                 SDT_VPRINTF("RSDP in BIOS mem\n");
96         }
97
98 found_rsdp:
99         if (rsdp->Revision != 2 /* || AcpiGbl_DoNotUseXsdt */) {
100                 sdt_search_func = sdt_search_rsdt;
101                 sdt_search_paddr = rsdp->RsdtPhysicalAddress;
102         } else {
103                 sdt_search_func = sdt_search_xsdt;
104                 sdt_search_paddr = rsdp->XsdtPhysicalAddress;
105         }
106         pmap_unmapdev((vm_offset_t)ptr, mapsz);
107 }
108 SYSINIT(sdt_probe, SI_BOOT2_PRESMP, SI_ORDER_FIRST, sdt_probe, 0);
109
110 static const ACPI_TABLE_RSDP *
111 sdt_rsdp_search(const uint8_t *target, int size)
112 {
113         const ACPI_TABLE_RSDP *rsdp;
114         int i;
115
116         KKASSERT(size > sizeof(*rsdp));
117
118         for (i = 0; i < size - sizeof(*rsdp); i += ACPI_RSDP_SCAN_STEP) {
119                 rsdp = (const ACPI_TABLE_RSDP *)&target[i];
120                 if (memcmp(rsdp->Signature, ACPI_SIG_RSDP,
121                            sizeof(rsdp->Signature)) == 0)
122                         return rsdp;
123         }
124         return NULL;
125 }
126
127 void *
128 sdt_sdth_map(vm_paddr_t paddr)
129 {
130         ACPI_TABLE_HEADER *sdth;
131         vm_size_t mapsz;
132
133         sdth = pmap_mapdev(paddr, sizeof(*sdth));
134         mapsz = sdth->Length;
135         pmap_unmapdev((vm_offset_t)sdth, sizeof(*sdth));
136
137         if (mapsz < sizeof(*sdth))
138                 return NULL;
139
140         return pmap_mapdev(paddr, mapsz);
141 }
142
143 void
144 sdt_sdth_unmap(ACPI_TABLE_HEADER *sdth)
145 {
146         pmap_unmapdev((vm_offset_t)sdth, sdth->Length);
147 }
148
149 static vm_paddr_t
150 sdt_search_xsdt(vm_paddr_t xsdt_paddr, const uint8_t *sig)
151 {
152         ACPI_TABLE_XSDT *xsdt;
153         vm_paddr_t sdt_paddr = 0;
154         int i, nent;
155
156         if (xsdt_paddr == 0) {
157                 kprintf("sdt_search_xsdt: XSDT paddr == 0\n");
158                 return 0;
159         }
160
161         xsdt = sdt_sdth_map(xsdt_paddr);
162         if (xsdt == NULL) {
163                 kprintf("sdt_search_xsdt: can't map XSDT\n");
164                 return 0;
165         }
166
167         if (memcmp(xsdt->Header.Signature, ACPI_SIG_XSDT,
168                    ACPI_NAME_SIZE) != 0) {
169                 kprintf("sdt_search_xsdt: not XSDT\n");
170                 goto back;
171         }
172
173         if (xsdt->Header.Revision != 1) {
174                 kprintf("sdt_search_xsdt: unknown XSDT revision %d\n",
175                         xsdt->Header.Revision);
176         }
177
178         if (xsdt->Header.Length < sizeof(xsdt->Header)) {
179                 kprintf("sdt_search_xsdt: invalid XSDT length %u\n",
180                         xsdt->Header.Length);
181                 goto back;
182         }
183
184         nent = (xsdt->Header.Length - sizeof(xsdt->Header)) /
185                sizeof(xsdt->TableOffsetEntry[0]);
186         for (i = 0; i < nent; ++i) {
187                 ACPI_TABLE_HEADER *sdth;
188
189                 if (xsdt->TableOffsetEntry[i] == 0)
190                         continue;
191
192                 sdth = sdt_sdth_map(xsdt->TableOffsetEntry[i]);
193                 if (sdth != NULL) {
194                         int ret;
195
196                         ret = memcmp(sdth->Signature, sig, ACPI_NAME_SIZE);
197                         sdt_sdth_unmap(sdth);
198
199                         if (ret == 0) {
200                                 sdt_paddr = xsdt->TableOffsetEntry[i];
201                                 break;
202                         }
203                 }
204         }
205 back:
206         sdt_sdth_unmap(&xsdt->Header);
207         return sdt_paddr;
208 }
209
210 static vm_paddr_t
211 sdt_search_rsdt(vm_paddr_t rsdt_paddr, const uint8_t *sig)
212 {
213         ACPI_TABLE_RSDT *rsdt;
214         vm_paddr_t sdt_paddr = 0;
215         int i, nent;
216
217         if (rsdt_paddr == 0) {
218                 kprintf("sdt_search_rsdt: RSDT paddr == 0\n");
219                 return 0;
220         }
221
222         rsdt = sdt_sdth_map(rsdt_paddr);
223         if (rsdt == NULL) {
224                 kprintf("sdt_search_rsdt: can't map RSDT\n");
225                 return 0;
226         }
227
228         if (memcmp(rsdt->Header.Signature, ACPI_SIG_RSDT,
229                    ACPI_NAME_SIZE) != 0) {
230                 kprintf("sdt_search_rsdt: not RSDT\n");
231                 goto back;
232         }
233
234         if (rsdt->Header.Revision != 1) {
235                 kprintf("sdt_search_rsdt: unknown RSDT revision %d\n",
236                         rsdt->Header.Revision);
237         }
238
239         if (rsdt->Header.Length < sizeof(rsdt->Header)) {
240                 kprintf("sdt_search_rsdt: invalid RSDT length %u\n",
241                         rsdt->Header.Length);
242                 goto back;
243         }
244
245         nent = (rsdt->Header.Length - sizeof(rsdt->Header)) /
246                sizeof(rsdt->TableOffsetEntry[0]);
247         for (i = 0; i < nent; ++i) {
248                 ACPI_TABLE_HEADER *sdth;
249
250                 if (rsdt->TableOffsetEntry[i] == 0)
251                         continue;
252
253                 sdth = sdt_sdth_map(rsdt->TableOffsetEntry[i]);
254                 if (sdth != NULL) {
255                         int ret;
256
257                         ret = memcmp(sdth->Signature, sig, ACPI_NAME_SIZE);
258                         sdt_sdth_unmap(sdth);
259
260                         if (ret == 0) {
261                                 sdt_paddr = rsdt->TableOffsetEntry[i];
262                                 break;
263                         }
264                 }
265         }
266 back:
267         sdt_sdth_unmap(&rsdt->Header);
268         return sdt_paddr;
269 }
270
271 vm_paddr_t
272 sdt_search(const uint8_t *sig)
273 {
274         if (sdt_search_func == NULL)
275                 return 0;
276         return sdt_search_func(sdt_search_paddr, sig);
277 }