]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[cpuid] Allow input %ecx value to be specified
authorMichael Brown <mcb30@ipxe.org>
Thu, 15 Jun 2017 13:50:20 +0000 (14:50 +0100)
committerMichael Brown <mcb30@ipxe.org>
Fri, 16 Jun 2017 00:17:48 +0000 (01:17 +0100)
For some CPUID leaves (e.g. %eax=0x00000004), the result depends on
the input value of %ecx.  Allow this subfunction number to be
specified as a parameter to the cpuid() wrapper.

The subfunction number is exposed via the ${cpuid/...} settings
mechanism using the syntax

  ${cpuid/<subfunction>.0x40.<register>.<function>}

e.g.

  ${cpuid/0.0x40.0.0x0000000b}
  ${cpuid/1.0x40.0.0x0000000b}

to retrieve the CPU topology information.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/x86/core/cpuid.c
src/arch/x86/core/cpuid_settings.c
src/arch/x86/core/rdtsc_timer.c
src/arch/x86/drivers/hyperv/hyperv.c
src/arch/x86/drivers/xen/hvm.c
src/arch/x86/include/ipxe/cpuid.h

index 864c1035d824fc2c632f7fa26ab7954db6491d2b..1a7c93e837b597878053b5b801a1d55b376d48c2 100644 (file)
@@ -84,7 +84,7 @@ int cpuid_supported ( uint32_t function ) {
                return rc;
 
        /* Find highest supported function number within this family */
-       cpuid ( ( function & CPUID_EXTENDED ), &max_function, &discard_b,
+       cpuid ( ( function & CPUID_EXTENDED ), 0, &max_function, &discard_b,
                &discard_c, &discard_d );
 
        /* Fail if maximum function number is meaningless (e.g. if we
@@ -125,7 +125,7 @@ static void x86_intel_features ( struct x86_features *features ) {
        }
 
        /* Get features */
-       cpuid ( CPUID_FEATURES, &discard_a, &discard_b,
+       cpuid ( CPUID_FEATURES, 0, &discard_a, &discard_b,
                &features->intel.ecx, &features->intel.edx );
        DBGC ( features, "CPUID Intel features: %%ecx=%08x, %%edx=%08x\n",
               features->intel.ecx, features->intel.edx );
@@ -149,7 +149,7 @@ static void x86_amd_features ( struct x86_features *features ) {
        }
 
        /* Get features */
-       cpuid ( CPUID_AMD_FEATURES, &discard_a, &discard_b,
+       cpuid ( CPUID_AMD_FEATURES, 0, &discard_a, &discard_b,
                &features->amd.ecx, &features->amd.edx );
        DBGC ( features, "CPUID AMD features: %%ecx=%08x, %%edx=%08x\n",
               features->amd.ecx, features->amd.edx );
index 306dbefb86d1fcfebcc80316ce7633d76ba52025..0b67ee91dba244d12fd1068e13669e1733f04f66 100644 (file)
@@ -37,10 +37,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  * CPUID settings are numerically encoded as:
  *
  *  Bit  31    Extended function
- *  Bits 30-28 Unused
- *  Bits 27-24 Number of consecutive functions to call, minus one
+ *  Bits 30-24 (bit 22 = 1) Subfunction number
+ *             (bit 22 = 0) Number of consecutive functions to call, minus one
  *  Bit  23    Return result as little-endian (used for strings)
- *  Bits 22-18 Unused
+ *  Bit  22    Interpret bits 30-24 as a subfunction number
+ *  Bits 21-18 Unused
  *  Bits 17-16 Number of registers in register array, minus one
  *  Bits 15-8  Array of register indices.  First entry in array is in
  *             bits 9-8.  Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx.
@@ -50,6 +51,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  * extracting a single register from a single function to be encoded
  * using "cpuid/<register>.<function>", e.g. "cpuid/2.0x80000001" to
  * retrieve the value of %ecx from calling CPUID with %eax=0x80000001.
+ *
+ * A subfunction (i.e. an input value for %ecx) may be specified using
+ * "cpuid/<subfunction>.0x40.<register>.<function>".  This slightly
+ * cumbersome syntax is required in order to maintain backwards
+ * compatibility with older scripts.
  */
 
 /** CPUID setting tag register indices */
@@ -60,12 +66,18 @@ enum cpuid_registers {
        CPUID_EDX = 3,
 };
 
+/** CPUID setting tag flags */
+enum cpuid_flags {
+       CPUID_LITTLE_ENDIAN = 0x00800000UL,
+       CPUID_USE_SUBFUNCTION = 0x00400000UL,
+};
+
 /**
  * Construct CPUID setting tag
  *
  * @v function         Starting function number
- * @v num_functions    Number of consecutive functions
- * @v little_endian    Return result as little-endian
+ * @v subfunction      Subfunction, or number of consecutive functions minus 1
+ * @v flags            Flags
  * @v num_registers    Number of registers in register array
  * @v register1                First register in register array (or zero, if empty)
  * @v register2                Second register in register array (or zero, if empty)
@@ -73,21 +85,13 @@ enum cpuid_registers {
  * @v register4                Fourth register in register array (or zero, if empty)
  * @ret tag            Setting tag
  */
-#define CPUID_TAG( function, num_functions, little_endian, num_registers, \
-                  register1, register2, register3, register4 )           \
-       ( (function) | ( ( (num_functions) - 1 ) << 24 ) |                \
-         ( (little_endian) << 23 ) | ( ( (num_registers) - 1) << 16 ) |  \
-         ( (register1) << 8 ) | ( (register2) << 10 ) |                  \
+#define CPUID_TAG( function, subfunction, flags, num_registers,                \
+                  register1, register2, register3, register4 )         \
+       ( (function) | ( (subfunction) << 24 ) | (flags) |              \
+         ( ( (num_registers) - 1 ) << 16 ) |                           \
+         ( (register1) << 8 ) | ( (register2) << 10 ) |                \
          ( (register3) << 12 ) | ( (register4) << 14 ) )
 
-/**
- * Extract endianness from CPUID setting tag
- *
- * @v tag              Setting tag
- * @ret little_endian  Result should be returned as little-endian
- */
-#define CPUID_LITTLE_ENDIAN( tag ) ( (tag) & 0x00800000UL )
-
 /**
  * Extract starting function number from CPUID setting tag
  *
@@ -97,12 +101,12 @@ enum cpuid_registers {
 #define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL )
 
 /**
- * Extract number of consecutive functions from CPUID setting tag
+ * Extract subfunction number from CPUID setting tag
  *
  * @v tag              Setting tag
- * @ret num_functions  Number of consecutive functions
+ * @ret subfunction    Subfunction number
  */
-#define CPUID_NUM_FUNCTIONS( tag ) ( ( ( (tag) >> 24 ) & 0xf ) + 1 )
+#define CPUID_SUBFUNCTION( tag ) ( ( (tag) >> 24 ) & 0x7f )
 
 /**
  * Extract register array from CPUID setting tag
@@ -149,6 +153,7 @@ static int cpuid_settings_fetch ( struct settings *settings,
                                  struct setting *setting,
                                  void *data, size_t len ) {
        uint32_t function;
+       uint32_t subfunction;
        uint32_t num_functions;
        uint32_t registers;
        uint32_t num_registers;
@@ -160,7 +165,13 @@ static int cpuid_settings_fetch ( struct settings *settings,
 
        /* Call each function in turn */
        function = CPUID_FUNCTION ( setting->tag );
-       num_functions = CPUID_NUM_FUNCTIONS ( setting->tag );
+       subfunction = CPUID_SUBFUNCTION ( setting->tag );
+       if ( setting->tag & CPUID_USE_SUBFUNCTION ) {
+               num_functions = 1;
+       } else {
+               num_functions = ( subfunction + 1 );
+               subfunction = 0;
+       }
        for ( ; num_functions-- ; function++ ) {
 
                /* Fail if this function is not supported */
@@ -171,17 +182,17 @@ static int cpuid_settings_fetch ( struct settings *settings,
                }
 
                /* Issue CPUID */
-               cpuid ( function, &buf[CPUID_EAX], &buf[CPUID_EBX],
-                       &buf[CPUID_ECX], &buf[CPUID_EDX] );
-               DBGC ( settings, "CPUID %#08x => %#08x:%#08x:%#08x:%#08x\n",
-                      function, buf[0], buf[1], buf[2], buf[3] );
+               cpuid ( function, subfunction, &buf[CPUID_EAX],
+                       &buf[CPUID_EBX], &buf[CPUID_ECX], &buf[CPUID_EDX] );
+               DBGC ( settings, "CPUID %#08x:%x => %#08x:%#08x:%#08x:%#08x\n",
+                      function, subfunction, buf[0], buf[1], buf[2], buf[3] );
 
                /* Copy results to buffer */
                registers = CPUID_REGISTERS ( setting->tag );
                num_registers = CPUID_NUM_REGISTERS ( setting->tag );
                for ( ; num_registers-- ; registers >>= 2 ) {
                        output = buf[ registers & 0x3 ];
-                       if ( ! CPUID_LITTLE_ENDIAN ( setting->tag ) )
+                       if ( ! ( setting->tag & CPUID_LITTLE_ENDIAN ) )
                                output = cpu_to_be32 ( output );
                        frag_len = sizeof ( output );
                        if ( frag_len > len )
@@ -237,7 +248,7 @@ const struct setting cpuvendor_setting __setting ( SETTING_HOST_EXTRA,
                                                   cpuvendor ) = {
        .name = "cpuvendor",
        .description = "CPU vendor",
-       .tag = CPUID_TAG ( CPUID_VENDOR_ID, 1, 1, 3,
+       .tag = CPUID_TAG ( CPUID_VENDOR_ID, 0, CPUID_LITTLE_ENDIAN, 3,
                           CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ),
        .type = &setting_type_string,
        .scope = &cpuid_settings_scope,
@@ -248,7 +259,7 @@ const struct setting cpumodel_setting __setting ( SETTING_HOST_EXTRA,
                                                  cpumodel ) = {
        .name = "cpumodel",
        .description = "CPU model",
-       .tag = CPUID_TAG ( CPUID_MODEL, 3, 1, 4,
+       .tag = CPUID_TAG ( CPUID_MODEL, 2, CPUID_LITTLE_ENDIAN, 4,
                           CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ),
        .type = &setting_type_string,
        .scope = &cpuid_settings_scope,
index ed5151503f0d17d1dc0dad854fea8fcb907001de..bee5f1ca484fba07ae0cd3bd4772d47fe6e0d7c5 100644 (file)
@@ -132,7 +132,7 @@ static int rdtsc_probe ( void ) {
                       strerror ( rc ) );
                return rc;
        }
-       cpuid ( CPUID_APM, &discard_a, &discard_b, &discard_c, &apm );
+       cpuid ( CPUID_APM, 0, &discard_a, &discard_b, &discard_c, &apm );
        if ( ! ( apm & CPUID_APM_EDX_TSC_INVARIANT ) ) {
                DBGC ( colour, "RDTSC has non-invariant TSC (%#08x)\n",
                       apm );
index 98c2b30c051c7da6fc3d58b61bc75222fb419ad3..4e687687886a4e9fae85a622b5fb40feeb2fb2e7 100644 (file)
@@ -170,7 +170,7 @@ static int hv_check_hv ( void ) {
        }
 
        /* Check that hypervisor is Hyper-V */
-       cpuid ( HV_CPUID_INTERFACE_ID, &interface_id, &discard_ebx,
+       cpuid ( HV_CPUID_INTERFACE_ID, 0, &interface_id, &discard_ebx,
                &discard_ecx, &discard_edx );
        if ( interface_id != HV_INTERFACE_ID ) {
                DBGC ( HV_INTERFACE_ID, "HV not running in Hyper-V (interface "
@@ -194,7 +194,7 @@ static int hv_check_features ( struct hv_hypervisor *hv ) {
        uint32_t discard_edx;
 
        /* Check that required features and privileges are available */
-       cpuid ( HV_CPUID_FEATURES, &available, &permissions, &discard_ecx,
+       cpuid ( HV_CPUID_FEATURES, 0, &available, &permissions, &discard_ecx,
                &discard_edx );
        if ( ! ( available & HV_FEATURES_AVAIL_HYPERCALL_MSR ) ) {
                DBGC ( hv, "HV %p has no hypercall MSRs (features %08x:%08x)\n",
@@ -253,10 +253,10 @@ static void hv_map_hypercall ( struct hv_hypervisor *hv ) {
        wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
 
        /* Get hypervisor system identity (for debugging) */
-       cpuid ( HV_CPUID_VENDOR_ID, &discard_eax, &vendor_id.ebx,
+       cpuid ( HV_CPUID_VENDOR_ID, 0, &discard_eax, &vendor_id.ebx,
                &vendor_id.ecx, &vendor_id.edx );
        vendor_id.text[ sizeof ( vendor_id.text ) - 1 ] = '\0';
-       cpuid ( HV_CPUID_HYPERVISOR_ID, &build, &version, &discard_ecx,
+       cpuid ( HV_CPUID_HYPERVISOR_ID, 0, &build, &version, &discard_ecx,
                &discard_edx );
        DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv,
               vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build );
@@ -735,7 +735,7 @@ static int hv_timer_probe ( void ) {
                return rc;
 
        /* Check for available reference counter */
-       cpuid ( HV_CPUID_FEATURES, &available, &discard_ebx, &discard_ecx,
+       cpuid ( HV_CPUID_FEATURES, 0, &available, &discard_ebx, &discard_ecx,
                &discard_edx );
        if ( ! ( available & HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR ) ) {
                DBGC ( HV_INTERFACE_ID, "HV has no time reference counter\n" );
index 7ac32d54c3307bedff7980e04c687eecf6764eb1..57196f5553d26017aa6c33e6345d1abf875f98c8 100644 (file)
@@ -66,12 +66,12 @@ static int hvm_cpuid_base ( struct hvm_device *hvm ) {
        /* Scan for magic signature */
        for ( base = HVM_CPUID_MIN ; base <= HVM_CPUID_MAX ;
              base += HVM_CPUID_STEP ) {
-               cpuid ( base, &discard_eax, &signature.ebx, &signature.ecx,
+               cpuid ( base, 0, &discard_eax, &signature.ebx, &signature.ecx,
                        &signature.edx );
                if ( memcmp ( &signature, HVM_CPUID_MAGIC,
                              sizeof ( signature ) ) == 0 ) {
                        hvm->cpuid_base = base;
-                       cpuid ( ( base + HVM_CPUID_VERSION ), &version,
+                       cpuid ( ( base + HVM_CPUID_VERSION ), 0, &version,
                                &discard_ebx, &discard_ecx, &discard_edx );
                        DBGC2 ( hvm, "HVM using CPUID base %#08x (v%d.%d)\n",
                                base, ( version >> 16 ), ( version & 0xffff ) );
@@ -101,7 +101,7 @@ static int hvm_map_hypercall ( struct hvm_device *hvm ) {
        int rc;
 
        /* Get number of hypercall pages and MSR to use */
-       cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), &pages, &msr,
+       cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), 0, &pages, &msr,
                &discard_ecx, &discard_edx );
 
        /* Allocate pages */
index a9df9f0de579ab7314ba9525e2618cf083a54d55..0ae572da4558ef1156b99ea85c34ff42008eeee0 100644 (file)
@@ -66,19 +66,20 @@ struct x86_features {
 /**
  * Issue CPUID instruction
  *
- * @v function         CPUID function
+ * @v function         CPUID function (input via %eax)
+ * @v subfunction      CPUID subfunction (input via %ecx)
  * @v eax              Output via %eax
  * @v ebx              Output via %ebx
  * @v ecx              Output via %ecx
  * @v edx              Output via %edx
  */
 static inline __attribute__ (( always_inline )) void
-cpuid ( uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
-       uint32_t *edx ) {
+cpuid ( uint32_t function, uint32_t subfunction, uint32_t *eax, uint32_t *ebx,
+       uint32_t *ecx, uint32_t *edx ) {
 
        __asm__ ( "cpuid"
                  : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx )
-                 : "0" ( function ) );
+                 : "0" ( function ), "2" ( subfunction ) );
 }
 
 extern int cpuid_supported ( uint32_t function );