Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

cpuid_settings.c 8.0KB

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