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  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <string.h>
27 #include <errno.h>
28 #include <byteswap.h>
29 #include <ipxe/init.h>
30 #include <ipxe/settings.h>
31 #include <ipxe/cpuid.h>
32 
33 /** @file
34  *
35  * x86 CPUID settings
36  *
37  * CPUID settings are numerically encoded as:
38  *
39  *  Bit  31	Extended function
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)
43  *  Bit  22	Interpret bits 30-24 as a subfunction number
44  *  Bits 21-18	Unused
45  *  Bits 17-16	Number of registers in register array, minus one
46  *  Bits 15-8	Array of register indices.  First entry in array is in
47  *		bits 9-8.  Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx.
48  *  Bits 7-0	Starting function number (excluding "extended" bit)
49  *
50  * This encoding scheme is designed to allow the common case of
51  * extracting a single register from a single function to be encoded
52  * using "cpuid/<register>.<function>", e.g. "cpuid/2.0x80000001" to
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.
59  */
60 
61 /** CPUID setting tag register indices */
62 enum cpuid_registers {
63 	CPUID_EAX = 0,
64 	CPUID_EBX = 1,
65 	CPUID_ECX = 2,
66 	CPUID_EDX = 3,
67 };
68 
69 /** CPUID setting tag flags */
70 enum cpuid_flags {
71 	CPUID_LITTLE_ENDIAN = 0x00800000UL,
72 	CPUID_USE_SUBFUNCTION = 0x00400000UL,
73 };
74 
75 /**
76  * Construct CPUID setting tag
77  *
78  * @v function		Starting function number
79  * @v subfunction	Subfunction, or number of consecutive functions minus 1
80  * @v flags		Flags
81  * @v num_registers	Number of registers in register array
82  * @v register1		First register in register array (or zero, if empty)
83  * @v register2		Second register in register array (or zero, if empty)
84  * @v register3		Third register in register array (or zero, if empty)
85  * @v register4		Fourth register in register array (or zero, if empty)
86  * @ret tag		Setting tag
87  */
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 ) |		\
93 	  ( (register3) << 12 ) | ( (register4) << 14 ) )
94 
95 /**
96  * Extract starting function number from CPUID setting tag
97  *
98  * @v tag		Setting tag
99  * @ret function	Starting function number
100  */
101 #define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL )
102 
103 /**
104  * Extract subfunction number from CPUID setting tag
105  *
106  * @v tag		Setting tag
107  * @ret subfunction	Subfunction number
108  */
109 #define CPUID_SUBFUNCTION( tag ) ( ( (tag) >> 24 ) & 0x7f )
110 
111 /**
112  * Extract register array from CPUID setting tag
113  *
114  * @v tag		Setting tag
115  * @ret registers	Register array
116  */
117 #define CPUID_REGISTERS( tag ) ( ( (tag) >> 8 ) & 0xff )
118 
119 /**
120  * Extract number of registers from CPUID setting tag
121  *
122  * @v tag		Setting tag
123  * @ret num_registers	Number of registers within register array
124  */
125 #define CPUID_NUM_REGISTERS( tag ) ( ( ( (tag) >> 16 ) & 0x3 ) + 1 )
126 
127 /** CPUID settings scope */
128 static const struct settings_scope cpuid_settings_scope;
129 
130 /**
131  * Check applicability of CPUID setting
132  *
133  * @v settings		Settings block
134  * @v setting		Setting
135  * @ret applies		Setting applies within this settings block
136  */
cpuid_settings_applies(struct settings * settings __unused,const struct setting * setting)137 static int cpuid_settings_applies ( struct settings *settings __unused,
138 				    const struct setting *setting ) {
139 
140 	return ( setting->scope == &cpuid_settings_scope );
141 }
142 
143 /**
144  * Fetch value of CPUID setting
145  *
146  * @v settings		Settings block
147  * @v setting		Setting to fetch
148  * @v data		Buffer to fill with setting data
149  * @v len		Length of buffer
150  * @ret len		Length of setting data, or negative error
151  */
cpuid_settings_fetch(struct settings * settings,struct setting * setting,void * data,size_t len)152 static int cpuid_settings_fetch ( struct settings *settings,
153 				  struct setting *setting,
154 				  void *data, size_t len ) {
155 	uint32_t function;
156 	uint32_t subfunction;
157 	uint32_t num_functions;
158 	uint32_t registers;
159 	uint32_t num_registers;
160 	uint32_t buf[4];
161 	uint32_t output;
162 	size_t frag_len;
163 	size_t result_len = 0;
164 	int rc;
165 
166 	/* Call each function in turn */
167 	function = CPUID_FUNCTION ( 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 	}
175 	for ( ; num_functions-- ; function++ ) {
176 
177 		/* Fail if this function is not supported */
178 		if ( ( rc = cpuid_supported ( function ) ) != 0 ) {
179 			DBGC ( settings, "CPUID function %#08x not supported: "
180 			       "%s\n", function, strerror ( rc ) );
181 			return rc;
182 		}
183 
184 		/* Issue CPUID */
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] );
189 
190 		/* Copy results to buffer */
191 		registers = CPUID_REGISTERS ( setting->tag );
192 		num_registers = CPUID_NUM_REGISTERS ( setting->tag );
193 		for ( ; num_registers-- ; registers >>= 2 ) {
194 			output = buf[ registers & 0x3 ];
195 			if ( ! ( setting->tag & CPUID_LITTLE_ENDIAN ) )
196 				output = cpu_to_be32 ( output );
197 			frag_len = sizeof ( output );
198 			if ( frag_len > len )
199 				frag_len = len;
200 			memcpy ( data, &output, frag_len );
201 			data += frag_len;
202 			len -= frag_len;
203 			result_len += sizeof ( output );
204 		}
205 	}
206 
207 	/* Set type if not already specified */
208 	if ( ! setting->type )
209 		setting->type = &setting_type_hexraw;
210 
211 	return result_len;
212 }
213 
214 /** CPUID settings operations */
215 static struct settings_operations cpuid_settings_operations = {
216 	.applies = cpuid_settings_applies,
217 	.fetch = cpuid_settings_fetch,
218 };
219 
220 /** CPUID settings */
221 static struct settings cpuid_settings = {
222 	.refcnt = NULL,
223 	.siblings = LIST_HEAD_INIT ( cpuid_settings.siblings ),
224 	.children = LIST_HEAD_INIT ( cpuid_settings.children ),
225 	.op = &cpuid_settings_operations,
226 	.default_scope = &cpuid_settings_scope,
227 };
228 
229 /** Initialise CPUID settings */
cpuid_settings_init(void)230 static void cpuid_settings_init ( void ) {
231 	int rc;
232 
233 	if ( ( rc = register_settings ( &cpuid_settings, NULL,
234 					"cpuid" ) ) != 0 ) {
235 		DBG ( "CPUID could not register settings: %s\n",
236 		      strerror ( rc ) );
237 		return;
238 	}
239 }
240 
241 /** CPUID settings initialiser */
242 struct init_fn cpuid_settings_init_fn __init_fn ( INIT_NORMAL ) = {
243 	.initialise = cpuid_settings_init,
244 };
245 
246 /** CPU vendor setting */
247 const struct setting cpuvendor_setting __setting ( SETTING_HOST_EXTRA,
248 						   cpuvendor ) = {
249 	.name = "cpuvendor",
250 	.description = "CPU vendor",
251 	.tag = CPUID_TAG ( CPUID_VENDOR_ID, 0, CPUID_LITTLE_ENDIAN, 3,
252 			   CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ),
253 	.type = &setting_type_string,
254 	.scope = &cpuid_settings_scope,
255 };
256 
257 /** CPU model setting */
258 const struct setting cpumodel_setting __setting ( SETTING_HOST_EXTRA,
259 						  cpumodel ) = {
260 	.name = "cpumodel",
261 	.description = "CPU model",
262 	.tag = CPUID_TAG ( CPUID_MODEL, 2, CPUID_LITTLE_ENDIAN, 4,
263 			   CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ),
264 	.type = &setting_type_string,
265 	.scope = &cpuid_settings_scope,
266 };
267