Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / arch / x86 / core / cpuid_settings.c
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 const 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                                     const 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 /** CPU vendor setting */
255 const struct setting cpuvendor_setting __setting ( SETTING_HOST_EXTRA,
256                                                    cpuvendor ) = {
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 /** CPU model setting */
266 const struct setting cpumodel_setting __setting ( SETTING_HOST_EXTRA,
267                                                   cpumodel ) = {
268         .name = "cpumodel",
269         .description = "CPU model",
270         .tag = CPUID_TAG ( CPUID_MODEL, 3, 1, 4,
271                            CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ),
272         .type = &setting_type_string,
273         .scope = &cpuid_settings_scope,
274 };