Browse Source

[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>
tags/v1.20.1
Michael Brown 7 years ago
parent
commit
a6a5825f8d

+ 3
- 3
src/arch/x86/core/cpuid.c View File

84
 		return rc;
84
 		return rc;
85
 
85
 
86
 	/* Find highest supported function number within this family */
86
 	/* Find highest supported function number within this family */
87
-	cpuid ( ( function & CPUID_EXTENDED ), &max_function, &discard_b,
87
+	cpuid ( ( function & CPUID_EXTENDED ), 0, &max_function, &discard_b,
88
 		&discard_c, &discard_d );
88
 		&discard_c, &discard_d );
89
 
89
 
90
 	/* Fail if maximum function number is meaningless (e.g. if we
90
 	/* Fail if maximum function number is meaningless (e.g. if we
125
 	}
125
 	}
126
 
126
 
127
 	/* Get features */
127
 	/* Get features */
128
-	cpuid ( CPUID_FEATURES, &discard_a, &discard_b,
128
+	cpuid ( CPUID_FEATURES, 0, &discard_a, &discard_b,
129
 		&features->intel.ecx, &features->intel.edx );
129
 		&features->intel.ecx, &features->intel.edx );
130
 	DBGC ( features, "CPUID Intel features: %%ecx=%08x, %%edx=%08x\n",
130
 	DBGC ( features, "CPUID Intel features: %%ecx=%08x, %%edx=%08x\n",
131
 	       features->intel.ecx, features->intel.edx );
131
 	       features->intel.ecx, features->intel.edx );
149
 	}
149
 	}
150
 
150
 
151
 	/* Get features */
151
 	/* Get features */
152
-	cpuid ( CPUID_AMD_FEATURES, &discard_a, &discard_b,
152
+	cpuid ( CPUID_AMD_FEATURES, 0, &discard_a, &discard_b,
153
 		&features->amd.ecx, &features->amd.edx );
153
 		&features->amd.ecx, &features->amd.edx );
154
 	DBGC ( features, "CPUID AMD features: %%ecx=%08x, %%edx=%08x\n",
154
 	DBGC ( features, "CPUID AMD features: %%ecx=%08x, %%edx=%08x\n",
155
 	       features->amd.ecx, features->amd.edx );
155
 	       features->amd.ecx, features->amd.edx );

+ 40
- 29
src/arch/x86/core/cpuid_settings.c View File

37
  * CPUID settings are numerically encoded as:
37
  * CPUID settings are numerically encoded as:
38
  *
38
  *
39
  *  Bit  31	Extended function
39
  *  Bit  31	Extended function
40
- *  Bits 30-28	Unused
41
- *  Bits 27-24	Number of consecutive functions to call, minus one
40
+ *  Bits 30-24	(bit 22 = 1) Subfunction number
41
+ *		(bit 22 = 0) Number of consecutive functions to call, minus one
42
  *  Bit  23	Return result as little-endian (used for strings)
42
  *  Bit  23	Return result as little-endian (used for strings)
43
- *  Bits 22-18	Unused
43
+ *  Bit  22	Interpret bits 30-24 as a subfunction number
44
+ *  Bits 21-18	Unused
44
  *  Bits 17-16	Number of registers in register array, minus one
45
  *  Bits 17-16	Number of registers in register array, minus one
45
  *  Bits 15-8	Array of register indices.  First entry in array is in
46
  *  Bits 15-8	Array of register indices.  First entry in array is in
46
  *		bits 9-8.  Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx.
47
  *		bits 9-8.  Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx.
50
  * extracting a single register from a single function to be encoded
51
  * extracting a single register from a single function to be encoded
51
  * using "cpuid/<register>.<function>", e.g. "cpuid/2.0x80000001" to
52
  * using "cpuid/<register>.<function>", e.g. "cpuid/2.0x80000001" to
52
  * retrieve the value of %ecx from calling CPUID with %eax=0x80000001.
53
  * retrieve the value of %ecx from calling CPUID with %eax=0x80000001.
54
+ *
55
+ * A subfunction (i.e. an input value for %ecx) may be specified using
56
+ * "cpuid/<subfunction>.0x40.<register>.<function>".  This slightly
57
+ * cumbersome syntax is required in order to maintain backwards
58
+ * compatibility with older scripts.
53
  */
59
  */
54
 
60
 
55
 /** CPUID setting tag register indices */
61
 /** CPUID setting tag register indices */
60
 	CPUID_EDX = 3,
66
 	CPUID_EDX = 3,
61
 };
67
 };
62
 
68
 
69
+/** CPUID setting tag flags */
70
+enum cpuid_flags {
71
+	CPUID_LITTLE_ENDIAN = 0x00800000UL,
72
+	CPUID_USE_SUBFUNCTION = 0x00400000UL,
73
+};
74
+
63
 /**
75
 /**
64
  * Construct CPUID setting tag
76
  * Construct CPUID setting tag
65
  *
77
  *
66
  * @v function		Starting function number
78
  * @v function		Starting function number
67
- * @v num_functions	Number of consecutive functions
68
- * @v little_endian	Return result as little-endian
79
+ * @v subfunction	Subfunction, or number of consecutive functions minus 1
80
+ * @v flags		Flags
69
  * @v num_registers	Number of registers in register array
81
  * @v num_registers	Number of registers in register array
70
  * @v register1		First register in register array (or zero, if empty)
82
  * @v register1		First register in register array (or zero, if empty)
71
  * @v register2		Second register in register array (or zero, if empty)
83
  * @v register2		Second register in register array (or zero, if empty)
73
  * @v register4		Fourth register in register array (or zero, if empty)
85
  * @v register4		Fourth register in register array (or zero, if empty)
74
  * @ret tag		Setting tag
86
  * @ret tag		Setting tag
75
  */
87
  */
76
-#define CPUID_TAG( function, num_functions, little_endian, num_registers, \
77
-		   register1, register2, register3, register4 )		  \
78
-	( (function) | ( ( (num_functions) - 1 ) << 24 ) |		  \
79
-	  ( (little_endian) << 23 ) | ( ( (num_registers) - 1) << 16 ) |  \
80
-	  ( (register1) << 8 ) | ( (register2) << 10 ) |		  \
88
+#define CPUID_TAG( function, subfunction, flags, num_registers,		\
89
+		   register1, register2, register3, register4 )		\
90
+	( (function) | ( (subfunction) << 24 ) | (flags) |		\
91
+	  ( ( (num_registers) - 1 ) << 16 ) |				\
92
+	  ( (register1) << 8 ) | ( (register2) << 10 ) |		\
81
 	  ( (register3) << 12 ) | ( (register4) << 14 ) )
93
 	  ( (register3) << 12 ) | ( (register4) << 14 ) )
82
 
94
 
83
-/**
84
- * Extract endianness from CPUID setting tag
85
- *
86
- * @v tag		Setting tag
87
- * @ret little_endian	Result should be returned as little-endian
88
- */
89
-#define CPUID_LITTLE_ENDIAN( tag ) ( (tag) & 0x00800000UL )
90
-
91
 /**
95
 /**
92
  * Extract starting function number from CPUID setting tag
96
  * Extract starting function number from CPUID setting tag
93
  *
97
  *
97
 #define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL )
101
 #define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL )
98
 
102
 
99
 /**
103
 /**
100
- * Extract number of consecutive functions from CPUID setting tag
104
+ * Extract subfunction number from CPUID setting tag
101
  *
105
  *
102
  * @v tag		Setting tag
106
  * @v tag		Setting tag
103
- * @ret num_functions	Number of consecutive functions
107
+ * @ret subfunction	Subfunction number
104
  */
108
  */
105
-#define CPUID_NUM_FUNCTIONS( tag ) ( ( ( (tag) >> 24 ) & 0xf ) + 1 )
109
+#define CPUID_SUBFUNCTION( tag ) ( ( (tag) >> 24 ) & 0x7f )
106
 
110
 
107
 /**
111
 /**
108
  * Extract register array from CPUID setting tag
112
  * Extract register array from CPUID setting tag
149
 				  struct setting *setting,
153
 				  struct setting *setting,
150
 				  void *data, size_t len ) {
154
 				  void *data, size_t len ) {
151
 	uint32_t function;
155
 	uint32_t function;
156
+	uint32_t subfunction;
152
 	uint32_t num_functions;
157
 	uint32_t num_functions;
153
 	uint32_t registers;
158
 	uint32_t registers;
154
 	uint32_t num_registers;
159
 	uint32_t num_registers;
160
 
165
 
161
 	/* Call each function in turn */
166
 	/* Call each function in turn */
162
 	function = CPUID_FUNCTION ( setting->tag );
167
 	function = CPUID_FUNCTION ( setting->tag );
163
-	num_functions = CPUID_NUM_FUNCTIONS ( setting->tag );
168
+	subfunction = CPUID_SUBFUNCTION ( setting->tag );
169
+	if ( setting->tag & CPUID_USE_SUBFUNCTION ) {
170
+		num_functions = 1;
171
+	} else {
172
+		num_functions = ( subfunction + 1 );
173
+		subfunction = 0;
174
+	}
164
 	for ( ; num_functions-- ; function++ ) {
175
 	for ( ; num_functions-- ; function++ ) {
165
 
176
 
166
 		/* Fail if this function is not supported */
177
 		/* Fail if this function is not supported */
171
 		}
182
 		}
172
 
183
 
173
 		/* Issue CPUID */
184
 		/* Issue CPUID */
174
-		cpuid ( function, &buf[CPUID_EAX], &buf[CPUID_EBX],
175
-			&buf[CPUID_ECX], &buf[CPUID_EDX] );
176
-		DBGC ( settings, "CPUID %#08x => %#08x:%#08x:%#08x:%#08x\n",
177
-		       function, buf[0], buf[1], buf[2], buf[3] );
185
+		cpuid ( function, subfunction, &buf[CPUID_EAX],
186
+			&buf[CPUID_EBX], &buf[CPUID_ECX], &buf[CPUID_EDX] );
187
+		DBGC ( settings, "CPUID %#08x:%x => %#08x:%#08x:%#08x:%#08x\n",
188
+		       function, subfunction, buf[0], buf[1], buf[2], buf[3] );
178
 
189
 
179
 		/* Copy results to buffer */
190
 		/* Copy results to buffer */
180
 		registers = CPUID_REGISTERS ( setting->tag );
191
 		registers = CPUID_REGISTERS ( setting->tag );
181
 		num_registers = CPUID_NUM_REGISTERS ( setting->tag );
192
 		num_registers = CPUID_NUM_REGISTERS ( setting->tag );
182
 		for ( ; num_registers-- ; registers >>= 2 ) {
193
 		for ( ; num_registers-- ; registers >>= 2 ) {
183
 			output = buf[ registers & 0x3 ];
194
 			output = buf[ registers & 0x3 ];
184
-			if ( ! CPUID_LITTLE_ENDIAN ( setting->tag ) )
195
+			if ( ! ( setting->tag & CPUID_LITTLE_ENDIAN ) )
185
 				output = cpu_to_be32 ( output );
196
 				output = cpu_to_be32 ( output );
186
 			frag_len = sizeof ( output );
197
 			frag_len = sizeof ( output );
187
 			if ( frag_len > len )
198
 			if ( frag_len > len )
237
 						   cpuvendor ) = {
248
 						   cpuvendor ) = {
238
 	.name = "cpuvendor",
249
 	.name = "cpuvendor",
239
 	.description = "CPU vendor",
250
 	.description = "CPU vendor",
240
-	.tag = CPUID_TAG ( CPUID_VENDOR_ID, 1, 1, 3,
251
+	.tag = CPUID_TAG ( CPUID_VENDOR_ID, 0, CPUID_LITTLE_ENDIAN, 3,
241
 			   CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ),
252
 			   CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ),
242
 	.type = &setting_type_string,
253
 	.type = &setting_type_string,
243
 	.scope = &cpuid_settings_scope,
254
 	.scope = &cpuid_settings_scope,
248
 						  cpumodel ) = {
259
 						  cpumodel ) = {
249
 	.name = "cpumodel",
260
 	.name = "cpumodel",
250
 	.description = "CPU model",
261
 	.description = "CPU model",
251
-	.tag = CPUID_TAG ( CPUID_MODEL, 3, 1, 4,
262
+	.tag = CPUID_TAG ( CPUID_MODEL, 2, CPUID_LITTLE_ENDIAN, 4,
252
 			   CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ),
263
 			   CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ),
253
 	.type = &setting_type_string,
264
 	.type = &setting_type_string,
254
 	.scope = &cpuid_settings_scope,
265
 	.scope = &cpuid_settings_scope,

+ 1
- 1
src/arch/x86/core/rdtsc_timer.c View File

132
 		       strerror ( rc ) );
132
 		       strerror ( rc ) );
133
 		return rc;
133
 		return rc;
134
 	}
134
 	}
135
-	cpuid ( CPUID_APM, &discard_a, &discard_b, &discard_c, &apm );
135
+	cpuid ( CPUID_APM, 0, &discard_a, &discard_b, &discard_c, &apm );
136
 	if ( ! ( apm & CPUID_APM_EDX_TSC_INVARIANT ) ) {
136
 	if ( ! ( apm & CPUID_APM_EDX_TSC_INVARIANT ) ) {
137
 		DBGC ( colour, "RDTSC has non-invariant TSC (%#08x)\n",
137
 		DBGC ( colour, "RDTSC has non-invariant TSC (%#08x)\n",
138
 		       apm );
138
 		       apm );

+ 5
- 5
src/arch/x86/drivers/hyperv/hyperv.c View File

170
 	}
170
 	}
171
 
171
 
172
 	/* Check that hypervisor is Hyper-V */
172
 	/* Check that hypervisor is Hyper-V */
173
-	cpuid ( HV_CPUID_INTERFACE_ID, &interface_id, &discard_ebx,
173
+	cpuid ( HV_CPUID_INTERFACE_ID, 0, &interface_id, &discard_ebx,
174
 		&discard_ecx, &discard_edx );
174
 		&discard_ecx, &discard_edx );
175
 	if ( interface_id != HV_INTERFACE_ID ) {
175
 	if ( interface_id != HV_INTERFACE_ID ) {
176
 		DBGC ( HV_INTERFACE_ID, "HV not running in Hyper-V (interface "
176
 		DBGC ( HV_INTERFACE_ID, "HV not running in Hyper-V (interface "
194
 	uint32_t discard_edx;
194
 	uint32_t discard_edx;
195
 
195
 
196
 	/* Check that required features and privileges are available */
196
 	/* Check that required features and privileges are available */
197
-	cpuid ( HV_CPUID_FEATURES, &available, &permissions, &discard_ecx,
197
+	cpuid ( HV_CPUID_FEATURES, 0, &available, &permissions, &discard_ecx,
198
 		&discard_edx );
198
 		&discard_edx );
199
 	if ( ! ( available & HV_FEATURES_AVAIL_HYPERCALL_MSR ) ) {
199
 	if ( ! ( available & HV_FEATURES_AVAIL_HYPERCALL_MSR ) ) {
200
 		DBGC ( hv, "HV %p has no hypercall MSRs (features %08x:%08x)\n",
200
 		DBGC ( hv, "HV %p has no hypercall MSRs (features %08x:%08x)\n",
253
 	wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
253
 	wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
254
 
254
 
255
 	/* Get hypervisor system identity (for debugging) */
255
 	/* Get hypervisor system identity (for debugging) */
256
-	cpuid ( HV_CPUID_VENDOR_ID, &discard_eax, &vendor_id.ebx,
256
+	cpuid ( HV_CPUID_VENDOR_ID, 0, &discard_eax, &vendor_id.ebx,
257
 		&vendor_id.ecx, &vendor_id.edx );
257
 		&vendor_id.ecx, &vendor_id.edx );
258
 	vendor_id.text[ sizeof ( vendor_id.text ) - 1 ] = '\0';
258
 	vendor_id.text[ sizeof ( vendor_id.text ) - 1 ] = '\0';
259
-	cpuid ( HV_CPUID_HYPERVISOR_ID, &build, &version, &discard_ecx,
259
+	cpuid ( HV_CPUID_HYPERVISOR_ID, 0, &build, &version, &discard_ecx,
260
 		&discard_edx );
260
 		&discard_edx );
261
 	DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv,
261
 	DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv,
262
 	       vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build );
262
 	       vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build );
735
 		return rc;
735
 		return rc;
736
 
736
 
737
 	/* Check for available reference counter */
737
 	/* Check for available reference counter */
738
-	cpuid ( HV_CPUID_FEATURES, &available, &discard_ebx, &discard_ecx,
738
+	cpuid ( HV_CPUID_FEATURES, 0, &available, &discard_ebx, &discard_ecx,
739
 		&discard_edx );
739
 		&discard_edx );
740
 	if ( ! ( available & HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR ) ) {
740
 	if ( ! ( available & HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR ) ) {
741
 		DBGC ( HV_INTERFACE_ID, "HV has no time reference counter\n" );
741
 		DBGC ( HV_INTERFACE_ID, "HV has no time reference counter\n" );

+ 3
- 3
src/arch/x86/drivers/xen/hvm.c View File

66
 	/* Scan for magic signature */
66
 	/* Scan for magic signature */
67
 	for ( base = HVM_CPUID_MIN ; base <= HVM_CPUID_MAX ;
67
 	for ( base = HVM_CPUID_MIN ; base <= HVM_CPUID_MAX ;
68
 	      base += HVM_CPUID_STEP ) {
68
 	      base += HVM_CPUID_STEP ) {
69
-		cpuid ( base, &discard_eax, &signature.ebx, &signature.ecx,
69
+		cpuid ( base, 0, &discard_eax, &signature.ebx, &signature.ecx,
70
 			&signature.edx );
70
 			&signature.edx );
71
 		if ( memcmp ( &signature, HVM_CPUID_MAGIC,
71
 		if ( memcmp ( &signature, HVM_CPUID_MAGIC,
72
 			      sizeof ( signature ) ) == 0 ) {
72
 			      sizeof ( signature ) ) == 0 ) {
73
 			hvm->cpuid_base = base;
73
 			hvm->cpuid_base = base;
74
-			cpuid ( ( base + HVM_CPUID_VERSION ), &version,
74
+			cpuid ( ( base + HVM_CPUID_VERSION ), 0, &version,
75
 				&discard_ebx, &discard_ecx, &discard_edx );
75
 				&discard_ebx, &discard_ecx, &discard_edx );
76
 			DBGC2 ( hvm, "HVM using CPUID base %#08x (v%d.%d)\n",
76
 			DBGC2 ( hvm, "HVM using CPUID base %#08x (v%d.%d)\n",
77
 				base, ( version >> 16 ), ( version & 0xffff ) );
77
 				base, ( version >> 16 ), ( version & 0xffff ) );
101
 	int rc;
101
 	int rc;
102
 
102
 
103
 	/* Get number of hypercall pages and MSR to use */
103
 	/* Get number of hypercall pages and MSR to use */
104
-	cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), &pages, &msr,
104
+	cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), 0, &pages, &msr,
105
 		&discard_ecx, &discard_edx );
105
 		&discard_ecx, &discard_edx );
106
 
106
 
107
 	/* Allocate pages */
107
 	/* Allocate pages */

+ 5
- 4
src/arch/x86/include/ipxe/cpuid.h View File

66
 /**
66
 /**
67
  * Issue CPUID instruction
67
  * Issue CPUID instruction
68
  *
68
  *
69
- * @v function		CPUID function
69
+ * @v function		CPUID function (input via %eax)
70
+ * @v subfunction	CPUID subfunction (input via %ecx)
70
  * @v eax		Output via %eax
71
  * @v eax		Output via %eax
71
  * @v ebx		Output via %ebx
72
  * @v ebx		Output via %ebx
72
  * @v ecx		Output via %ecx
73
  * @v ecx		Output via %ecx
73
  * @v edx		Output via %edx
74
  * @v edx		Output via %edx
74
  */
75
  */
75
 static inline __attribute__ (( always_inline )) void
76
 static inline __attribute__ (( always_inline )) void
76
-cpuid ( uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
77
-	uint32_t *edx ) {
77
+cpuid ( uint32_t function, uint32_t subfunction, uint32_t *eax, uint32_t *ebx,
78
+	uint32_t *ecx, uint32_t *edx ) {
78
 
79
 
79
 	__asm__ ( "cpuid"
80
 	__asm__ ( "cpuid"
80
 		  : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx )
81
 		  : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx )
81
-		  : "0" ( function ) );
82
+		  : "0" ( function ), "2" ( subfunction ) );
82
 }
83
 }
83
 
84
 
84
 extern int cpuid_supported ( uint32_t function );
85
 extern int cpuid_supported ( uint32_t function );

Loading…
Cancel
Save