|
@@ -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,
|