]> git.sesse.net Git - vlc/blob - src/misc/cpu.c
* ./src/misc/cpu.c: we use void(*)(int) instead of sighandler_t because
[vlc] / src / misc / cpu.c
1 /*****************************************************************************
2  * cpu.c: CPU detection code
3  *****************************************************************************
4  * Copyright (C) 1998-2002 VideoLAN
5  * $Id: cpu.c,v 1.6 2002/08/19 11:37:57 sam Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Eugenio Jarosiewicz <ej0@cise.ufl.eduEujenio>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <signal.h>                               /* SIGHUP, SIGINT, SIGKILL */
30 #include <setjmp.h>                                       /* longjmp, setjmp */
31
32 #include <vlc/vlc.h>
33
34 #ifdef SYS_DARWIN
35 #   include <mach/mach.h>                               /* AltiVec detection */
36 #   include <mach/mach_error.h>       /* some day the header files||compiler *
37                                                        will define it for us */
38 #   include <mach/bootstrap.h>
39 #endif
40
41 #include "vlc_cpu.h"
42
43 /*****************************************************************************
44  * Local prototypes
45  *****************************************************************************/
46 static void SigHandler   ( int );
47 static u32  Capabilities ( vlc_object_t * );
48
49 /*****************************************************************************
50  * Global variables - they're needed for signal handling
51  *****************************************************************************/
52 static jmp_buf env;
53 static int     i_illegal;
54 #if defined( __i386__ )
55 static char   *psz_capability;
56 #endif
57
58 /*****************************************************************************
59  * CPUCapabilities: get the CPU capabilities
60  *****************************************************************************
61  * This function is a wrapper around Capabilities().
62  *****************************************************************************/
63 u32 __CPUCapabilities( vlc_object_t *p_this )
64 {
65     u32 i_capabilities;
66
67     vlc_mutex_lock( p_this->p_vlc->p_global_lock );
68     i_capabilities = Capabilities( p_this );
69     vlc_mutex_unlock( p_this->p_vlc->p_global_lock );
70     
71     return i_capabilities;
72 }
73
74 /*****************************************************************************
75  * Capabilities: list the processors MMX support and other capabilities
76  *****************************************************************************
77  * This function is called to list extensions the CPU may have.
78  *****************************************************************************/
79 static u32 Capabilities( vlc_object_t *p_this )
80 {
81     volatile u32 i_capabilities = CPU_CAPABILITY_NONE;
82
83 #if defined( SYS_DARWIN )
84     struct host_basic_info hi;
85     kern_return_t          ret;
86     host_name_port_t       host;
87
88     int i_size;
89     char *psz_name, *psz_subname;
90
91     i_capabilities |= CPU_CAPABILITY_FPU;
92
93     /* Should 'never' fail? */
94     host = mach_host_self();
95
96     i_size = sizeof( hi ) / sizeof( int );
97     ret = host_info( host, HOST_BASIC_INFO, ( host_info_t )&hi, &i_size );
98
99     if( ret != KERN_SUCCESS )
100     {
101         fprintf( stderr, "error: couldn't get CPU information\n" );
102         return i_capabilities;
103     }
104
105     slot_name( hi.cpu_type, hi.cpu_subtype, &psz_name, &psz_subname );
106     /* FIXME: need better way to detect newer proccessors.
107      * could do strncmp(a,b,5), but that's real ugly */
108     if( !strcmp(psz_name, "ppc7400") || !strcmp(psz_name, "ppc7450") )
109     {
110         i_capabilities |= CPU_CAPABILITY_ALTIVEC;
111     }
112
113     return i_capabilities;
114
115 #elif defined( __i386__ )
116     volatile unsigned int  i_eax, i_ebx, i_ecx, i_edx;
117     volatile vlc_bool_t    b_amd;
118
119     /* Needed for x86 CPU capabilities detection */
120 #   define cpuid( a )                      \
121         asm volatile ( "pushl %%ebx\n\t"   \
122                        "cpuid\n\t"         \
123                        "movl %%ebx,%1\n\t" \
124                        "popl %%ebx\n\t"    \
125                      : "=a" ( i_eax ),     \
126                        "=r" ( i_ebx ),     \
127                        "=c" ( i_ecx ),     \
128                        "=d" ( i_edx )      \
129                      : "a"  ( a )          \
130                      : "cc" );
131
132 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW )
133     void (*pf_sigill) (int) = signal( SIGILL, SigHandler );
134 #   endif
135
136     i_capabilities |= CPU_CAPABILITY_FPU;
137
138     /* test for a 486 CPU */
139     asm volatile ( "pushl %%ebx\n\t"
140                    "pushfl\n\t"
141                    "popl %%eax\n\t"
142                    "movl %%eax, %%ebx\n\t"
143                    "xorl $0x200000, %%eax\n\t"
144                    "pushl %%eax\n\t"
145                    "popfl\n\t"
146                    "pushfl\n\t"
147                    "popl %%eax\n\t"
148                    "movl %%ebx,%1\n\t"
149                    "popl %%ebx\n\t"
150                  : "=a" ( i_eax ),
151                    "=r" ( i_ebx )
152                  :
153                  : "cc" );
154
155     if( i_eax == i_ebx )
156     {
157 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW )
158         signal( SIGILL, pf_sigill );
159 #   endif
160         return i_capabilities;
161     }
162
163     i_capabilities |= CPU_CAPABILITY_486;
164
165     /* the CPU supports the CPUID instruction - get its level */
166     cpuid( 0x00000000 );
167
168     if( !i_eax )
169     {
170 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW )
171         signal( SIGILL, pf_sigill );
172 #   endif
173         return i_capabilities;
174     }
175
176     /* FIXME: this isn't correct, since some 486s have cpuid */
177     i_capabilities |= CPU_CAPABILITY_586;
178
179     /* borrowed from mpeg2dec */
180     b_amd = ( i_ebx == 0x68747541 ) && ( i_ecx == 0x444d4163 )
181                     && ( i_edx == 0x69746e65 );
182
183     /* test for the MMX flag */
184     cpuid( 0x00000001 );
185
186     if( ! (i_edx & 0x00800000) )
187     {
188 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW )
189         signal( SIGILL, pf_sigill );
190 #   endif
191         return i_capabilities;
192     }
193
194     i_capabilities |= CPU_CAPABILITY_MMX;
195
196     if( i_edx & 0x02000000 )
197     {
198         i_capabilities |= CPU_CAPABILITY_MMXEXT;
199
200 #   ifdef CAN_COMPILE_SSE
201         /* We test if OS supports the SSE instructions */
202         psz_capability = "SSE";
203         i_illegal = 0;
204
205         if( setjmp( env ) == 0 )
206         {
207             /* Test a SSE instruction */
208             __asm__ __volatile__ ( "xorps %%xmm0,%%xmm0\n" : : );
209         }
210
211         if( i_illegal == 0 )
212         {
213             i_capabilities |= CPU_CAPABILITY_SSE;
214         }
215 #   endif
216     }
217
218     /* test for additional capabilities */
219     cpuid( 0x80000000 );
220
221     if( i_eax < 0x80000001 )
222     {
223 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW )
224         signal( SIGILL, pf_sigill );
225 #   endif
226         return i_capabilities;
227     }
228
229     /* list these additional capabilities */
230     cpuid( 0x80000001 );
231
232 #   ifdef CAN_COMPILE_3DNOW
233     if( i_edx & 0x80000000 )
234     {
235         psz_capability = "3D Now!";
236         i_illegal = 0;
237
238         if( setjmp( env ) == 0 )
239         {
240             /* Test a 3D Now! instruction */
241             __asm__ __volatile__ ( "pfadd %%mm0,%%mm0\n" "femms\n" : : );
242         }
243
244         if( i_illegal == 0 )
245         {
246             i_capabilities |= CPU_CAPABILITY_3DNOW;
247         }
248     }
249 #   endif
250
251     if( b_amd && ( i_edx & 0x00400000 ) )
252     {
253         i_capabilities |= CPU_CAPABILITY_MMXEXT;
254     }
255
256 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW )
257     signal( SIGILL, pf_sigill );
258 #   endif
259     return i_capabilities;
260
261 #elif defined( __powerpc__ )
262
263 #   ifdef CAN_COMPILE_ALTIVEC
264     void (*pf_sigill) (int) = signal( SIGILL, SigHandler );
265
266     i_capabilities |= CPU_CAPABILITY_FPU;
267
268     i_illegal = 0;
269
270     if( setjmp( env ) == 0 )
271     {
272         asm volatile ("mtspr 256, %0\n\t"
273                       "vand %%v0, %%v0, %%v0"
274                       :
275                       : "r" (-1));
276     }
277
278     if( i_illegal == 0 )
279     {
280         i_capabilities |= CPU_CAPABILITY_ALTIVEC;
281     }
282
283     signal( SIGILL, pf_sigill );
284 #   endif
285
286     return i_capabilities;
287
288 #elif defined( __sparc__ )
289
290     i_capabilities |= CPU_CAPABILITY_FPU;
291     return i_capabilities;
292
293 #else
294     /* default behaviour */
295     return i_capabilities;
296
297 #endif
298 }
299
300 /*****************************************************************************
301  * SigHandler: system signal handler
302  *****************************************************************************
303  * This function is called when an illegal instruction signal is received by
304  * the program. We use this function to test OS and CPU capabilities
305  *****************************************************************************/
306 static void SigHandler( int i_signal )
307 {
308     /* Acknowledge the signal received */
309     i_illegal = 1;
310
311 #ifdef HAVE_SIGRELSE
312     sigrelse( i_signal );
313 #endif
314
315 #if defined( __i386__ )
316     fprintf( stderr, "warning: your CPU has %s instructions, but not your "
317                      "operating system.\n", psz_capability );
318     fprintf( stderr, "         some optimizations will be disabled unless "
319                      "you upgrade your OS\n" );
320 #   if defined( SYS_LINUX )
321     fprintf( stderr, "         (for instance Linux kernel 2.4.x or later)\n" );
322 #   endif
323 #endif
324
325     longjmp( env, 1 );
326 }
327