diff options
author | Michael Brown <mcb30@ipxe.org> | 2017-06-15 14:50:20 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2017-06-16 01:17:48 +0100 |
commit | a6a5825f8d658834610de31b7b9be2e7d0a8804e (patch) | |
tree | f51a8b343176bbf7c31483006060e93dc0161924 /src/arch | |
parent | c8cb867d655227d779ce8ce9e392e853997b01dd (diff) | |
download | ipxe-a6a5825f8d658834610de31b7b9be2e7d0a8804e.zip ipxe-a6a5825f8d658834610de31b7b9be2e7d0a8804e.tar.gz ipxe-a6a5825f8d658834610de31b7b9be2e7d0a8804e.tar.bz2 |
[cpuid] Allow input %ecx value to be specified
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>
Diffstat (limited to 'src/arch')
-rw-r--r-- | src/arch/x86/core/cpuid.c | 6 | ||||
-rw-r--r-- | src/arch/x86/core/cpuid_settings.c | 69 | ||||
-rw-r--r-- | src/arch/x86/core/rdtsc_timer.c | 2 | ||||
-rw-r--r-- | src/arch/x86/drivers/hyperv/hyperv.c | 10 | ||||
-rw-r--r-- | src/arch/x86/drivers/xen/hvm.c | 6 | ||||
-rw-r--r-- | src/arch/x86/include/ipxe/cpuid.h | 9 |
6 files changed, 57 insertions, 45 deletions
diff --git a/src/arch/x86/core/cpuid.c b/src/arch/x86/core/cpuid.c index 864c103..1a7c93e 100644 --- a/src/arch/x86/core/cpuid.c +++ b/src/arch/x86/core/cpuid.c @@ -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 ); diff --git a/src/arch/x86/core/cpuid_settings.c b/src/arch/x86/core/cpuid_settings.c index 306dbef..0b67ee9 100644 --- a/src/arch/x86/core/cpuid_settings.c +++ b/src/arch/x86/core/cpuid_settings.c @@ -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,22 +85,14 @@ 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 * * @v tag 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, diff --git a/src/arch/x86/core/rdtsc_timer.c b/src/arch/x86/core/rdtsc_timer.c index ed51515..bee5f1c 100644 --- a/src/arch/x86/core/rdtsc_timer.c +++ b/src/arch/x86/core/rdtsc_timer.c @@ -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 ); diff --git a/src/arch/x86/drivers/hyperv/hyperv.c b/src/arch/x86/drivers/hyperv/hyperv.c index 98c2b30..4e68768 100644 --- a/src/arch/x86/drivers/hyperv/hyperv.c +++ b/src/arch/x86/drivers/hyperv/hyperv.c @@ -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" ); diff --git a/src/arch/x86/drivers/xen/hvm.c b/src/arch/x86/drivers/xen/hvm.c index 7ac32d5..57196f5 100644 --- a/src/arch/x86/drivers/xen/hvm.c +++ b/src/arch/x86/drivers/xen/hvm.c @@ -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 */ diff --git a/src/arch/x86/include/ipxe/cpuid.h b/src/arch/x86/include/ipxe/cpuid.h index a9df9f0..0ae572d 100644 --- a/src/arch/x86/include/ipxe/cpuid.h +++ b/src/arch/x86/include/ipxe/cpuid.h @@ -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 ); |