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