mirror of https://github.com/ipxe/ipxe.git
[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>pull/64/head
parent
c8cb867d65
commit
a6a5825f8d
|
@ -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 );
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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" );
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 );
|
||||
|
|
Loading…
Reference in New Issue