[process] Include process name in debug messages
[ipxe.git] / src / core / acpi.c
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 * Compute ACPI table checksum
47 *
48 * @v table Any ACPI table
49 * @ret checksum 0 if checksum is good
50 */
51 static uint8_t acpi_checksum ( userptr_t table ) {
52 struct acpi_header acpi;
53 uint8_t sum = 0;
54 uint8_t data = 0;
55 unsigned int i;
56
57 /* Read table length */
58 copy_from_user ( &acpi.length, table,
59 offsetof ( typeof ( acpi ), length ),
60 sizeof ( acpi.length ) );
61
62 /* Compute checksum */
63 for ( i = 0 ; i < le32_to_cpu ( acpi.length ) ; i++ ) {
64 copy_from_user ( &data, table, i, sizeof ( data ) );
65 sum += data;
66 }
67
68 return sum;
69 }
70
71 /**
72 * Fix up ACPI table checksum
73 *
74 * @v acpi ACPI table header
75 */
76 void acpi_fix_checksum ( struct acpi_header *acpi ) {
77
78 /* Update checksum */
79 acpi->checksum -= acpi_checksum ( virt_to_user ( acpi ) );
80 }
81
82 /**
83 * Locate ACPI table
84 *
85 * @v signature Requested table signature
86 * @v index Requested index of table with this signature
87 * @ret table Table, or UNULL if not found
88 */
89 userptr_t acpi_find ( uint32_t signature, unsigned int index ) {
90 struct acpi_header acpi;
91 struct acpi_rsdt *rsdtab;
92 typeof ( rsdtab->entry[0] ) entry;
93 userptr_t rsdt;
94 userptr_t table;
95 size_t len;
96 unsigned int count;
97 unsigned int i;
98
99 /* Locate RSDT */
100 rsdt = acpi_find_rsdt();
101 if ( ! rsdt ) {
102 DBG ( "RSDT not found\n" );
103 return UNULL;
104 }
105
106 /* Read RSDT header */
107 copy_from_user ( &acpi, rsdt, 0, sizeof ( acpi ) );
108 if ( acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) {
109 DBGC ( rsdt, "RSDT %#08lx has invalid signature:\n",
110 user_to_phys ( rsdt, 0 ) );
111 DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi,
112 sizeof ( acpi ) );
113 return UNULL;
114 }
115 len = le32_to_cpu ( acpi.length );
116 if ( len < sizeof ( rsdtab->acpi ) ) {
117 DBGC ( rsdt, "RSDT %#08lx has invalid length:\n",
118 user_to_phys ( rsdt, 0 ) );
119 DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi,
120 sizeof ( acpi ) );
121 return UNULL;
122 }
123
124 /* Calculate number of entries */
125 count = ( ( len - sizeof ( rsdtab->acpi ) ) / sizeof ( entry ) );
126
127 /* Search through entries */
128 for ( i = 0 ; i < count ; i++ ) {
129
130 /* Get table address */
131 copy_from_user ( &entry, rsdt,
132 offsetof ( typeof ( *rsdtab ), entry[i] ),
133 sizeof ( entry ) );
134
135 /* Read table header */
136 table = phys_to_user ( entry );
137 copy_from_user ( &acpi.signature, table, 0,
138 sizeof ( acpi.signature ) );
139
140 /* Check table signature */
141 if ( acpi.signature != cpu_to_le32 ( signature ) )
142 continue;
143
144 /* Check index */
145 if ( index-- )
146 continue;
147
148 /* Check table integrity */
149 if ( acpi_checksum ( table ) != 0 ) {
150 DBGC ( rsdt, "RSDT %#08lx found %s with bad checksum "
151 "at %08lx\n", user_to_phys ( rsdt, 0 ),
152 acpi_name ( signature ),
153 user_to_phys ( table, 0 ) );
154 break;
155 }
156
157 DBGC ( rsdt, "RSDT %#08lx found %s at %08lx\n",
158 user_to_phys ( rsdt, 0 ), acpi_name ( signature ),
159 user_to_phys ( table, 0 ) );
160 return table;
161 }
162
163 DBGC ( rsdt, "RSDT %#08lx could not find %s\n",
164 user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
165 return UNULL;
166 }
167
168 /**
169 * Extract \_Sx value from DSDT/SSDT
170 *
171 * @v zsdt DSDT or SSDT
172 * @v signature Signature (e.g. "_S5_")
173 * @ret sx \_Sx value, or negative error
174 *
175 * In theory, extracting the \_Sx value from the DSDT/SSDT requires a
176 * full ACPI parser plus some heuristics to work around the various
177 * broken encodings encountered in real ACPI implementations.
178 *
179 * In practice, we can get the same result by scanning through the
180 * DSDT/SSDT for the signature (e.g. "_S5_"), extracting the first
181 * four bytes, removing any bytes with bit 3 set, and treating
182 * whatever is left as a little-endian value. This is one of the
183 * uglier hacks I have ever implemented, but it's still prettier than
184 * the ACPI specification itself.
185 */
186 static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
187 struct acpi_header acpi;
188 union {
189 uint32_t dword;
190 uint8_t byte[4];
191 } buf;
192 size_t offset;
193 size_t len;
194 unsigned int sx;
195 uint8_t *byte;
196
197 /* Read table header */
198 copy_from_user ( &acpi, zsdt, 0, sizeof ( acpi ) );
199 len = le32_to_cpu ( acpi.length );
200
201 /* Locate signature */
202 for ( offset = sizeof ( acpi ) ;
203 ( ( offset + sizeof ( buf ) /* signature */ + 3 /* pkg header */
204 + sizeof ( buf ) /* value */ ) < len ) ;
205 offset++ ) {
206
207 /* Check signature */
208 copy_from_user ( &buf, zsdt, offset, sizeof ( buf ) );
209 if ( buf.dword != cpu_to_le32 ( signature ) )
210 continue;
211 DBGC ( zsdt, "DSDT/SSDT %#08lx found %s at offset %#zx\n",
212 user_to_phys ( zsdt, 0 ), acpi_name ( signature ),
213 offset );
214 offset += sizeof ( buf );
215
216 /* Read first four bytes of value */
217 copy_from_user ( &buf, zsdt, ( offset + 3 /* pkg header */ ),
218 sizeof ( buf ) );
219 DBGC ( zsdt, "DSDT/SSDT %#08lx found %s containing "
220 "%02x:%02x:%02x:%02x\n", user_to_phys ( zsdt, 0 ),
221 acpi_name ( signature ), buf.byte[0], buf.byte[1],
222 buf.byte[2], buf.byte[3] );
223
224 /* Extract \Sx value. There are three potential
225 * encodings that we might encounter:
226 *
227 * - SLP_TYPa, SLP_TYPb, rsvd, rsvd
228 *
229 * - <byteprefix>, SLP_TYPa, <byteprefix>, SLP_TYPb, ...
230 *
231 * - <dwordprefix>, SLP_TYPa, SLP_TYPb, 0, 0
232 *
233 * Since <byteprefix> and <dwordprefix> both have bit
234 * 3 set, and valid SLP_TYPx must have bit 3 clear
235 * (since SLP_TYPx is a 3-bit field), we can just skip
236 * any bytes with bit 3 set.
237 */
238 byte = &buf.byte[0];
239 if ( *byte & 0x08 )
240 byte++;
241 sx = *(byte++);
242 if ( *byte & 0x08 )
243 byte++;
244 sx |= ( *byte << 8 );
245 return sx;
246 }
247
248 return -ENOENT;
249 }
250
251 /**
252 * Extract \_Sx value from DSDT/SSDT
253 *
254 * @v signature Signature (e.g. "_S5_")
255 * @ret sx \_Sx value, or negative error
256 */
257 int acpi_sx ( uint32_t signature ) {
258 struct acpi_fadt fadtab;
259 userptr_t rsdt;
260 userptr_t fadt;
261 userptr_t dsdt;
262 userptr_t ssdt;
263 unsigned int i;
264 int sx;
265
266 /* Locate RSDT */
267 rsdt = acpi_find_rsdt();
268 if ( ! rsdt ) {
269 DBG ( "RSDT not found\n" );
270 return -ENOENT;
271 }
272
273 /* Try DSDT first */
274 fadt = acpi_find ( FADT_SIGNATURE, 0 );
275 if ( fadt ) {
276 copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) );
277 dsdt = phys_to_user ( fadtab.dsdt );
278 if ( ( sx = acpi_sx_zsdt ( dsdt, signature ) ) >= 0 )
279 return sx;
280 }
281
282 /* Try all SSDTs */
283 for ( i = 0 ; ; i++ ) {
284 ssdt = acpi_find ( SSDT_SIGNATURE, i );
285 if ( ! ssdt )
286 break;
287 if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 )
288 return sx;
289 }
290
291 DBGC ( rsdt, "RSDT %#08lx could not find \\_Sx \"%s\"\n",
292 user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
293 return -ENOENT;
294 }
295
296 /******************************************************************************
297 *
298 * Descriptors
299 *
300 ******************************************************************************
301 */
302
303 /**
304 * Add ACPI descriptor
305 *
306 * @v desc ACPI descriptor
307 */
308 void acpi_add ( struct acpi_descriptor *desc ) {
309
310 /* Add to list of descriptors */
311 ref_get ( desc->refcnt );
312 list_add_tail ( &desc->list, &desc->model->descs );
313 }
314
315 /**
316 * Remove ACPI descriptor
317 *
318 * @v desc ACPI descriptor
319 */
320 void acpi_del ( struct acpi_descriptor *desc ) {
321
322 /* Remove from list of descriptors */
323 list_check_contains_entry ( desc, &desc->model->descs, list );
324 list_del ( &desc->list );
325 ref_put ( desc->refcnt );
326 }
327
328 /**
329 * Get object's ACPI descriptor
330 *
331 * @v intf Interface
332 * @ret desc ACPI descriptor, or NULL
333 */
334 struct acpi_descriptor * acpi_describe ( struct interface *intf ) {
335 struct interface *dest;
336 acpi_describe_TYPE ( void * ) *op =
337 intf_get_dest_op ( intf, acpi_describe, &dest );
338 void *object = intf_object ( dest );
339 struct acpi_descriptor *desc;
340
341 if ( op ) {
342 desc = op ( object );
343 } else {
344 desc = NULL;
345 }
346
347 intf_put ( dest );
348 return desc;
349 }
350
351 /**
352 * Install ACPI tables
353 *
354 * @v install Table installation method
355 * @ret rc Return status code
356 */
357 int acpi_install ( int ( * install ) ( struct acpi_header *acpi ) ){
358 struct acpi_model *model;
359 int rc;
360
361 for_each_table_entry ( model, ACPI_MODELS ) {
362 if ( ( rc = model->install ( install ) ) != 0 )
363 return rc;
364 }
365
366 return 0;
367 }