Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

cpu.c

00001 /*****************************************************************************
00002  * cpu.c: CPU detection code
00003  *****************************************************************************
00004  * Copyright (C) 1998-2004 the VideoLAN team
00005  * $Id: cpu.c 12727 2005-10-01 23:32:39Z sam $
00006  *
00007  * Authors: Samuel Hocevar <[email protected]>
00008  *          Christophe Massiot <[email protected]>
00009  *          Eugenio Jarosiewicz <[email protected]>
00010  *
00011  * This program is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00024  *****************************************************************************/
00025 
00026 /*****************************************************************************
00027  * Preamble
00028  *****************************************************************************/
00029 #include <vlc/vlc.h>
00030 
00031 #ifdef HAVE_SIGNAL_H
00032 #   include <signal.h>                            /* SIGHUP, SIGINT, SIGKILL */
00033 #   include <setjmp.h>                                    /* longjmp, setjmp */
00034 #endif
00035 
00036 #ifdef SYS_DARWIN
00037 #include <sys/sysctl.h>
00038 #endif
00039 
00040 #include "vlc_cpu.h"
00041 
00042 /*****************************************************************************
00043  * Local prototypes
00044  *****************************************************************************/
00045 #ifdef HAVE_SIGNAL_H
00046 static void SigHandler   ( int );
00047 #endif
00048 
00049 /*****************************************************************************
00050  * Global variables - they're needed for signal handling
00051  *****************************************************************************/
00052 #ifdef HAVE_SIGNAL_H
00053 static jmp_buf env;
00054 static int     i_illegal;
00055 #if defined( __i386__ ) || defined( __x86_64__ )
00056 static char   *psz_capability;
00057 #endif
00058 #endif
00059 
00060 /*****************************************************************************
00061  * CPUCapabilities: get the CPU capabilities
00062  *****************************************************************************
00063  * This function is called to list extensions the CPU may have.
00064  *****************************************************************************/
00065 uint32_t CPUCapabilities( void )
00066 {
00067     volatile uint32_t i_capabilities = CPU_CAPABILITY_NONE;
00068 
00069 #if defined( SYS_DARWIN )
00070     int selectors[2] = { CTL_HW, HW_VECTORUNIT };
00071     int i_has_altivec = 0;
00072     size_t i_length = sizeof( i_has_altivec );
00073     int i_error = sysctl( selectors, 2, &i_has_altivec, &i_length, NULL, 0);
00074 
00075     i_capabilities |= CPU_CAPABILITY_FPU;
00076 
00077     if( i_error == 0 && i_has_altivec != 0 )
00078         i_capabilities |= CPU_CAPABILITY_ALTIVEC;
00079 
00080     return i_capabilities;
00081 
00082 #elif defined( __i386__ ) || defined( __x86_64__ )
00083     volatile unsigned int  i_eax, i_ebx, i_ecx, i_edx;
00084     volatile vlc_bool_t    b_amd;
00085 
00086     /* Needed for x86 CPU capabilities detection */
00087 #   if defined( __x86_64__ )
00088 #       define cpuid( reg )                    \
00089             asm volatile ( "cpuid\n\t"         \
00090                            "movl %%ebx,%1\n\t" \
00091                          : "=a" ( i_eax ),     \
00092                            "=b" ( i_ebx ),     \
00093                            "=c" ( i_ecx ),     \
00094                            "=d" ( i_edx )      \
00095                          : "a"  ( reg )        \
00096                          : "cc" );
00097 #   else
00098 #       define cpuid( reg )                    \
00099             asm volatile ( "push %%ebx\n\t"    \
00100                            "cpuid\n\t"         \
00101                            "movl %%ebx,%1\n\t" \
00102                            "pop %%ebx\n\t"     \
00103                          : "=a" ( i_eax ),     \
00104                            "=r" ( i_ebx ),     \
00105                            "=c" ( i_ecx ),     \
00106                            "=d" ( i_edx )      \
00107                          : "a"  ( reg )        \
00108                          : "cc" );
00109 #   endif
00110 
00111 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
00112      && defined( HAVE_SIGNAL_H )
00113     void (*pf_sigill) (int) = signal( SIGILL, SigHandler );
00114 #   endif
00115 
00116     i_capabilities |= CPU_CAPABILITY_FPU;
00117 
00118 #   if defined( __i386__ )
00119     /* check if cpuid instruction is supported */
00120     asm volatile ( "push %%ebx\n\t"
00121                    "pushf\n\t"
00122                    "pop %%eax\n\t"
00123                    "movl %%eax, %%ebx\n\t"
00124                    "xorl $0x200000, %%eax\n\t"
00125                    "push %%eax\n\t"
00126                    "popf\n\t"
00127                    "pushf\n\t"
00128                    "pop %%eax\n\t"
00129                    "movl %%ebx,%1\n\t"
00130                    "pop %%ebx\n\t"
00131                  : "=a" ( i_eax ),
00132                    "=r" ( i_ebx )
00133                  :
00134                  : "cc" );
00135 
00136     if( i_eax == i_ebx )
00137     {
00138 #       if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
00139             && defined( HAVE_SIGNAL_H )
00140         signal( SIGILL, pf_sigill );
00141 #       endif
00142         return i_capabilities;
00143     }
00144 #   else
00145     /* x86_64 supports cpuid instruction, so we dont need to check it */
00146 #   endif
00147 
00148     i_capabilities |= CPU_CAPABILITY_486;
00149 
00150     /* the CPU supports the CPUID instruction - get its level */
00151     cpuid( 0x00000000 );
00152 
00153     if( !i_eax )
00154     {
00155 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
00156      && defined( HAVE_SIGNAL_H )
00157         signal( SIGILL, pf_sigill );
00158 #   endif
00159         return i_capabilities;
00160     }
00161 
00162     /* FIXME: this isn't correct, since some 486s have cpuid */
00163     i_capabilities |= CPU_CAPABILITY_586;
00164 
00165     /* borrowed from mpeg2dec */
00166     b_amd = ( i_ebx == 0x68747541 ) && ( i_ecx == 0x444d4163 )
00167                     && ( i_edx == 0x69746e65 );
00168 
00169     /* test for the MMX flag */
00170     cpuid( 0x00000001 );
00171 
00172     if( ! (i_edx & 0x00800000) )
00173     {
00174 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
00175      && defined( HAVE_SIGNAL_H )
00176         signal( SIGILL, pf_sigill );
00177 #   endif
00178         return i_capabilities;
00179     }
00180 
00181     i_capabilities |= CPU_CAPABILITY_MMX;
00182 
00183     if( i_edx & 0x02000000 )
00184     {
00185         i_capabilities |= CPU_CAPABILITY_MMXEXT;
00186 
00187 #   ifdef CAN_COMPILE_SSE
00188         /* We test if OS supports the SSE instructions */
00189         psz_capability = "SSE";
00190         i_illegal = 0;
00191 
00192         if( setjmp( env ) == 0 )
00193         {
00194             /* Test a SSE instruction */
00195             __asm__ __volatile__ ( "xorps %%xmm0,%%xmm0\n" : : );
00196         }
00197 
00198         if( i_illegal == 0 )
00199         {
00200             i_capabilities |= CPU_CAPABILITY_SSE;
00201         }
00202 #   endif
00203     }
00204 
00205     if( i_edx & 0x04000000 )
00206     {
00207 #   if defined(CAN_COMPILE_SSE)
00208         /* We test if OS supports the SSE instructions */
00209         psz_capability = "SSE2";
00210         i_illegal = 0;
00211 
00212         if( setjmp( env ) == 0 )
00213         {
00214             /* Test a SSE2 instruction */
00215             __asm__ __volatile__ ( "movupd %%xmm0, %%xmm0\n" : : );
00216         }
00217 
00218         if( i_illegal == 0 )
00219         {
00220             i_capabilities |= CPU_CAPABILITY_SSE2;
00221         }
00222 #   endif
00223     }
00224 
00225     /* test for additional capabilities */
00226     cpuid( 0x80000000 );
00227 
00228     if( i_eax < 0x80000001 )
00229     {
00230 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
00231      && defined( HAVE_SIGNAL_H )
00232         signal( SIGILL, pf_sigill );
00233 #   endif
00234         return i_capabilities;
00235     }
00236 
00237     /* list these additional capabilities */
00238     cpuid( 0x80000001 );
00239 
00240 #   ifdef CAN_COMPILE_3DNOW
00241     if( i_edx & 0x80000000 )
00242     {
00243         psz_capability = "3D Now!";
00244         i_illegal = 0;
00245 
00246         if( setjmp( env ) == 0 )
00247         {
00248             /* Test a 3D Now! instruction */
00249             __asm__ __volatile__ ( "pfadd %%mm0,%%mm0\n" "femms\n" : : );
00250         }
00251 
00252         if( i_illegal == 0 )
00253         {
00254             i_capabilities |= CPU_CAPABILITY_3DNOW;
00255         }
00256     }
00257 #   endif
00258 
00259     if( b_amd && ( i_edx & 0x00400000 ) )
00260     {
00261         i_capabilities |= CPU_CAPABILITY_MMXEXT;
00262     }
00263 
00264 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
00265      && defined( HAVE_SIGNAL_H )
00266     signal( SIGILL, pf_sigill );
00267 #   endif
00268     return i_capabilities;
00269 
00270 #elif defined( __powerpc__ )
00271 
00272 #   ifdef CAN_COMPILE_ALTIVEC && defined( HAVE_SIGNAL_H )
00273     void (*pf_sigill) (int) = signal( SIGILL, SigHandler );
00274 
00275     i_capabilities |= CPU_CAPABILITY_FPU;
00276 
00277     i_illegal = 0;
00278 
00279     if( setjmp( env ) == 0 )
00280     {
00281         asm volatile ("mtspr 256, %0\n\t"
00282                       "vand %%v0, %%v0, %%v0"
00283                       :
00284                       : "r" (-1));
00285     }
00286 
00287     if( i_illegal == 0 )
00288     {
00289         i_capabilities |= CPU_CAPABILITY_ALTIVEC;
00290     }
00291 
00292     signal( SIGILL, pf_sigill );
00293 #   endif
00294 
00295     return i_capabilities;
00296 
00297 #elif defined( __sparc__ )
00298 
00299     i_capabilities |= CPU_CAPABILITY_FPU;
00300     return i_capabilities;
00301 
00302 #elif defined( _MSC_VER ) && !defined( UNDER_CE )
00303     i_capabilities |= CPU_CAPABILITY_FPU;
00304     return i_capabilities;
00305 
00306 #else
00307     /* default behaviour */
00308     return i_capabilities;
00309 
00310 #endif
00311 }
00312 
00313 /*****************************************************************************
00314  * SigHandler: system signal handler
00315  *****************************************************************************
00316  * This function is called when an illegal instruction signal is received by
00317  * the program. We use this function to test OS and CPU capabilities
00318  *****************************************************************************/
00319 #if defined( HAVE_SIGNAL_H )
00320 static void SigHandler( int i_signal )
00321 {
00322     /* Acknowledge the signal received */
00323     i_illegal = 1;
00324 
00325 #ifdef HAVE_SIGRELSE
00326     sigrelse( i_signal );
00327 #endif
00328 
00329 #if defined( __i386__ )
00330     fprintf( stderr, "warning: your CPU has %s instructions, but not your "
00331                      "operating system.\n", psz_capability );
00332     fprintf( stderr, "         some optimizations will be disabled unless "
00333                      "you upgrade your OS\n" );
00334 #   if defined( SYS_LINUX )
00335     fprintf( stderr, "         (for instance Linux kernel 2.4.x or later)\n" );
00336 #   endif
00337 #endif
00338 
00339     longjmp( env, 1 );
00340 }
00341 #endif
00342 

Generated on Tue Dec 20 10:15:00 2005 for vlc-0.8.4a by  doxygen 1.4.2