~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/ipxe/src/arch/x86/core/cpuid_settings.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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-28  Unused
 
41
 *  Bits 27-24  Number of consecutive functions to call, minus one
 
42
 *  Bit  23     Return result as little-endian (used for strings)
 
43
 *  Bits 22-18  Unused
 
44
 *  Bits 17-16  Number of registers in register array, minus one
 
45
 *  Bits 15-8   Array of register indices.  First entry in array is in
 
46
 *              bits 9-8.  Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx.
 
47
 *  Bits 7-0    Starting function number (excluding "extended" bit)
 
48
 *
 
49
 * This encoding scheme is designed to allow the common case of
 
50
 * extracting a single register from a single function to be encoded
 
51
 * using "cpuid/<register>.<function>", e.g. "cpuid/2.0x80000001" to
 
52
 * retrieve the value of %ecx from calling CPUID with %eax=0x80000001.
 
53
 */
 
54
 
 
55
/** CPUID setting tag register indices */
 
56
enum cpuid_registers {
 
57
        CPUID_EAX = 0,
 
58
        CPUID_EBX = 1,
 
59
        CPUID_ECX = 2,
 
60
        CPUID_EDX = 3,
 
61
};
 
62
 
 
63
/**
 
64
 * Construct CPUID setting tag
 
65
 *
 
66
 * @v function          Starting function number
 
67
 * @v num_functions     Number of consecutive functions
 
68
 * @v little_endian     Return result as little-endian
 
69
 * @v num_registers     Number of registers in register array
 
70
 * @v register1         First register in register array (or zero, if empty)
 
71
 * @v register2         Second register in register array (or zero, if empty)
 
72
 * @v register3         Third register in register array (or zero, if empty)
 
73
 * @v register4         Fourth register in register array (or zero, if empty)
 
74
 * @ret tag             Setting tag
 
75
 */
 
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 ) |                  \
 
81
          ( (register3) << 12 ) | ( (register4) << 14 ) )
 
82
 
 
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
/**
 
92
 * Extract starting function number from CPUID setting tag
 
93
 *
 
94
 * @v tag               Setting tag
 
95
 * @ret function        Starting function number
 
96
 */
 
97
#define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL )
 
98
 
 
99
/**
 
100
 * Extract number of consecutive functions from CPUID setting tag
 
101
 *
 
102
 * @v tag               Setting tag
 
103
 * @ret num_functions   Number of consecutive functions
 
104
 */
 
105
#define CPUID_NUM_FUNCTIONS( tag ) ( ( ( (tag) >> 24 ) & 0xf ) + 1 )
 
106
 
 
107
/**
 
108
 * Extract register array from CPUID setting tag
 
109
 *
 
110
 * @v tag               Setting tag
 
111
 * @ret registers       Register array
 
112
 */
 
113
#define CPUID_REGISTERS( tag ) ( ( (tag) >> 8 ) & 0xff )
 
114
 
 
115
/**
 
116
 * Extract number of registers from CPUID setting tag
 
117
 *
 
118
 * @v tag               Setting tag
 
119
 * @ret num_registers   Number of registers within register array
 
120
 */
 
121
#define CPUID_NUM_REGISTERS( tag ) ( ( ( (tag) >> 16 ) & 0x3 ) + 1 )
 
122
 
 
123
/** CPUID settings scope */
 
124
static const struct settings_scope cpuid_settings_scope;
 
125
 
 
126
/**
 
127
 * Check applicability of CPUID setting
 
128
 *
 
129
 * @v settings          Settings block
 
130
 * @v setting           Setting
 
131
 * @ret applies         Setting applies within this settings block
 
132
 */
 
133
static int cpuid_settings_applies ( struct settings *settings __unused,
 
134
                                    const struct setting *setting ) {
 
135
 
 
136
        return ( setting->scope == &cpuid_settings_scope );
 
137
}
 
138
 
 
139
/**
 
140
 * Fetch value of CPUID setting
 
141
 *
 
142
 * @v settings          Settings block
 
143
 * @v setting           Setting to fetch
 
144
 * @v data              Buffer to fill with setting data
 
145
 * @v len               Length of buffer
 
146
 * @ret len             Length of setting data, or negative error
 
147
 */
 
148
static int cpuid_settings_fetch ( struct settings *settings,
 
149
                                  struct setting *setting,
 
150
                                  void *data, size_t len ) {
 
151
        uint32_t function;
 
152
        uint32_t max_function;
 
153
        uint32_t num_functions;
 
154
        uint32_t registers;
 
155
        uint32_t num_registers;
 
156
        uint32_t buf[4];
 
157
        uint32_t output;
 
158
        uint32_t discard_b;
 
159
        uint32_t discard_c;
 
160
        uint32_t discard_d;
 
161
        size_t frag_len;
 
162
        size_t result_len = 0;
 
163
 
 
164
        /* Fail unless CPUID is supported */
 
165
        if ( ! cpuid_is_supported() ) {
 
166
                DBGC ( settings, "CPUID not supported\n" );
 
167
                return -ENOTSUP;
 
168
        }
 
169
 
 
170
        /* Find highest supported function number within this set */
 
171
        function = CPUID_FUNCTION ( setting->tag );
 
172
        cpuid ( function & CPUID_EXTENDED, &max_function, &discard_b,
 
173
                &discard_c, &discard_d );
 
174
 
 
175
        /* Fail if maximum function number is meaningless (e.g. if we
 
176
         * are attempting to call an extended function on a CPU which
 
177
         * does not support them).
 
178
         */
 
179
        if ( ( max_function & CPUID_AMD_CHECK_MASK ) !=
 
180
             ( function & CPUID_AMD_CHECK_MASK ) ) {
 
181
                DBGC ( settings, "CPUID invalid maximum function\n" );
 
182
                return -ENOTSUP;
 
183
        }
 
184
 
 
185
        /* Call each function in turn */
 
186
        num_functions = CPUID_NUM_FUNCTIONS ( setting->tag );
 
187
        for ( ; num_functions-- ; function++ ) {
 
188
 
 
189
                /* Fail if this function is not supported */
 
190
                if ( function > max_function ) {
 
191
                        DBGC ( settings, "CPUID function %#08x not supported\n",
 
192
                               function );
 
193
                        return -ENOTSUP;
 
194
                }
 
195
 
 
196
                /* Issue CPUID */
 
197
                cpuid ( function, &buf[CPUID_EAX], &buf[CPUID_EBX],
 
198
                        &buf[CPUID_ECX], &buf[CPUID_EDX] );
 
199
                DBGC ( settings, "CPUID %#08x => %#08x:%#08x:%#08x:%#08x\n",
 
200
                       function, buf[0], buf[1], buf[2], buf[3] );
 
201
 
 
202
                /* Copy results to buffer */
 
203
                registers = CPUID_REGISTERS ( setting->tag );
 
204
                num_registers = CPUID_NUM_REGISTERS ( setting->tag );
 
205
                for ( ; num_registers-- ; registers >>= 2 ) {
 
206
                        output = buf[ registers & 0x3 ];
 
207
                        if ( ! CPUID_LITTLE_ENDIAN ( setting->tag ) )
 
208
                                output = cpu_to_be32 ( output );
 
209
                        frag_len = sizeof ( output );
 
210
                        if ( frag_len > len )
 
211
                                frag_len = len;
 
212
                        memcpy ( data, &output, frag_len );
 
213
                        data += frag_len;
 
214
                        len -= frag_len;
 
215
                        result_len += sizeof ( output );
 
216
                }
 
217
        }
 
218
 
 
219
        /* Set type if not already specified */
 
220
        if ( ! setting->type )
 
221
                setting->type = &setting_type_hexraw;
 
222
 
 
223
        return result_len;
 
224
}
 
225
 
 
226
/** CPUID settings operations */
 
227
static struct settings_operations cpuid_settings_operations = {
 
228
        .applies = cpuid_settings_applies,
 
229
        .fetch = cpuid_settings_fetch,
 
230
};
 
231
 
 
232
/** CPUID settings */
 
233
static struct settings cpuid_settings = {
 
234
        .refcnt = NULL,
 
235
        .siblings = LIST_HEAD_INIT ( cpuid_settings.siblings ),
 
236
        .children = LIST_HEAD_INIT ( cpuid_settings.children ),
 
237
        .op = &cpuid_settings_operations,
 
238
        .default_scope = &cpuid_settings_scope,
 
239
};
 
240
 
 
241
/** Initialise CPUID settings */
 
242
static void cpuid_settings_init ( void ) {
 
243
        int rc;
 
244
 
 
245
        if ( ( rc = register_settings ( &cpuid_settings, NULL,
 
246
                                        "cpuid" ) ) != 0 ) {
 
247
                DBG ( "CPUID could not register settings: %s\n",
 
248
                      strerror ( rc ) );
 
249
                return;
 
250
        }
 
251
}
 
252
 
 
253
/** CPUID settings initialiser */
 
254
struct init_fn cpuid_settings_init_fn __init_fn ( INIT_NORMAL ) = {
 
255
        .initialise = cpuid_settings_init,
 
256
};
 
257
 
 
258
/** CPU vendor setting */
 
259
const struct setting cpuvendor_setting __setting ( SETTING_HOST_EXTRA,
 
260
                                                   cpuvendor ) = {
 
261
        .name = "cpuvendor",
 
262
        .description = "CPU vendor",
 
263
        .tag = CPUID_TAG ( CPUID_VENDOR_ID, 1, 1, 3,
 
264
                           CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ),
 
265
        .type = &setting_type_string,
 
266
        .scope = &cpuid_settings_scope,
 
267
};
 
268
 
 
269
/** CPU model setting */
 
270
const struct setting cpumodel_setting __setting ( SETTING_HOST_EXTRA,
 
271
                                                  cpumodel ) = {
 
272
        .name = "cpumodel",
 
273
        .description = "CPU model",
 
274
        .tag = CPUID_TAG ( CPUID_MODEL, 3, 1, 4,
 
275
                           CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ),
 
276
        .type = &setting_type_string,
 
277
        .scope = &cpuid_settings_scope,
 
278
};