summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c')
-rw-r--r--qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c274
1 files changed, 274 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c b/qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c
new file mode 100644
index 000000000..42dea9336
--- /dev/null
+++ b/qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/init.h>
+#include <ipxe/settings.h>
+#include <ipxe/cpuid.h>
+
+/** @file
+ *
+ * x86 CPUID settings
+ *
+ * CPUID settings are numerically encoded as:
+ *
+ * Bit 31 Extended function
+ * Bits 30-28 Unused
+ * Bits 27-24 Number of consecutive functions to call, minus one
+ * Bit 23 Return result as little-endian (used for strings)
+ * Bits 22-18 Unused
+ * Bits 17-16 Number of registers in register array, minus one
+ * Bits 15-8 Array of register indices. First entry in array is in
+ * bits 9-8. Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx.
+ * Bits 7-0 Starting function number (excluding "extended" bit)
+ *
+ * This encoding scheme is designed to allow the common case of
+ * extracting a single register from a single function to be encoded
+ * using "cpuid/<register>.<function>", e.g. "cpuid/2.0x80000001" to
+ * retrieve the value of %ecx from calling CPUID with %eax=0x80000001.
+ */
+
+/** CPUID setting tag register indices */
+enum cpuid_registers {
+ CPUID_EAX = 0,
+ CPUID_EBX = 1,
+ CPUID_ECX = 2,
+ CPUID_EDX = 3,
+};
+
+/**
+ * Construct CPUID setting tag
+ *
+ * @v function Starting function number
+ * @v num_functions Number of consecutive functions
+ * @v little_endian Return result as little-endian
+ * @v num_registers Number of registers in register array
+ * @v register1 First register in register array (or zero, if empty)
+ * @v register2 Second register in register array (or zero, if empty)
+ * @v register3 Third register in register array (or zero, if empty)
+ * @v register4 Fourth register in register array (or zero, if empty)
+ * @ret tag Setting tag
+ */
+#define CPUID_TAG( function, num_functions, little_endian, num_registers, \
+ register1, register2, register3, register4 ) \
+ ( (function) | ( ( (num_functions) - 1 ) << 24 ) | \
+ ( (little_endian) << 23 ) | ( ( (num_registers) - 1) << 16 ) | \
+ ( (register1) << 8 ) | ( (register2) << 10 ) | \
+ ( (register3) << 12 ) | ( (register4) << 14 ) )
+
+/**
+ * Extract endianness from CPUID setting tag
+ *
+ * @v tag Setting tag
+ * @ret little_endian Result should be returned as little-endian
+ */
+#define CPUID_LITTLE_ENDIAN( tag ) ( (tag) & 0x00800000UL )
+
+/**
+ * Extract starting function number from CPUID setting tag
+ *
+ * @v tag Setting tag
+ * @ret function Starting function number
+ */
+#define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL )
+
+/**
+ * Extract number of consecutive functions from CPUID setting tag
+ *
+ * @v tag Setting tag
+ * @ret num_functions Number of consecutive functions
+ */
+#define CPUID_NUM_FUNCTIONS( tag ) ( ( ( (tag) >> 24 ) & 0xf ) + 1 )
+
+/**
+ * Extract register array from CPUID setting tag
+ *
+ * @v tag Setting tag
+ * @ret registers Register array
+ */
+#define CPUID_REGISTERS( tag ) ( ( (tag) >> 8 ) & 0xff )
+
+/**
+ * Extract number of registers from CPUID setting tag
+ *
+ * @v tag Setting tag
+ * @ret num_registers Number of registers within register array
+ */
+#define CPUID_NUM_REGISTERS( tag ) ( ( ( (tag) >> 16 ) & 0x3 ) + 1 )
+
+/** CPUID settings scope */
+static const struct settings_scope cpuid_settings_scope;
+
+/**
+ * Check applicability of CPUID setting
+ *
+ * @v settings Settings block
+ * @v setting Setting
+ * @ret applies Setting applies within this settings block
+ */
+static int cpuid_settings_applies ( struct settings *settings __unused,
+ const struct setting *setting ) {
+
+ return ( setting->scope == &cpuid_settings_scope );
+}
+
+/**
+ * Fetch value of CPUID setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+static int cpuid_settings_fetch ( struct settings *settings,
+ struct setting *setting,
+ void *data, size_t len ) {
+ uint32_t function;
+ uint32_t max_function;
+ uint32_t num_functions;
+ uint32_t registers;
+ uint32_t num_registers;
+ uint32_t buf[4];
+ uint32_t output;
+ uint32_t discard_b;
+ uint32_t discard_c;
+ uint32_t discard_d;
+ size_t frag_len;
+ size_t result_len = 0;
+
+ /* Fail unless CPUID is supported */
+ if ( ! cpuid_is_supported() ) {
+ DBGC ( settings, "CPUID not supported\n" );
+ return -ENOTSUP;
+ }
+
+ /* Find highest supported function number within this set */
+ function = CPUID_FUNCTION ( setting->tag );
+ cpuid ( function & CPUID_EXTENDED, &max_function, &discard_b,
+ &discard_c, &discard_d );
+
+ /* Fail if maximum function number is meaningless (e.g. if we
+ * are attempting to call an extended function on a CPU which
+ * does not support them).
+ */
+ if ( ( max_function & CPUID_AMD_CHECK_MASK ) !=
+ ( function & CPUID_AMD_CHECK_MASK ) ) {
+ DBGC ( settings, "CPUID invalid maximum function\n" );
+ return -ENOTSUP;
+ }
+
+ /* Call each function in turn */
+ num_functions = CPUID_NUM_FUNCTIONS ( setting->tag );
+ for ( ; num_functions-- ; function++ ) {
+
+ /* Fail if this function is not supported */
+ if ( function > max_function ) {
+ DBGC ( settings, "CPUID function %#08x not supported\n",
+ function );
+ return -ENOTSUP;
+ }
+
+ /* Issue CPUID */
+ cpuid ( function, &buf[CPUID_EAX], &buf[CPUID_EBX],
+ &buf[CPUID_ECX], &buf[CPUID_EDX] );
+ DBGC ( settings, "CPUID %#08x => %#08x:%#08x:%#08x:%#08x\n",
+ function, buf[0], buf[1], buf[2], buf[3] );
+
+ /* Copy results to buffer */
+ registers = CPUID_REGISTERS ( setting->tag );
+ num_registers = CPUID_NUM_REGISTERS ( setting->tag );
+ for ( ; num_registers-- ; registers >>= 2 ) {
+ output = buf[ registers & 0x3 ];
+ if ( ! CPUID_LITTLE_ENDIAN ( setting->tag ) )
+ output = cpu_to_be32 ( output );
+ frag_len = sizeof ( output );
+ if ( frag_len > len )
+ frag_len = len;
+ memcpy ( data, &output, frag_len );
+ data += frag_len;
+ len -= frag_len;
+ result_len += sizeof ( output );
+ }
+ }
+
+ /* Set type if not already specified */
+ if ( ! setting->type )
+ setting->type = &setting_type_hexraw;
+
+ return result_len;
+}
+
+/** CPUID settings operations */
+static struct settings_operations cpuid_settings_operations = {
+ .applies = cpuid_settings_applies,
+ .fetch = cpuid_settings_fetch,
+};
+
+/** CPUID settings */
+static struct settings cpuid_settings = {
+ .refcnt = NULL,
+ .siblings = LIST_HEAD_INIT ( cpuid_settings.siblings ),
+ .children = LIST_HEAD_INIT ( cpuid_settings.children ),
+ .op = &cpuid_settings_operations,
+ .default_scope = &cpuid_settings_scope,
+};
+
+/** Initialise CPUID settings */
+static void cpuid_settings_init ( void ) {
+ int rc;
+
+ if ( ( rc = register_settings ( &cpuid_settings, NULL,
+ "cpuid" ) ) != 0 ) {
+ DBG ( "CPUID could not register settings: %s\n",
+ strerror ( rc ) );
+ return;
+ }
+}
+
+/** CPUID settings initialiser */
+struct init_fn cpuid_settings_init_fn __init_fn ( INIT_NORMAL ) = {
+ .initialise = cpuid_settings_init,
+};
+
+/** CPU vendor setting */
+const struct setting cpuvendor_setting __setting ( SETTING_HOST_EXTRA,
+ cpuvendor ) = {
+ .name = "cpuvendor",
+ .description = "CPU vendor",
+ .tag = CPUID_TAG ( CPUID_VENDOR_ID, 1, 1, 3,
+ CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ),
+ .type = &setting_type_string,
+ .scope = &cpuid_settings_scope,
+};
+
+/** CPU model setting */
+const struct setting cpumodel_setting __setting ( SETTING_HOST_EXTRA,
+ cpumodel ) = {
+ .name = "cpumodel",
+ .description = "CPU model",
+ .tag = CPUID_TAG ( CPUID_MODEL, 3, 1, 4,
+ CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ),
+ .type = &setting_type_string,
+ .scope = &cpuid_settings_scope,
+};