acpi_dev.c revision 1.1 1 /* $NetBSD: acpi_dev.c,v 1.1 2020/12/06 02:57:30 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2020 Jared McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
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 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: acpi_dev.c,v 1.1 2020/12/06 02:57:30 jmcneill Exp $");
31
32 #include <sys/param.h>
33 #include <sys/conf.h>
34 #include <sys/mman.h>
35
36 #include <dev/acpi/acpireg.h>
37 #include <dev/acpi/acpivar.h>
38
39 #define _COMPONENT ACPI_BUS_COMPONENT
40 ACPI_MODULE_NAME ("acpi_dev")
41
42 static dev_type_read(acpi_read);
43
44 const struct cdevsw acpi_cdevsw = {
45 .d_open = nullopen,
46 .d_close = nullclose,
47 .d_read = acpi_read,
48 .d_write = nowrite,
49 .d_ioctl = noioctl,
50 .d_stop = nostop,
51 .d_tty = notty,
52 .d_poll = nopoll,
53 .d_mmap = nommap,
54 .d_kqfilter = nokqfilter,
55 .d_discard = nodiscard,
56 .d_flag = D_OTHER | D_MPSAFE,
57 };
58
59 /*
60 * acpi_find_table_rsdp --
61 *
62 * Returns true if the RSDP table is found and overlaps the specified
63 * physical address. The table's physical start address and length
64 * are placed in 'paddr' and 'plen' when found.
65 *
66 */
67 static bool
68 acpi_find_table_rsdp(ACPI_PHYSICAL_ADDRESS pa,
69 ACPI_PHYSICAL_ADDRESS *paddr, uint32_t *plen)
70 {
71 ACPI_PHYSICAL_ADDRESS table_pa;
72
73 table_pa = AcpiOsGetRootPointer();
74 if (table_pa == 0) {
75 return false;
76 }
77 if (pa >= table_pa && pa < table_pa + sizeof(ACPI_TABLE_RSDP)) {
78 *paddr = table_pa;
79 *plen = sizeof(ACPI_TABLE_RSDP);
80 return true;
81 }
82
83 return false;
84 }
85
86 /*
87 * acpi_find_table_sdt --
88 *
89 * Returns true if the XSDT/RSDT table is found and overlaps the
90 * specified physical address. The table's physical start address
91 * and length are placed in 'paddr' and 'plen' when found.
92 *
93 */
94 static bool
95 acpi_find_table_sdt(ACPI_PHYSICAL_ADDRESS pa,
96 ACPI_PHYSICAL_ADDRESS *paddr, uint32_t *plen)
97 {
98 ACPI_PHYSICAL_ADDRESS table_pa;
99 ACPI_TABLE_RSDP *rsdp;
100 ACPI_TABLE_HEADER *thdr;
101 uint32_t table_len;
102
103 table_pa = AcpiOsGetRootPointer();
104 KASSERT(table_pa != 0);
105
106 /*
107 * Find the XSDT/RSDT using the RSDP.
108 */
109 rsdp = AcpiOsMapMemory(table_pa, sizeof(ACPI_TABLE_RSDP));
110 if (rsdp == NULL) {
111 return false;
112 }
113 if (rsdp->Revision > 1 && rsdp->XsdtPhysicalAddress) {
114 table_pa = rsdp->XsdtPhysicalAddress;
115 } else {
116 table_pa = rsdp->RsdtPhysicalAddress;
117 }
118 AcpiOsUnmapMemory(rsdp, sizeof(ACPI_TABLE_RSDP));
119 if (table_pa == 0) {
120 return false;
121 }
122
123 /*
124 * Map the XSDT/RSDT and check access.
125 */
126 thdr = AcpiOsMapMemory(table_pa, sizeof(ACPI_TABLE_HEADER));
127 if (thdr == NULL) {
128 return false;
129 }
130 table_len = thdr->Length;
131 AcpiOsUnmapMemory(thdr, sizeof(ACPI_TABLE_HEADER));
132 if (pa >= table_pa && pa < table_pa + table_len) {
133 *paddr = table_pa;
134 *plen = table_len;
135 return true;
136 }
137
138 return false;
139 }
140
141 /*
142 * acpi_find_table --
143 *
144 * Find an ACPI table that overlaps the specified physical address.
145 * Returns true if the table is found and places the table start
146 * address into 'paddr' and the length into 'plen'.
147 *
148 */
149 static bool
150 acpi_find_table(ACPI_PHYSICAL_ADDRESS pa,
151 ACPI_PHYSICAL_ADDRESS *paddr, uint32_t *plen)
152 {
153 ACPI_TABLE_DESC *tdesc;
154 bool found_table;
155 uint32_t i;
156
157 /* Check for RSDP access. */
158 if (acpi_find_table_rsdp(pa, paddr, plen)) {
159 return true;
160 }
161
162 /* Check for XSDT/RSDT access. */
163 if (acpi_find_table_sdt(pa, paddr, plen)) {
164 return true;
165 }
166
167 /* Check for root table access. */
168 found_table = false;
169 AcpiUtAcquireMutex(ACPI_MTX_TABLES);
170 for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++) {
171 tdesc = &AcpiGbl_RootTableList.Tables[i];
172 if (pa >= tdesc->Address &&
173 pa < tdesc->Address + tdesc->Length) {
174 *paddr = tdesc->Address;
175 *plen = tdesc->Length;
176 found_table = true;
177 break;
178 }
179 }
180 AcpiUtReleaseMutex(ACPI_MTX_TABLES);
181 return found_table;
182 }
183
184 /*
185 * acpi_read --
186 *
187 * Read data from an ACPI configuration table that resides in
188 * physical memory. Only supports reading one table at a time.
189 *
190 */
191 static int
192 acpi_read(dev_t dev, struct uio *uio, int flag)
193 {
194 ACPI_PHYSICAL_ADDRESS pa, table_pa;
195 uint32_t table_len;
196 uint8_t *data;
197 int error;
198 size_t len;
199
200 if (uio->uio_rw != UIO_READ) {
201 return EPERM;
202 }
203
204 /* Make sure this is a read of a known table */
205 if (!acpi_find_table(uio->uio_offset, &table_pa, &table_len)) {
206 return EIO;
207 }
208
209 /* Copy the contents of the table to user-space */
210 pa = uio->uio_offset;
211 len = uimin(pa - table_pa + table_len, uio->uio_resid);
212 data = AcpiOsMapMemory(pa, len);
213 if (data == NULL) {
214 return ENOMEM;
215 }
216 error = uiomove(data, len, uio);
217 AcpiOsUnmapMemory(data, len);
218
219 return error;
220 }
221