You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

acpi.c 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  24. #include <errno.h>
  25. #include <byteswap.h>
  26. #include <ipxe/uaccess.h>
  27. #include <ipxe/acpi.h>
  28. #include <ipxe/interface.h>
  29. /** @file
  30. *
  31. * ACPI support functions
  32. *
  33. */
  34. /******************************************************************************
  35. *
  36. * Utility functions
  37. *
  38. ******************************************************************************
  39. */
  40. /**
  41. * Transcribe ACPI table signature (for debugging)
  42. *
  43. * @v signature ACPI table signature
  44. * @ret name ACPI table signature name
  45. */
  46. static const char * acpi_name ( uint32_t signature ) {
  47. static union {
  48. uint32_t signature;
  49. char name[5];
  50. } u;
  51. u.signature = cpu_to_le32 ( signature );
  52. return u.name;
  53. }
  54. /**
  55. * Fix up ACPI table checksum
  56. *
  57. * @v acpi ACPI table header
  58. */
  59. void acpi_fix_checksum ( struct acpi_description_header *acpi ) {
  60. unsigned int i = 0;
  61. uint8_t sum = 0;
  62. for ( i = 0 ; i < acpi->length ; i++ ) {
  63. sum += *( ( ( uint8_t * ) acpi ) + i );
  64. }
  65. acpi->checksum -= sum;
  66. }
  67. /**
  68. * Locate ACPI root system description table within a memory range
  69. *
  70. * @v start Start address to search
  71. * @v len Length to search
  72. * @ret rsdt ACPI root system description table, or UNULL
  73. */
  74. static userptr_t acpi_find_rsdt_range ( userptr_t start, size_t len ) {
  75. static const char signature[8] = RSDP_SIGNATURE;
  76. struct acpi_rsdp rsdp;
  77. userptr_t rsdt;
  78. size_t offset;
  79. uint8_t sum;
  80. unsigned int i;
  81. /* Search for RSDP */
  82. for ( offset = 0 ; ( ( offset + sizeof ( rsdp ) ) < len ) ;
  83. offset += RSDP_STRIDE ) {
  84. /* Check signature and checksum */
  85. copy_from_user ( &rsdp, start, offset, sizeof ( rsdp ) );
  86. if ( memcmp ( rsdp.signature, signature,
  87. sizeof ( signature ) ) != 0 )
  88. continue;
  89. for ( sum = 0, i = 0 ; i < sizeof ( rsdp ) ; i++ )
  90. sum += *( ( ( uint8_t * ) &rsdp ) + i );
  91. if ( sum != 0 )
  92. continue;
  93. /* Extract RSDT */
  94. rsdt = phys_to_user ( le32_to_cpu ( rsdp.rsdt ) );
  95. DBGC ( rsdt, "RSDT %#08lx found via RSDP %#08lx\n",
  96. user_to_phys ( rsdt, 0 ),
  97. user_to_phys ( start, offset ) );
  98. return rsdt;
  99. }
  100. return UNULL;
  101. }
  102. /**
  103. * Locate ACPI root system description table
  104. *
  105. * @v ebda Extended BIOS data area, or UNULL
  106. * @ret rsdt ACPI root system description table, or UNULL
  107. */
  108. userptr_t acpi_find_rsdt ( userptr_t ebda ) {
  109. userptr_t rsdt;
  110. /* Search EBDA, if applicable */
  111. if ( ebda ) {
  112. rsdt = acpi_find_rsdt_range ( ebda, RSDP_EBDA_LEN );
  113. if ( rsdt )
  114. return rsdt;
  115. }
  116. /* Search fixed BIOS area */
  117. rsdt = acpi_find_rsdt_range ( phys_to_user ( RSDP_BIOS_START ),
  118. RSDP_BIOS_LEN );
  119. if ( rsdt )
  120. return rsdt;
  121. return UNULL;
  122. }
  123. /**
  124. * Locate ACPI table
  125. *
  126. * @v rsdt ACPI root system description table
  127. * @v signature Requested table signature
  128. * @v index Requested index of table with this signature
  129. * @ret table Table, or UNULL if not found
  130. */
  131. userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, unsigned int index ) {
  132. struct acpi_description_header acpi;
  133. struct acpi_rsdt *rsdtab;
  134. typeof ( rsdtab->entry[0] ) entry;
  135. userptr_t table;
  136. size_t len;
  137. unsigned int count;
  138. unsigned int i;
  139. /* Read RSDT header */
  140. copy_from_user ( &acpi, rsdt, 0, sizeof ( acpi ) );
  141. if ( acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) {
  142. DBGC ( rsdt, "RSDT %#08lx has invalid signature:\n",
  143. user_to_phys ( rsdt, 0 ) );
  144. DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi,
  145. sizeof ( acpi ) );
  146. return UNULL;
  147. }
  148. len = le32_to_cpu ( acpi.length );
  149. if ( len < sizeof ( rsdtab->acpi ) ) {
  150. DBGC ( rsdt, "RSDT %#08lx has invalid length:\n",
  151. user_to_phys ( rsdt, 0 ) );
  152. DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi,
  153. sizeof ( acpi ) );
  154. return UNULL;
  155. }
  156. /* Calculate number of entries */
  157. count = ( ( len - sizeof ( rsdtab->acpi ) ) / sizeof ( entry ) );
  158. /* Search through entries */
  159. for ( i = 0 ; i < count ; i++ ) {
  160. /* Get table address */
  161. copy_from_user ( &entry, rsdt,
  162. offsetof ( typeof ( *rsdtab ), entry[i] ),
  163. sizeof ( entry ) );
  164. /* Read table header */
  165. table = phys_to_user ( entry );
  166. copy_from_user ( &acpi.signature, table, 0,
  167. sizeof ( acpi.signature ) );
  168. /* Check table signature */
  169. if ( acpi.signature != cpu_to_le32 ( signature ) )
  170. continue;
  171. /* Check index */
  172. if ( index-- )
  173. continue;
  174. DBGC ( rsdt, "RSDT %#08lx found %s at %08lx\n",
  175. user_to_phys ( rsdt, 0 ), acpi_name ( signature ),
  176. user_to_phys ( table, 0 ) );
  177. return table;
  178. }
  179. DBGC ( rsdt, "RSDT %#08lx could not find %s\n",
  180. user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
  181. return UNULL;
  182. }
  183. /**
  184. * Extract \_Sx value from DSDT/SSDT
  185. *
  186. * @v zsdt DSDT or SSDT
  187. * @v signature Signature (e.g. "_S5_")
  188. * @ret sx \_Sx value, or negative error
  189. *
  190. * In theory, extracting the \_Sx value from the DSDT/SSDT requires a
  191. * full ACPI parser plus some heuristics to work around the various
  192. * broken encodings encountered in real ACPI implementations.
  193. *
  194. * In practice, we can get the same result by scanning through the
  195. * DSDT/SSDT for the signature (e.g. "_S5_"), extracting the first
  196. * four bytes, removing any bytes with bit 3 set, and treating
  197. * whatever is left as a little-endian value. This is one of the
  198. * uglier hacks I have ever implemented, but it's still prettier than
  199. * the ACPI specification itself.
  200. */
  201. static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
  202. struct acpi_description_header acpi;
  203. union {
  204. uint32_t dword;
  205. uint8_t byte[4];
  206. } buf;
  207. size_t offset;
  208. size_t len;
  209. unsigned int sx;
  210. uint8_t *byte;
  211. /* Read table header */
  212. copy_from_user ( &acpi, zsdt, 0, sizeof ( acpi ) );
  213. len = le32_to_cpu ( acpi.length );
  214. /* Locate signature */
  215. for ( offset = sizeof ( acpi ) ;
  216. ( ( offset + sizeof ( buf ) /* signature */ + 3 /* pkg header */
  217. + sizeof ( buf ) /* value */ ) < len ) ;
  218. offset++ ) {
  219. /* Check signature */
  220. copy_from_user ( &buf, zsdt, offset, sizeof ( buf ) );
  221. if ( buf.dword != cpu_to_le32 ( signature ) )
  222. continue;
  223. DBGC ( zsdt, "DSDT/SSDT %#08lx found %s at offset %#zx\n",
  224. user_to_phys ( zsdt, 0 ), acpi_name ( signature ),
  225. offset );
  226. offset += sizeof ( buf );
  227. /* Read first four bytes of value */
  228. copy_from_user ( &buf, zsdt, ( offset + 3 /* pkg header */ ),
  229. sizeof ( buf ) );
  230. DBGC ( zsdt, "DSDT/SSDT %#08lx found %s containing "
  231. "%02x:%02x:%02x:%02x\n", user_to_phys ( zsdt, 0 ),
  232. acpi_name ( signature ), buf.byte[0], buf.byte[1],
  233. buf.byte[2], buf.byte[3] );
  234. /* Extract \Sx value. There are three potential
  235. * encodings that we might encounter:
  236. *
  237. * - SLP_TYPa, SLP_TYPb, rsvd, rsvd
  238. *
  239. * - <byteprefix>, SLP_TYPa, <byteprefix>, SLP_TYPb, ...
  240. *
  241. * - <dwordprefix>, SLP_TYPa, SLP_TYPb, 0, 0
  242. *
  243. * Since <byteprefix> and <dwordprefix> both have bit
  244. * 3 set, and valid SLP_TYPx must have bit 3 clear
  245. * (since SLP_TYPx is a 3-bit field), we can just skip
  246. * any bytes with bit 3 set.
  247. */
  248. byte = &buf.byte[0];
  249. if ( *byte & 0x08 )
  250. byte++;
  251. sx = *(byte++);
  252. if ( *byte & 0x08 )
  253. byte++;
  254. sx |= ( *byte << 8 );
  255. return sx;
  256. }
  257. return -ENOENT;
  258. }
  259. /**
  260. * Extract \_Sx value from DSDT/SSDT
  261. *
  262. * @v rsdt ACPI root system description table
  263. * @v signature Signature (e.g. "_S5_")
  264. * @ret sx \_Sx value, or negative error
  265. */
  266. int acpi_sx ( userptr_t rsdt, uint32_t signature ) {
  267. struct acpi_fadt fadtab;
  268. userptr_t fadt;
  269. userptr_t dsdt;
  270. userptr_t ssdt;
  271. unsigned int i;
  272. int sx;
  273. /* Try DSDT first */
  274. fadt = acpi_find ( rsdt, 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. /* Try all SSDTs */
  282. for ( i = 0 ; ; i++ ) {
  283. ssdt = acpi_find ( rsdt, SSDT_SIGNATURE, i );
  284. if ( ! ssdt )
  285. break;
  286. if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 )
  287. return sx;
  288. }
  289. DBGC ( rsdt, "RSDT %#08lx could not find \\_Sx \"%s\"\n",
  290. user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
  291. return -ENOENT;
  292. }
  293. /******************************************************************************
  294. *
  295. * Interface methods
  296. *
  297. ******************************************************************************
  298. */
  299. /**
  300. * Describe object in an ACPI table
  301. *
  302. * @v intf Interface
  303. * @v acpi ACPI table
  304. * @v len Length of ACPI table
  305. * @ret rc Return status code
  306. */
  307. int acpi_describe ( struct interface *intf,
  308. struct acpi_description_header *acpi, size_t len ) {
  309. struct interface *dest;
  310. acpi_describe_TYPE ( void * ) *op =
  311. intf_get_dest_op ( intf, acpi_describe, &dest );
  312. void *object = intf_object ( dest );
  313. int rc;
  314. if ( op ) {
  315. rc = op ( object, acpi, len );
  316. } else {
  317. /* Default is to fail to describe */
  318. rc = -EOPNOTSUPP;
  319. }
  320. intf_put ( dest );
  321. return rc;
  322. }