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.

cpuid_settings.c 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * Copyright (C) 2013 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 <string.h>
  25. #include <errno.h>
  26. #include <byteswap.h>
  27. #include <ipxe/init.h>
  28. #include <ipxe/settings.h>
  29. #include <ipxe/cpuid.h>
  30. /** @file
  31. *
  32. * x86 CPUID settings
  33. *
  34. * CPUID settings are numerically encoded as:
  35. *
  36. * Bit 31 Extended function
  37. * Bits 30-28 Unused
  38. * Bits 27-24 Number of consecutive functions to call, minus one
  39. * Bit 23 Return result as little-endian (used for strings)
  40. * Bits 22-18 Unused
  41. * Bits 17-16 Number of registers in register array, minus one
  42. * Bits 15-8 Array of register indices. First entry in array is in
  43. * bits 9-8. Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx.
  44. * Bits 7-0 Starting function number (excluding "extended" bit)
  45. *
  46. * This encoding scheme is designed to allow the common case of
  47. * extracting a single register from a single function to be encoded
  48. * using "cpuid/<register>.<function>", e.g. "cpuid/2.0x80000001" to
  49. * retrieve the value of %ecx from calling CPUID with %eax=0x80000001.
  50. */
  51. /** CPUID setting tag register indices */
  52. enum cpuid_registers {
  53. CPUID_EAX = 0,
  54. CPUID_EBX = 1,
  55. CPUID_ECX = 2,
  56. CPUID_EDX = 3,
  57. };
  58. /**
  59. * Construct CPUID setting tag
  60. *
  61. * @v function Starting function number
  62. * @v num_functions Number of consecutive functions
  63. * @v little_endian Return result as little-endian
  64. * @v num_registers Number of registers in register array
  65. * @v register1 First register in register array (or zero, if empty)
  66. * @v register2 Second register in register array (or zero, if empty)
  67. * @v register3 Third register in register array (or zero, if empty)
  68. * @v register4 Fourth register in register array (or zero, if empty)
  69. * @ret tag Setting tag
  70. */
  71. #define CPUID_TAG( function, num_functions, little_endian, num_registers, \
  72. register1, register2, register3, register4 ) \
  73. ( (function) | ( ( (num_functions) - 1 ) << 24 ) | \
  74. ( (little_endian) << 23 ) | ( ( (num_registers) - 1) << 16 ) | \
  75. ( (register1) << 8 ) | ( (register2) << 10 ) | \
  76. ( (register3) << 12 ) | ( (register4) << 14 ) )
  77. /**
  78. * Extract endianness from CPUID setting tag
  79. *
  80. * @v tag Setting tag
  81. * @ret little_endian Result should be returned as little-endian
  82. */
  83. #define CPUID_LITTLE_ENDIAN( tag ) ( (tag) & 0x00800000UL )
  84. /**
  85. * Extract starting function number from CPUID setting tag
  86. *
  87. * @v tag Setting tag
  88. * @ret function Starting function number
  89. */
  90. #define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL )
  91. /**
  92. * Extract number of consecutive functions from CPUID setting tag
  93. *
  94. * @v tag Setting tag
  95. * @ret num_functions Number of consecutive functions
  96. */
  97. #define CPUID_NUM_FUNCTIONS( tag ) ( ( ( (tag) >> 24 ) & 0xf ) + 1 )
  98. /**
  99. * Extract register array from CPUID setting tag
  100. *
  101. * @v tag Setting tag
  102. * @ret registers Register array
  103. */
  104. #define CPUID_REGISTERS( tag ) ( ( (tag) >> 8 ) & 0xff )
  105. /**
  106. * Extract number of registers from CPUID setting tag
  107. *
  108. * @v tag Setting tag
  109. * @ret num_registers Number of registers within register array
  110. */
  111. #define CPUID_NUM_REGISTERS( tag ) ( ( ( (tag) >> 16 ) & 0x3 ) + 1 )
  112. /** CPUID settings scope */
  113. static const struct settings_scope cpuid_settings_scope;
  114. /**
  115. * Check applicability of CPUID setting
  116. *
  117. * @v settings Settings block
  118. * @v setting Setting
  119. * @ret applies Setting applies within this settings block
  120. */
  121. static int cpuid_settings_applies ( struct settings *settings __unused,
  122. const struct setting *setting ) {
  123. return ( setting->scope == &cpuid_settings_scope );
  124. }
  125. /**
  126. * Fetch value of CPUID setting
  127. *
  128. * @v settings Settings block
  129. * @v setting Setting to fetch
  130. * @v data Buffer to fill with setting data
  131. * @v len Length of buffer
  132. * @ret len Length of setting data, or negative error
  133. */
  134. static int cpuid_settings_fetch ( struct settings *settings,
  135. struct setting *setting,
  136. void *data, size_t len ) {
  137. uint32_t function;
  138. uint32_t max_function;
  139. uint32_t num_functions;
  140. uint32_t registers;
  141. uint32_t num_registers;
  142. uint32_t buf[4];
  143. uint32_t output;
  144. uint32_t discard_b;
  145. uint32_t discard_c;
  146. uint32_t discard_d;
  147. size_t frag_len;
  148. size_t result_len = 0;
  149. /* Fail unless CPUID is supported */
  150. if ( ! cpuid_is_supported() ) {
  151. DBGC ( settings, "CPUID not supported\n" );
  152. return -ENOTSUP;
  153. }
  154. /* Find highest supported function number within this set */
  155. function = CPUID_FUNCTION ( setting->tag );
  156. cpuid ( function & CPUID_EXTENDED, &max_function, &discard_b,
  157. &discard_c, &discard_d );
  158. /* Fail if maximum function number is meaningless (e.g. if we
  159. * are attempting to call an extended function on a CPU which
  160. * does not support them).
  161. */
  162. if ( ( max_function & CPUID_AMD_CHECK_MASK ) !=
  163. ( function & CPUID_AMD_CHECK_MASK ) ) {
  164. DBGC ( settings, "CPUID invalid maximum function\n" );
  165. return -ENOTSUP;
  166. }
  167. /* Call each function in turn */
  168. num_functions = CPUID_NUM_FUNCTIONS ( setting->tag );
  169. for ( ; num_functions-- ; function++ ) {
  170. /* Fail if this function is not supported */
  171. if ( function > max_function ) {
  172. DBGC ( settings, "CPUID function %#08x not supported\n",
  173. function );
  174. return -ENOTSUP;
  175. }
  176. /* Issue CPUID */
  177. cpuid ( function, &buf[CPUID_EAX], &buf[CPUID_EBX],
  178. &buf[CPUID_ECX], &buf[CPUID_EDX] );
  179. DBGC ( settings, "CPUID %#08x => %#08x:%#08x:%#08x:%#08x\n",
  180. function, buf[0], buf[1], buf[2], buf[3] );
  181. /* Copy results to buffer */
  182. registers = CPUID_REGISTERS ( setting->tag );
  183. num_registers = CPUID_NUM_REGISTERS ( setting->tag );
  184. for ( ; num_registers-- ; registers >>= 2 ) {
  185. output = buf[ registers & 0x3 ];
  186. if ( ! CPUID_LITTLE_ENDIAN ( setting->tag ) )
  187. output = cpu_to_be32 ( output );
  188. frag_len = sizeof ( output );
  189. if ( frag_len > len )
  190. frag_len = len;
  191. memcpy ( data, &output, frag_len );
  192. data += frag_len;
  193. len -= frag_len;
  194. result_len += sizeof ( output );
  195. }
  196. }
  197. /* Set type if not already specified */
  198. if ( ! setting->type )
  199. setting->type = &setting_type_hexraw;
  200. return result_len;
  201. }
  202. /** CPUID settings operations */
  203. static struct settings_operations cpuid_settings_operations = {
  204. .applies = cpuid_settings_applies,
  205. .fetch = cpuid_settings_fetch,
  206. };
  207. /** CPUID settings */
  208. static struct settings cpuid_settings = {
  209. .refcnt = NULL,
  210. .siblings = LIST_HEAD_INIT ( cpuid_settings.siblings ),
  211. .children = LIST_HEAD_INIT ( cpuid_settings.children ),
  212. .op = &cpuid_settings_operations,
  213. .default_scope = &cpuid_settings_scope,
  214. };
  215. /** Initialise CPUID settings */
  216. static void cpuid_settings_init ( void ) {
  217. int rc;
  218. if ( ( rc = register_settings ( &cpuid_settings, NULL,
  219. "cpuid" ) ) != 0 ) {
  220. DBG ( "CPUID could not register settings: %s\n",
  221. strerror ( rc ) );
  222. return;
  223. }
  224. }
  225. /** CPUID settings initialiser */
  226. struct init_fn cpuid_settings_init_fn __init_fn ( INIT_NORMAL ) = {
  227. .initialise = cpuid_settings_init,
  228. };
  229. /** CPU vendor setting */
  230. const struct setting cpuvendor_setting __setting ( SETTING_HOST_EXTRA,
  231. cpuvendor ) = {
  232. .name = "cpuvendor",
  233. .description = "CPU vendor",
  234. .tag = CPUID_TAG ( CPUID_VENDOR_ID, 1, 1, 3,
  235. CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ),
  236. .type = &setting_type_string,
  237. .scope = &cpuid_settings_scope,
  238. };
  239. /** CPU model setting */
  240. const struct setting cpumodel_setting __setting ( SETTING_HOST_EXTRA,
  241. cpumodel ) = {
  242. .name = "cpumodel",
  243. .description = "CPU model",
  244. .tag = CPUID_TAG ( CPUID_MODEL, 3, 1, 4,
  245. CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ),
  246. .type = &setting_type_string,
  247. .scope = &cpuid_settings_scope,
  248. };