Browse Source

[settings] Expose CPUID instruction via settings mechanism

Allow CPUID values to be read using the syntax

  ${cpuid/<register>.<function>}

For example, ${cpuid/2.0x80000001} will give the value of %ecx after
calling CPUID with %eax=0x80000001.  Values for <register> are encoded
as %eax=0, %ebx=1, %ecx=2, %edx=3.

The numeric encoding is more sophisticated than described above,
allowing for settings such as the CPU model (obtained by calling CPUID
with %eax=0x80000002-0x80000004 inclusive and concatenating the values
returned in %eax:%ebx:%ecx:%edx).  See the source code for details.

The "cpuvendor" and "cpumodel" settings provide easy access to these
more complex CPUID settings.

This functionality is intended to complement the "cpuid" command,
which allows for testing individual CPUID feature bits.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 11 years ago
parent
commit
55201e2d0e

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

@@ -33,7 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
33 33
  *
34 34
  * @ret is_supported	CPUID instruction is supported
35 35
  */
36
-static int cpuid_is_supported ( void ) {
36
+int cpuid_is_supported ( void ) {
37 37
 	unsigned long original;
38 38
 	unsigned long inverted;
39 39
 
@@ -52,24 +52,6 @@ static int cpuid_is_supported ( void ) {
52 52
 	return ( ( original ^ inverted ) & CPUID_FLAG );
53 53
 }
54 54
 
55
-/**
56
- * Issue CPUID instruction
57
- *
58
- * @v operation		CPUID operation
59
- * @v eax		Output via %eax
60
- * @v ebx		Output via %ebx
61
- * @v ecx		Output via %ecx
62
- * @v edx		Output via %edx
63
- */
64
-static inline __attribute__ (( always_inline )) void
65
-cpuid ( uint32_t operation, uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
66
-	uint32_t *edx ) {
67
-
68
-	__asm__ ( "cpuid"
69
-		  : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx )
70
-		  : "0" ( operation ) );
71
-}
72
-
73 55
 /**
74 56
  * Get Intel-defined x86 CPU features
75 57
  *

+ 272
- 0
src/arch/x86/core/cpuid_settings.c View File

@@ -0,0 +1,272 @@
1
+/*
2
+ * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ */
19
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+#include <string.h>
23
+#include <errno.h>
24
+#include <byteswap.h>
25
+#include <ipxe/init.h>
26
+#include <ipxe/settings.h>
27
+#include <ipxe/cpuid.h>
28
+
29
+/** @file
30
+ *
31
+ * x86 CPUID settings
32
+ *
33
+ * CPUID settings are numerically encoded as:
34
+ *
35
+ *  Bit  31	Extended function
36
+ *  Bits 30-28	Unused
37
+ *  Bits 27-24	Number of consecutive functions to call, minus one
38
+ *  Bit  23	Return result as little-endian (used for strings)
39
+ *  Bits 22-18	Unused
40
+ *  Bits 17-16	Number of registers in register array, minus one
41
+ *  Bits 15-8	Array of register indices.  First entry in array is in
42
+ *		bits 9-8.  Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx.
43
+ *  Bits 7-0	Starting function number (excluding "extended" bit)
44
+ *
45
+ * This encoding scheme is designed to allow the common case of
46
+ * extracting a single register from a single function to be encoded
47
+ * using "cpuid/<register>.<function>", e.g. "cpuid/2.0x80000001" to
48
+ * retrieve the value of %ecx from calling CPUID with %eax=0x80000001.
49
+ */
50
+
51
+/** CPUID setting tag register indices */
52
+enum cpuid_registers {
53
+	CPUID_EAX = 0,
54
+	CPUID_EBX = 1,
55
+	CPUID_ECX = 2,
56
+	CPUID_EDX = 3,
57
+};
58
+
59
+/**
60
+ * Construct CPUID setting tag
61
+ *
62
+ * @v function		Starting function number
63
+ * @v num_functions	Number of consecutive functions
64
+ * @v little_endian	Return result as little-endian
65
+ * @v num_registers	Number of registers in register array
66
+ * @v register1		First register in register array (or zero, if empty)
67
+ * @v register2		Second register in register array (or zero, if empty)
68
+ * @v register3		Third register in register array (or zero, if empty)
69
+ * @v register4		Fourth register in register array (or zero, if empty)
70
+ * @ret tag		Setting tag
71
+ */
72
+#define CPUID_TAG( function, num_functions, little_endian, num_registers, \
73
+		   register1, register2, register3, register4 )		  \
74
+	( (function) | ( ( (num_functions) - 1 ) << 24 ) |		  \
75
+	  ( (little_endian) << 23 ) | ( ( (num_registers) - 1) << 16 ) |  \
76
+	  ( (register1) << 8 ) | ( (register2) << 10 ) |		  \
77
+	  ( (register3) << 12 ) | ( (register4) << 14 ) )
78
+
79
+/**
80
+ * Extract endianness from CPUID setting tag
81
+ *
82
+ * @v tag		Setting tag
83
+ * @ret little_endian	Result should be returned as little-endian
84
+ */
85
+#define CPUID_LITTLE_ENDIAN( tag ) ( (tag) & 0x00800000UL )
86
+
87
+/**
88
+ * Extract starting function number from CPUID setting tag
89
+ *
90
+ * @v tag		Setting tag
91
+ * @ret function	Starting function number
92
+ */
93
+#define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL )
94
+
95
+/**
96
+ * Extract number of consecutive functions from CPUID setting tag
97
+ *
98
+ * @v tag		Setting tag
99
+ * @ret num_functions	Number of consecutive functions
100
+ */
101
+#define CPUID_NUM_FUNCTIONS( tag ) ( ( ( (tag) >> 24 ) & 0xf ) + 1 )
102
+
103
+/**
104
+ * Extract register array from CPUID setting tag
105
+ *
106
+ * @v tag		Setting tag
107
+ * @ret registers	Register array
108
+ */
109
+#define CPUID_REGISTERS( tag ) ( ( (tag) >> 8 ) & 0xff )
110
+
111
+/**
112
+ * Extract number of registers from CPUID setting tag
113
+ *
114
+ * @v tag		Setting tag
115
+ * @ret num_registers	Number of registers within register array
116
+ */
117
+#define CPUID_NUM_REGISTERS( tag ) ( ( ( (tag) >> 16 ) & 0x3 ) + 1 )
118
+
119
+/** CPUID settings scope */
120
+static struct settings_scope cpuid_settings_scope;
121
+
122
+/**
123
+ * Check applicability of CPUID setting
124
+ *
125
+ * @v settings		Settings block
126
+ * @v setting		Setting
127
+ * @ret applies		Setting applies within this settings block
128
+ */
129
+static int cpuid_settings_applies ( struct settings *settings __unused,
130
+				    struct setting *setting ) {
131
+
132
+	return ( setting->scope == &cpuid_settings_scope );
133
+}
134
+
135
+/**
136
+ * Fetch value of CPUID setting
137
+ *
138
+ * @v settings		Settings block
139
+ * @v setting		Setting to fetch
140
+ * @v data		Buffer to fill with setting data
141
+ * @v len		Length of buffer
142
+ * @ret len		Length of setting data, or negative error
143
+ */
144
+static int cpuid_settings_fetch ( struct settings *settings,
145
+				  struct setting *setting,
146
+				  void *data, size_t len ) {
147
+	uint32_t function;
148
+	uint32_t max_function;
149
+	uint32_t num_functions;
150
+	uint32_t registers;
151
+	uint32_t num_registers;
152
+	uint32_t buf[4];
153
+	uint32_t output;
154
+	uint32_t discard_b;
155
+	uint32_t discard_c;
156
+	uint32_t discard_d;
157
+	size_t frag_len;
158
+	size_t result_len = 0;
159
+
160
+	/* Fail unless CPUID is supported */
161
+	if ( ! cpuid_is_supported() ) {
162
+		DBGC ( settings, "CPUID not supported\n" );
163
+		return -ENOTSUP;
164
+	}
165
+
166
+	/* Find highest supported function number within this set */
167
+	function = CPUID_FUNCTION ( setting->tag );
168
+	cpuid ( function & CPUID_EXTENDED, &max_function, &discard_b,
169
+		&discard_c, &discard_d );
170
+
171
+	/* Fail if maximum function number is meaningless (e.g. if we
172
+	 * are attempting to call an extended function on a CPU which
173
+	 * does not support them).
174
+	 */
175
+	if ( ( max_function & CPUID_AMD_CHECK_MASK ) !=
176
+	     ( function & CPUID_AMD_CHECK_MASK ) ) {
177
+		DBGC ( settings, "CPUID invalid maximum function\n" );
178
+		return -ENOTSUP;
179
+	}
180
+
181
+	/* Call each function in turn */
182
+	num_functions = CPUID_NUM_FUNCTIONS ( setting->tag );
183
+	for ( ; num_functions-- ; function++ ) {
184
+
185
+		/* Fail if this function is not supported */
186
+		if ( function > max_function ) {
187
+			DBGC ( settings, "CPUID function %#08x not supported\n",
188
+			       function );
189
+			return -ENOTSUP;
190
+		}
191
+
192
+		/* Issue CPUID */
193
+		cpuid ( function, &buf[CPUID_EAX], &buf[CPUID_EBX],
194
+			&buf[CPUID_ECX], &buf[CPUID_EDX] );
195
+		DBGC ( settings, "CPUID %#08x => %#08x:%#08x:%#08x:%#08x\n",
196
+		       function, buf[0], buf[1], buf[2], buf[3] );
197
+
198
+		/* Copy results to buffer */
199
+		registers = CPUID_REGISTERS ( setting->tag );
200
+		num_registers = CPUID_NUM_REGISTERS ( setting->tag );
201
+		for ( ; num_registers-- ; registers >>= 2 ) {
202
+			output = buf[ registers & 0x3 ];
203
+			if ( ! CPUID_LITTLE_ENDIAN ( setting->tag ) )
204
+				output = cpu_to_be32 ( output );
205
+			frag_len = sizeof ( output );
206
+			if ( frag_len > len )
207
+				frag_len = len;
208
+			memcpy ( data, &output, frag_len );
209
+			data += frag_len;
210
+			len -= frag_len;
211
+			result_len += sizeof ( output );
212
+		}
213
+	}
214
+
215
+	/* Set type if not already specified */
216
+	if ( ! setting->type )
217
+		setting->type = &setting_type_hexraw;
218
+
219
+	return result_len;
220
+}
221
+
222
+/** CPUID settings operations */
223
+static struct settings_operations cpuid_settings_operations = {
224
+	.applies = cpuid_settings_applies,
225
+	.fetch = cpuid_settings_fetch,
226
+};
227
+
228
+/** CPUID settings */
229
+static struct settings cpuid_settings = {
230
+	.refcnt = NULL,
231
+	.siblings = LIST_HEAD_INIT ( cpuid_settings.siblings ),
232
+	.children = LIST_HEAD_INIT ( cpuid_settings.children ),
233
+	.op = &cpuid_settings_operations,
234
+	.default_scope = &cpuid_settings_scope,
235
+};
236
+
237
+/** Initialise CPUID settings */
238
+static void cpuid_settings_init ( void ) {
239
+	int rc;
240
+
241
+	if ( ( rc = register_settings ( &cpuid_settings, NULL,
242
+					"cpuid" ) ) != 0 ) {
243
+		DBG ( "CPUID could not register settings: %s\n",
244
+		      strerror ( rc ) );
245
+		return;
246
+	}
247
+}
248
+
249
+/** CPUID settings initialiser */
250
+struct init_fn cpuid_settings_init_fn __init_fn ( INIT_NORMAL ) = {
251
+	.initialise = cpuid_settings_init,
252
+};
253
+
254
+/** CPUID predefined settings */
255
+struct setting cpuid_predefined_settings[] __setting ( SETTING_HOST_EXTRA ) = {
256
+	{
257
+		.name = "cpuvendor",
258
+		.description = "CPU vendor",
259
+		.tag = CPUID_TAG ( CPUID_VENDOR_ID, 1, 1, 3,
260
+				   CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ),
261
+		.type = &setting_type_string,
262
+		.scope = &cpuid_settings_scope,
263
+	},
264
+	{
265
+		.name = "cpumodel",
266
+		.description = "CPU model",
267
+		.tag = CPUID_TAG ( CPUID_MODEL, 3, 1, 4,
268
+				   CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ),
269
+		.type = &setting_type_string,
270
+		.scope = &cpuid_settings_scope,
271
+	},
272
+};

+ 1
- 0
src/arch/x86/include/bits/errfile.h View File

@@ -46,6 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
46 46
 #define ERRFILE_timer_bios    ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 )
47 47
 
48 48
 #define ERRFILE_cpuid_cmd      ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 )
49
+#define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 )
49 50
 
50 51
 /** @} */
51 52
 

+ 25
- 0
src/arch/x86/include/ipxe/cpuid.h View File

@@ -30,6 +30,9 @@ struct x86_features {
30 30
 /** CPUID support flag */
31 31
 #define CPUID_FLAG 0x00200000UL
32 32
 
33
+/** CPUID extended function */
34
+#define CPUID_EXTENDED 0x80000000UL
35
+
33 36
 /** Get vendor ID and largest standard function */
34 37
 #define CPUID_VENDOR_ID 0x00000000UL
35 38
 
@@ -48,6 +51,28 @@ struct x86_features {
48 51
 /** Get extended features */
49 52
 #define CPUID_AMD_FEATURES 0x80000001UL
50 53
 
54
+/** Get CPU model */
55
+#define CPUID_MODEL 0x80000002UL
56
+
57
+/**
58
+ * Issue CPUID instruction
59
+ *
60
+ * @v operation		CPUID operation
61
+ * @v eax		Output via %eax
62
+ * @v ebx		Output via %ebx
63
+ * @v ecx		Output via %ecx
64
+ * @v edx		Output via %edx
65
+ */
66
+static inline __attribute__ (( always_inline )) void
67
+cpuid ( uint32_t operation, uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
68
+	uint32_t *edx ) {
69
+
70
+	__asm__ ( "cpuid"
71
+		  : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx )
72
+		  : "0" ( operation ) );
73
+}
74
+
75
+extern int cpuid_is_supported ( void );
51 76
 extern void x86_features ( struct x86_features *features );
52 77
 
53 78
 #endif /* _IPXE_CPUID_H */

+ 3
- 0
src/config/config.c View File

@@ -313,6 +313,9 @@ REQUIRE_OBJECT ( pci_settings );
313 313
 #ifdef VMWARE_SETTINGS
314 314
 REQUIRE_OBJECT ( guestinfo );
315 315
 #endif
316
+#ifdef CPUID_SETTINGS
317
+REQUIRE_OBJECT ( cpuid_settings );
318
+#endif
316 319
 
317 320
 /*
318 321
  * Drag in selected keyboard map

+ 1
- 0
src/config/settings.h View File

@@ -10,6 +10,7 @@
10 10
 FILE_LICENCE ( GPL2_OR_LATER );
11 11
 
12 12
 #define	PCI_SETTINGS	/* PCI device settings */
13
+//#define	CPUID_SETTINGS	/* CPUID settings */
13 14
 //#define	VMWARE_SETTINGS	/* VMware GuestInfo settings */
14 15
 
15 16
 #include <config/local/settings.h>

Loading…
Cancel
Save