1 /*
2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <errno.h>
27 #include <byteswap.h>
28 #include <ipxe/uaccess.h>
29 #include <ipxe/acpi.h>
30 #include <ipxe/interface.h>
31
32 /** @file
33 *
34 * ACPI support functions
35 *
36 */
37
38 /******************************************************************************
39 *
40 * Utility functions
41 *
42 ******************************************************************************
43 */
44
45 /**
46 * Fix up ACPI table checksum
47 *
48 * @v acpi ACPI table header
49 */
acpi_fix_checksum(struct acpi_header * acpi)50 void acpi_fix_checksum ( struct acpi_header *acpi ) {
51 unsigned int i = 0;
52 uint8_t sum = 0;
53
54 for ( i = 0 ; i < acpi->length ; i++ ) {
55 sum += *( ( ( uint8_t * ) acpi ) + i );
56 }
57 acpi->checksum -= sum;
58 }
59
60 /**
61 * Locate ACPI table
62 *
63 * @v signature Requested table signature
64 * @v index Requested index of table with this signature
65 * @ret table Table, or UNULL if not found
66 */
acpi_find(uint32_t signature,unsigned int index)67 userptr_t acpi_find ( uint32_t signature, unsigned int index ) {
68 struct acpi_header acpi;
69 struct acpi_rsdt *rsdtab;
70 typeof ( rsdtab->entry[0] ) entry;
71 userptr_t rsdt;
72 userptr_t table;
73 size_t len;
74 unsigned int count;
75 unsigned int i;
76
77 /* Locate RSDT */
78 rsdt = acpi_find_rsdt();
79 if ( ! rsdt ) {
80 DBG ( "RSDT not found\n" );
81 return UNULL;
82 }
83
84 /* Read RSDT header */
85 copy_from_user ( &acpi, rsdt, 0, sizeof ( acpi ) );
86 if ( acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) {
87 DBGC ( rsdt, "RSDT %#08lx has invalid signature:\n",
88 user_to_phys ( rsdt, 0 ) );
89 DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi,
90 sizeof ( acpi ) );
91 return UNULL;
92 }
93 len = le32_to_cpu ( acpi.length );
94 if ( len < sizeof ( rsdtab->acpi ) ) {
95 DBGC ( rsdt, "RSDT %#08lx has invalid length:\n",
96 user_to_phys ( rsdt, 0 ) );
97 DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi,
98 sizeof ( acpi ) );
99 return UNULL;
100 }
101
102 /* Calculate number of entries */
103 count = ( ( len - sizeof ( rsdtab->acpi ) ) / sizeof ( entry ) );
104
105 /* Search through entries */
106 for ( i = 0 ; i < count ; i++ ) {
107
108 /* Get table address */
109 copy_from_user ( &entry, rsdt,
110 offsetof ( typeof ( *rsdtab ), entry[i] ),
111 sizeof ( entry ) );
112
113 /* Read table header */
114 table = phys_to_user ( entry );
115 copy_from_user ( &acpi.signature, table, 0,
116 sizeof ( acpi.signature ) );
117
118 /* Check table signature */
119 if ( acpi.signature != cpu_to_le32 ( signature ) )
120 continue;
121
122 /* Check index */
123 if ( index-- )
124 continue;
125
126 DBGC ( rsdt, "RSDT %#08lx found %s at %08lx\n",
127 user_to_phys ( rsdt, 0 ), acpi_name ( signature ),
128 user_to_phys ( table, 0 ) );
129 return table;
130 }
131
132 DBGC ( rsdt, "RSDT %#08lx could not find %s\n",
133 user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
134 return UNULL;
135 }
136
137 /**
138 * Extract \_Sx value from DSDT/SSDT
139 *
140 * @v zsdt DSDT or SSDT
141 * @v signature Signature (e.g. "_S5_")
142 * @ret sx \_Sx value, or negative error
143 *
144 * In theory, extracting the \_Sx value from the DSDT/SSDT requires a
145 * full ACPI parser plus some heuristics to work around the various
146 * broken encodings encountered in real ACPI implementations.
147 *
148 * In practice, we can get the same result by scanning through the
149 * DSDT/SSDT for the signature (e.g. "_S5_"), extracting the first
150 * four bytes, removing any bytes with bit 3 set, and treating
151 * whatever is left as a little-endian value. This is one of the
152 * uglier hacks I have ever implemented, but it's still prettier than
153 * the ACPI specification itself.
154 */
acpi_sx_zsdt(userptr_t zsdt,uint32_t signature)155 static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
156 struct acpi_header acpi;
157 union {
158 uint32_t dword;
159 uint8_t byte[4];
160 } buf;
161 size_t offset;
162 size_t len;
163 unsigned int sx;
164 uint8_t *byte;
165
166 /* Read table header */
167 copy_from_user ( &acpi, zsdt, 0, sizeof ( acpi ) );
168 len = le32_to_cpu ( acpi.length );
169
170 /* Locate signature */
171 for ( offset = sizeof ( acpi ) ;
172 ( ( offset + sizeof ( buf ) /* signature */ + 3 /* pkg header */
173 + sizeof ( buf ) /* value */ ) < len ) ;
174 offset++ ) {
175
176 /* Check signature */
177 copy_from_user ( &buf, zsdt, offset, sizeof ( buf ) );
178 if ( buf.dword != cpu_to_le32 ( signature ) )
179 continue;
180 DBGC ( zsdt, "DSDT/SSDT %#08lx found %s at offset %#zx\n",
181 user_to_phys ( zsdt, 0 ), acpi_name ( signature ),
182 offset );
183 offset += sizeof ( buf );
184
185 /* Read first four bytes of value */
186 copy_from_user ( &buf, zsdt, ( offset + 3 /* pkg header */ ),
187 sizeof ( buf ) );
188 DBGC ( zsdt, "DSDT/SSDT %#08lx found %s containing "
189 "%02x:%02x:%02x:%02x\n", user_to_phys ( zsdt, 0 ),
190 acpi_name ( signature ), buf.byte[0], buf.byte[1],
191 buf.byte[2], buf.byte[3] );
192
193 /* Extract \Sx value. There are three potential
194 * encodings that we might encounter:
195 *
196 * - SLP_TYPa, SLP_TYPb, rsvd, rsvd
197 *
198 * - <byteprefix>, SLP_TYPa, <byteprefix>, SLP_TYPb, ...
199 *
200 * - <dwordprefix>, SLP_TYPa, SLP_TYPb, 0, 0
201 *
202 * Since <byteprefix> and <dwordprefix> both have bit
203 * 3 set, and valid SLP_TYPx must have bit 3 clear
204 * (since SLP_TYPx is a 3-bit field), we can just skip
205 * any bytes with bit 3 set.
206 */
207 byte = &buf.byte[0];
208 if ( *byte & 0x08 )
209 byte++;
210 sx = *(byte++);
211 if ( *byte & 0x08 )
212 byte++;
213 sx |= ( *byte << 8 );
214 return sx;
215 }
216
217 return -ENOENT;
218 }
219
220 /**
221 * Extract \_Sx value from DSDT/SSDT
222 *
223 * @v signature Signature (e.g. "_S5_")
224 * @ret sx \_Sx value, or negative error
225 */
acpi_sx(uint32_t signature)226 int acpi_sx ( uint32_t signature ) {
227 struct acpi_fadt fadtab;
228 userptr_t rsdt;
229 userptr_t fadt;
230 userptr_t dsdt;
231 userptr_t ssdt;
232 unsigned int i;
233 int sx;
234
235 /* Locate RSDT */
236 rsdt = acpi_find_rsdt();
237 if ( ! rsdt ) {
238 DBG ( "RSDT not found\n" );
239 return -ENOENT;
240 }
241
242 /* Try DSDT first */
243 fadt = acpi_find ( FADT_SIGNATURE, 0 );
244 if ( fadt ) {
245 copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) );
246 dsdt = phys_to_user ( fadtab.dsdt );
247 if ( ( sx = acpi_sx_zsdt ( dsdt, signature ) ) >= 0 )
248 return sx;
249 }
250
251 /* Try all SSDTs */
252 for ( i = 0 ; ; i++ ) {
253 ssdt = acpi_find ( SSDT_SIGNATURE, i );
254 if ( ! ssdt )
255 break;
256 if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 )
257 return sx;
258 }
259
260 DBGC ( rsdt, "RSDT %#08lx could not find \\_Sx \"%s\"\n",
261 user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
262 return -ENOENT;
263 }
264
265 /******************************************************************************
266 *
267 * Descriptors
268 *
269 ******************************************************************************
270 */
271
272 /**
273 * Add ACPI descriptor
274 *
275 * @v desc ACPI descriptor
276 */
acpi_add(struct acpi_descriptor * desc)277 void acpi_add ( struct acpi_descriptor *desc ) {
278
279 /* Add to list of descriptors */
280 ref_get ( desc->refcnt );
281 list_add_tail ( &desc->list, &desc->model->descs );
282 }
283
284 /**
285 * Remove ACPI descriptor
286 *
287 * @v desc ACPI descriptor
288 */
acpi_del(struct acpi_descriptor * desc)289 void acpi_del ( struct acpi_descriptor *desc ) {
290
291 /* Remove from list of descriptors */
292 list_check_contains_entry ( desc, &desc->model->descs, list );
293 list_del ( &desc->list );
294 ref_put ( desc->refcnt );
295 }
296
297 /**
298 * Get object's ACPI descriptor
299 *
300 * @v intf Interface
301 * @ret desc ACPI descriptor, or NULL
302 */
acpi_describe(struct interface * intf)303 struct acpi_descriptor * acpi_describe ( struct interface *intf ) {
304 struct interface *dest;
305 acpi_describe_TYPE ( void * ) *op =
306 intf_get_dest_op ( intf, acpi_describe, &dest );
307 void *object = intf_object ( dest );
308 struct acpi_descriptor *desc;
309
310 if ( op ) {
311 desc = op ( object );
312 } else {
313 desc = NULL;
314 }
315
316 intf_put ( dest );
317 return desc;
318 }
319
320 /**
321 * Install ACPI tables
322 *
323 * @v install Table installation method
324 * @ret rc Return status code
325 */
acpi_install(int (* install)(struct acpi_header * acpi))326 int acpi_install ( int ( * install ) ( struct acpi_header *acpi ) ){
327 struct acpi_model *model;
328 int rc;
329
330 for_each_table_entry ( model, ACPI_MODELS ) {
331 if ( ( rc = model->install ( install ) ) != 0 )
332 return rc;
333 }
334
335 return 0;
336 }
337