iPXE
cpuid.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <string.h>
00027 #include <errno.h>
00028 #include <ipxe/cpuid.h>
00029 
00030 /** @file
00031  *
00032  * x86 CPU feature detection
00033  *
00034  */
00035 
00036 /** Colour for debug messages */
00037 #define colour 0x861d
00038 
00039 /**
00040  * Check whether or not CPUID instruction is supported
00041  *
00042  * @ret rc              Return status code
00043  */
00044 static int cpuid_instruction_supported ( void ) {
00045         unsigned long original;
00046         unsigned long inverted;
00047 
00048         /* Check for instruction existence via flag modifiability */
00049         __asm__ ( "pushf\n\t"
00050                   "pushf\n\t"
00051                   "pop %0\n\t"
00052                   "mov %0,%1\n\t"
00053                   "xor %2,%1\n\t"
00054                   "push %1\n\t"
00055                   "popf\n\t"
00056                   "pushf\n\t"
00057                   "pop %1\n\t"
00058                   "popf\n\t"
00059                   : "=&r" ( original ), "=&r" ( inverted )
00060                   : "ir" ( CPUID_FLAG ) );
00061         if ( ! ( ( original ^ inverted ) & CPUID_FLAG ) ) {
00062                 DBGC ( colour, "CPUID instruction is not supported\n" );
00063                 return -ENOTSUP;
00064         }
00065 
00066         return 0;
00067 }
00068 
00069 /**
00070  * Check whether or not CPUID function is supported
00071  *
00072  * @v function          CPUID function
00073  * @ret rc              Return status code
00074  */
00075 int cpuid_supported ( uint32_t function ) {
00076         uint32_t max_function;
00077         uint32_t discard_b;
00078         uint32_t discard_c;
00079         uint32_t discard_d;
00080         int rc;
00081 
00082         /* Check that CPUID instruction is available */
00083         if ( ( rc = cpuid_instruction_supported() ) != 0 )
00084                 return rc;
00085 
00086         /* Find highest supported function number within this family */
00087         cpuid ( ( function & CPUID_EXTENDED ), 0, &max_function, &discard_b,
00088                 &discard_c, &discard_d );
00089 
00090         /* Fail if maximum function number is meaningless (e.g. if we
00091          * are attempting to call an extended function on a CPU which
00092          * does not support them).
00093          */
00094         if ( ( max_function & CPUID_AMD_CHECK_MASK ) !=
00095              ( function & CPUID_AMD_CHECK_MASK ) ) {
00096                 DBGC ( colour, "CPUID invalid maximum function %#08x\n",
00097                        max_function );
00098                 return -EINVAL;
00099         }
00100 
00101         /* Fail if this function is not supported */
00102         if ( function > max_function ) {
00103                 DBGC ( colour, "CPUID function %#08x not supported\n",
00104                        function );
00105                 return -ENOTTY;
00106         }
00107 
00108         return 0;
00109 }
00110 
00111 /**
00112  * Get Intel-defined x86 CPU features
00113  *
00114  * @v features          x86 CPU features to fill in
00115  */
00116 static void x86_intel_features ( struct x86_features *features ) {
00117         uint32_t discard_a;
00118         uint32_t discard_b;
00119         int rc;
00120 
00121         /* Check that features are available via CPUID */
00122         if ( ( rc = cpuid_supported ( CPUID_FEATURES ) ) != 0 ) {
00123                 DBGC ( features, "CPUID has no Intel-defined features\n" );
00124                 return;
00125         }
00126 
00127         /* Get features */
00128         cpuid ( CPUID_FEATURES, 0, &discard_a, &discard_b,
00129                 &features->intel.ecx, &features->intel.edx );
00130         DBGC ( features, "CPUID Intel features: %%ecx=%08x, %%edx=%08x\n",
00131                features->intel.ecx, features->intel.edx );
00132 
00133 }
00134 
00135 /**
00136  * Get AMD-defined x86 CPU features
00137  *
00138  * @v features          x86 CPU features to fill in
00139  */
00140 static void x86_amd_features ( struct x86_features *features ) {
00141         uint32_t discard_a;
00142         uint32_t discard_b;
00143         int rc;
00144 
00145         /* Check that features are available via CPUID */
00146         if ( ( rc = cpuid_supported ( CPUID_AMD_FEATURES ) ) != 0 ) {
00147                 DBGC ( features, "CPUID has no AMD-defined features\n" );
00148                 return;
00149         }
00150 
00151         /* Get features */
00152         cpuid ( CPUID_AMD_FEATURES, 0, &discard_a, &discard_b,
00153                 &features->amd.ecx, &features->amd.edx );
00154         DBGC ( features, "CPUID AMD features: %%ecx=%08x, %%edx=%08x\n",
00155                features->amd.ecx, features->amd.edx );
00156 }
00157 
00158 /**
00159  * Get x86 CPU features
00160  *
00161  * @v features          x86 CPU features to fill in
00162  */
00163 void x86_features ( struct x86_features *features ) {
00164 
00165         /* Clear all features */
00166         memset ( features, 0, sizeof ( *features ) );
00167 
00168         /* Get Intel-defined features */
00169         x86_intel_features ( features );
00170 
00171         /* Get AMD-defined features */
00172         x86_amd_features ( features );
00173 }