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 6 years ago
parent
commit
a6a5825f8d

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

@@ -84,7 +84,7 @@ int cpuid_supported ( uint32_t function ) {
84 84
 		return rc;
85 85
 
86 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 88
 		&discard_c, &discard_d );
89 89
 
90 90
 	/* Fail if maximum function number is meaningless (e.g. if we
@@ -125,7 +125,7 @@ static void x86_intel_features ( struct x86_features *features ) {
125 125
 	}
126 126
 
127 127
 	/* Get features */
128
-	cpuid ( CPUID_FEATURES, &discard_a, &discard_b,
128
+	cpuid ( CPUID_FEATURES, 0, &discard_a, &discard_b,
129 129
 		&features->intel.ecx, &features->intel.edx );
130 130
 	DBGC ( features, "CPUID Intel features: %%ecx=%08x, %%edx=%08x\n",
131 131
 	       features->intel.ecx, features->intel.edx );
@@ -149,7 +149,7 @@ static void x86_amd_features ( struct x86_features *features ) {
149 149
 	}
150 150
 
151 151
 	/* Get features */
152
-	cpuid ( CPUID_AMD_FEATURES, &discard_a, &discard_b,
152
+	cpuid ( CPUID_AMD_FEATURES, 0, &discard_a, &discard_b,
153 153
 		&features->amd.ecx, &features->amd.edx );
154 154
 	DBGC ( features, "CPUID AMD features: %%ecx=%08x, %%edx=%08x\n",
155 155
 	       features->amd.ecx, features->amd.edx );

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

@@ -37,10 +37,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
37 37
  * CPUID settings are numerically encoded as:
38 38
  *
39 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 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 45
  *  Bits 17-16	Number of registers in register array, minus one
45 46
  *  Bits 15-8	Array of register indices.  First entry in array is in
46 47
  *		bits 9-8.  Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx.
@@ -50,6 +51,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
50 51
  * extracting a single register from a single function to be encoded
51 52
  * using "cpuid/<register>.<function>", e.g. "cpuid/2.0x80000001" to
52 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 61
 /** CPUID setting tag register indices */
@@ -60,12 +66,18 @@ enum cpuid_registers {
60 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 76
  * Construct CPUID setting tag
65 77
  *
66 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 81
  * @v num_registers	Number of registers in register array
70 82
  * @v register1		First register in register array (or zero, if empty)
71 83
  * @v register2		Second register in register array (or zero, if empty)
@@ -73,21 +85,13 @@ enum cpuid_registers {
73 85
  * @v register4		Fourth register in register array (or zero, if empty)
74 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 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 96
  * Extract starting function number from CPUID setting tag
93 97
  *
@@ -97,12 +101,12 @@ enum cpuid_registers {
97 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 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 112
  * Extract register array from CPUID setting tag
@@ -149,6 +153,7 @@ static int cpuid_settings_fetch ( struct settings *settings,
149 153
 				  struct setting *setting,
150 154
 				  void *data, size_t len ) {
151 155
 	uint32_t function;
156
+	uint32_t subfunction;
152 157
 	uint32_t num_functions;
153 158
 	uint32_t registers;
154 159
 	uint32_t num_registers;
@@ -160,7 +165,13 @@ static int cpuid_settings_fetch ( struct settings *settings,
160 165
 
161 166
 	/* Call each function in turn */
162 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 175
 	for ( ; num_functions-- ; function++ ) {
165 176
 
166 177
 		/* Fail if this function is not supported */
@@ -171,17 +182,17 @@ static int cpuid_settings_fetch ( struct settings *settings,
171 182
 		}
172 183
 
173 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 190
 		/* Copy results to buffer */
180 191
 		registers = CPUID_REGISTERS ( setting->tag );
181 192
 		num_registers = CPUID_NUM_REGISTERS ( setting->tag );
182 193
 		for ( ; num_registers-- ; registers >>= 2 ) {
183 194
 			output = buf[ registers & 0x3 ];
184
-			if ( ! CPUID_LITTLE_ENDIAN ( setting->tag ) )
195
+			if ( ! ( setting->tag & CPUID_LITTLE_ENDIAN ) )
185 196
 				output = cpu_to_be32 ( output );
186 197
 			frag_len = sizeof ( output );
187 198
 			if ( frag_len > len )
@@ -237,7 +248,7 @@ const struct setting cpuvendor_setting __setting ( SETTING_HOST_EXTRA,
237 248
 						   cpuvendor ) = {
238 249
 	.name = "cpuvendor",
239 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 252
 			   CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ),
242 253
 	.type = &setting_type_string,
243 254
 	.scope = &cpuid_settings_scope,
@@ -248,7 +259,7 @@ const struct setting cpumodel_setting __setting ( SETTING_HOST_EXTRA,
248 259
 						  cpumodel ) = {
249 260
 	.name = "cpumodel",
250 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 263
 			   CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ),
253 264
 	.type = &setting_type_string,
254 265
 	.scope = &cpuid_settings_scope,

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

@@ -132,7 +132,7 @@ static int rdtsc_probe ( void ) {
132 132
 		       strerror ( rc ) );
133 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 136
 	if ( ! ( apm & CPUID_APM_EDX_TSC_INVARIANT ) ) {
137 137
 		DBGC ( colour, "RDTSC has non-invariant TSC (%#08x)\n",
138 138
 		       apm );

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

@@ -170,7 +170,7 @@ static int hv_check_hv ( void ) {
170 170
 	}
171 171
 
172 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 174
 		&discard_ecx, &discard_edx );
175 175
 	if ( interface_id != HV_INTERFACE_ID ) {
176 176
 		DBGC ( HV_INTERFACE_ID, "HV not running in Hyper-V (interface "
@@ -194,7 +194,7 @@ static int hv_check_features ( struct hv_hypervisor *hv ) {
194 194
 	uint32_t discard_edx;
195 195
 
196 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 198
 		&discard_edx );
199 199
 	if ( ! ( available & HV_FEATURES_AVAIL_HYPERCALL_MSR ) ) {
200 200
 		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 ) {
253 253
 	wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
254 254
 
255 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 257
 		&vendor_id.ecx, &vendor_id.edx );
258 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 260
 		&discard_edx );
261 261
 	DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv,
262 262
 	       vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build );
@@ -735,7 +735,7 @@ static int hv_timer_probe ( void ) {
735 735
 		return rc;
736 736
 
737 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 739
 		&discard_edx );
740 740
 	if ( ! ( available & HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR ) ) {
741 741
 		DBGC ( HV_INTERFACE_ID, "HV has no time reference counter\n" );

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

@@ -66,12 +66,12 @@ static int hvm_cpuid_base ( struct hvm_device *hvm ) {
66 66
 	/* Scan for magic signature */
67 67
 	for ( base = HVM_CPUID_MIN ; base <= HVM_CPUID_MAX ;
68 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 70
 			&signature.edx );
71 71
 		if ( memcmp ( &signature, HVM_CPUID_MAGIC,
72 72
 			      sizeof ( signature ) ) == 0 ) {
73 73
 			hvm->cpuid_base = base;
74
-			cpuid ( ( base + HVM_CPUID_VERSION ), &version,
74
+			cpuid ( ( base + HVM_CPUID_VERSION ), 0, &version,
75 75
 				&discard_ebx, &discard_ecx, &discard_edx );
76 76
 			DBGC2 ( hvm, "HVM using CPUID base %#08x (v%d.%d)\n",
77 77
 				base, ( version >> 16 ), ( version & 0xffff ) );
@@ -101,7 +101,7 @@ static int hvm_map_hypercall ( struct hvm_device *hvm ) {
101 101
 	int rc;
102 102
 
103 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 105
 		&discard_ecx, &discard_edx );
106 106
 
107 107
 	/* Allocate pages */

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

@@ -66,19 +66,20 @@ struct x86_features {
66 66
 /**
67 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 71
  * @v eax		Output via %eax
71 72
  * @v ebx		Output via %ebx
72 73
  * @v ecx		Output via %ecx
73 74
  * @v edx		Output via %edx
74 75
  */
75 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 80
 	__asm__ ( "cpuid"
80 81
 		  : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx )
81
-		  : "0" ( function ) );
82
+		  : "0" ( function ), "2" ( subfunction ) );
82 83
 }
83 84
 
84 85
 extern int cpuid_supported ( uint32_t function );

Loading…
Cancel
Save