Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright © 2000 SuSE, Inc.
  3.  * Copyright © 2007 Red Hat, Inc.
  4.  *
  5.  * Permission to use, copy, modify, distribute, and sell this software and its
  6.  * documentation for any purpose is hereby granted without fee, provided that
  7.  * the above copyright notice appear in all copies and that both that
  8.  * copyright notice and this permission notice appear in supporting
  9.  * documentation, and that the name of SuSE not be used in advertising or
  10.  * publicity pertaining to distribution of the software without specific,
  11.  * written prior permission.  SuSE makes no representations about the
  12.  * suitability of this software for any purpose.  It is provided "as is"
  13.  * without express or implied warranty.
  14.  *
  15.  * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
  17.  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  18.  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  19.  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  20.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  21.  */
  22. #ifdef HAVE_CONFIG_H
  23. #include <config.h>
  24. #endif
  25.  
  26. #include <string.h>
  27.  
  28. #if defined(USE_ARM_SIMD) && defined(_MSC_VER)
  29. /* Needed for EXCEPTION_ILLEGAL_INSTRUCTION */
  30. #include <windows.h>
  31. #endif
  32.  
  33. #include "pixman-private.h"
  34.  
  35. #ifdef USE_VMX
  36.  
  37. /* The CPU detection code needs to be in a file not compiled with
  38.  * "-maltivec -mabi=altivec", as gcc would try to save vector register
  39.  * across function calls causing SIGILL on cpus without Altivec/vmx.
  40.  */
  41. static pixman_bool_t initialized = FALSE;
  42. static volatile pixman_bool_t have_vmx = TRUE;
  43.  
  44. #ifdef __APPLE__
  45. #include <sys/sysctl.h>
  46.  
  47. static pixman_bool_t
  48. pixman_have_vmx (void)
  49. {
  50.     if (!initialized)
  51.     {
  52.         size_t length = sizeof(have_vmx);
  53.         int error =
  54.             sysctlbyname ("hw.optional.altivec", &have_vmx, &length, NULL, 0);
  55.  
  56.         if (error)
  57.             have_vmx = FALSE;
  58.  
  59.         initialized = TRUE;
  60.     }
  61.     return have_vmx;
  62. }
  63.  
  64. #elif defined (__OpenBSD__)
  65. #include <sys/param.h>
  66. #include <sys/sysctl.h>
  67. #include <machine/cpu.h>
  68.  
  69. static pixman_bool_t
  70. pixman_have_vmx (void)
  71. {
  72.     if (!initialized)
  73.     {
  74.         int mib[2] = { CTL_MACHDEP, CPU_ALTIVEC };
  75.         size_t length = sizeof(have_vmx);
  76.         int error =
  77.             sysctl (mib, 2, &have_vmx, &length, NULL, 0);
  78.  
  79.         if (error != 0)
  80.             have_vmx = FALSE;
  81.  
  82.         initialized = TRUE;
  83.     }
  84.     return have_vmx;
  85. }
  86.  
  87. #elif defined (__linux__)
  88. #include <sys/types.h>
  89. #include <sys/stat.h>
  90. #include <fcntl.h>
  91. #include <unistd.h>
  92. #include <stdio.h>
  93. #include <linux/auxvec.h>
  94. #include <asm/cputable.h>
  95.  
  96. static pixman_bool_t
  97. pixman_have_vmx (void)
  98. {
  99.     if (!initialized)
  100.     {
  101.         char fname[64];
  102.         unsigned long buf[64];
  103.         ssize_t count = 0;
  104.         pid_t pid;
  105.         int fd, i;
  106.  
  107.         pid = getpid ();
  108.         snprintf (fname, sizeof(fname) - 1, "/proc/%d/auxv", pid);
  109.  
  110.         fd = open (fname, O_RDONLY);
  111.         if (fd >= 0)
  112.         {
  113.             for (i = 0; i <= (count / sizeof(unsigned long)); i += 2)
  114.             {
  115.                 /* Read more if buf is empty... */
  116.                 if (i == (count / sizeof(unsigned long)))
  117.                 {
  118.                     count = read (fd, buf, sizeof(buf));
  119.                     if (count <= 0)
  120.                         break;
  121.                     i = 0;
  122.                 }
  123.  
  124.                 if (buf[i] == AT_HWCAP)
  125.                 {
  126.                     have_vmx = !!(buf[i + 1] & PPC_FEATURE_HAS_ALTIVEC);
  127.                     initialized = TRUE;
  128.                     break;
  129.                 }
  130.                 else if (buf[i] == AT_NULL)
  131.                 {
  132.                     break;
  133.                 }
  134.             }
  135.             close (fd);
  136.         }
  137.     }
  138.     if (!initialized)
  139.     {
  140.         /* Something went wrong. Assume 'no' rather than playing
  141.            fragile tricks with catching SIGILL. */
  142.         have_vmx = FALSE;
  143.         initialized = TRUE;
  144.     }
  145.  
  146.     return have_vmx;
  147. }
  148.  
  149. #else /* !__APPLE__ && !__OpenBSD__ && !__linux__ */
  150. #include <signal.h>
  151. #include <setjmp.h>
  152.  
  153. static jmp_buf jump_env;
  154.  
  155. static void
  156. vmx_test (int        sig,
  157.           siginfo_t *si,
  158.           void *     unused)
  159. {
  160.     longjmp (jump_env, 1);
  161. }
  162.  
  163. static pixman_bool_t
  164. pixman_have_vmx (void)
  165. {
  166.     struct sigaction sa, osa;
  167.     int jmp_result;
  168.  
  169.     if (!initialized)
  170.     {
  171.         sa.sa_flags = SA_SIGINFO;
  172.         sigemptyset (&sa.sa_mask);
  173.         sa.sa_sigaction = vmx_test;
  174.         sigaction (SIGILL, &sa, &osa);
  175.         jmp_result = setjmp (jump_env);
  176.         if (jmp_result == 0)
  177.         {
  178.             asm volatile ( "vor 0, 0, 0" );
  179.         }
  180.         sigaction (SIGILL, &osa, NULL);
  181.         have_vmx = (jmp_result == 0);
  182.         initialized = TRUE;
  183.     }
  184.     return have_vmx;
  185. }
  186.  
  187. #endif /* __APPLE__ */
  188. #endif /* USE_VMX */
  189.  
  190. #if defined(USE_ARM_SIMD) || defined(USE_ARM_NEON)
  191.  
  192. #if defined(_MSC_VER)
  193.  
  194. #if defined(USE_ARM_SIMD)
  195. extern int pixman_msvc_try_arm_simd_op ();
  196.  
  197. pixman_bool_t
  198. pixman_have_arm_simd (void)
  199. {
  200.     static pixman_bool_t initialized = FALSE;
  201.     static pixman_bool_t have_arm_simd = FALSE;
  202.  
  203.     if (!initialized)
  204.     {
  205.         __try {
  206.             pixman_msvc_try_arm_simd_op ();
  207.             have_arm_simd = TRUE;
  208.         } __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION) {
  209.             have_arm_simd = FALSE;
  210.         }
  211.         initialized = TRUE;
  212.     }
  213.  
  214.     return have_arm_simd;
  215. }
  216.  
  217. #endif /* USE_ARM_SIMD */
  218.  
  219. #if defined(USE_ARM_NEON)
  220. extern int pixman_msvc_try_arm_neon_op ();
  221.  
  222. pixman_bool_t
  223. pixman_have_arm_neon (void)
  224. {
  225.     static pixman_bool_t initialized = FALSE;
  226.     static pixman_bool_t have_arm_neon = FALSE;
  227.  
  228.     if (!initialized)
  229.     {
  230.         __try
  231.         {
  232.             pixman_msvc_try_arm_neon_op ();
  233.             have_arm_neon = TRUE;
  234.         }
  235.         __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION)
  236.         {
  237.             have_arm_neon = FALSE;
  238.         }
  239.         initialized = TRUE;
  240.     }
  241.  
  242.     return have_arm_neon;
  243. }
  244.  
  245. #endif /* USE_ARM_NEON */
  246.  
  247. #else /* linux ELF */
  248.  
  249. #include <stdlib.h>
  250. #include <unistd.h>
  251. #include <sys/types.h>
  252. #include <sys/stat.h>
  253. #include <sys/mman.h>
  254. #include <fcntl.h>
  255. #include <string.h>
  256. #include <elf.h>
  257.  
  258. static pixman_bool_t arm_has_v7 = FALSE;
  259. static pixman_bool_t arm_has_v6 = FALSE;
  260. static pixman_bool_t arm_has_vfp = FALSE;
  261. static pixman_bool_t arm_has_neon = FALSE;
  262. static pixman_bool_t arm_has_iwmmxt = FALSE;
  263. static pixman_bool_t arm_tests_initialized = FALSE;
  264.  
  265. static void
  266. pixman_arm_read_auxv ()
  267. {
  268.     int fd;
  269.     Elf32_auxv_t aux;
  270.  
  271.     fd = open ("/proc/self/auxv", O_RDONLY);
  272.     if (fd >= 0)
  273.     {
  274.         while (read (fd, &aux, sizeof(Elf32_auxv_t)) == sizeof(Elf32_auxv_t))
  275.         {
  276.             if (aux.a_type == AT_HWCAP)
  277.             {
  278.                 uint32_t hwcap = aux.a_un.a_val;
  279.                 /* hardcode these values to avoid depending on specific
  280.                  * versions of the hwcap header, e.g. HWCAP_NEON
  281.                  */
  282.                 arm_has_vfp = (hwcap & 64) != 0;
  283.                 arm_has_iwmmxt = (hwcap & 512) != 0;
  284.                 /* this flag is only present on kernel 2.6.29 */
  285.                 arm_has_neon = (hwcap & 4096) != 0;
  286.             }
  287.             else if (aux.a_type == AT_PLATFORM)
  288.             {
  289.                 const char *plat = (const char*) aux.a_un.a_val;
  290.                 if (strncmp (plat, "v7l", 3) == 0)
  291.                 {
  292.                     arm_has_v7 = TRUE;
  293.                     arm_has_v6 = TRUE;
  294.                 }
  295.                 else if (strncmp (plat, "v6l", 3) == 0)
  296.                 {
  297.                     arm_has_v6 = TRUE;
  298.                 }
  299.             }
  300.         }
  301.         close (fd);
  302.     }
  303.  
  304.     arm_tests_initialized = TRUE;
  305. }
  306.  
  307. #if defined(USE_ARM_SIMD)
  308. pixman_bool_t
  309. pixman_have_arm_simd (void)
  310. {
  311.     if (!arm_tests_initialized)
  312.         pixman_arm_read_auxv ();
  313.  
  314.     return arm_has_v6;
  315. }
  316.  
  317. #endif /* USE_ARM_SIMD */
  318.  
  319. #if defined(USE_ARM_NEON)
  320. pixman_bool_t
  321. pixman_have_arm_neon (void)
  322. {
  323.     if (!arm_tests_initialized)
  324.         pixman_arm_read_auxv ();
  325.  
  326.     return arm_has_neon;
  327. }
  328.  
  329. #endif /* USE_ARM_NEON */
  330.  
  331. #endif /* linux */
  332.  
  333. #endif /* USE_ARM_SIMD || USE_ARM_NEON */
  334.  
  335. #if defined(USE_MMX) || defined(USE_SSE2)
  336. /* The CPU detection code needs to be in a file not compiled with
  337.  * "-mmmx -msse", as gcc would generate CMOV instructions otherwise
  338.  * that would lead to SIGILL instructions on old CPUs that don't have
  339.  * it.
  340.  */
  341. #if !defined(__amd64__) && !defined(__x86_64__) && !defined(_M_AMD64)
  342.  
  343. #ifdef HAVE_GETISAX
  344. #include <sys/auxv.h>
  345. #endif
  346.  
  347. typedef enum
  348. {
  349.     NO_FEATURES = 0,
  350.     MMX = 0x1,
  351.     MMX_EXTENSIONS = 0x2,
  352.     SSE = 0x6,
  353.     SSE2 = 0x8,
  354.     CMOV = 0x10
  355. } cpu_features_t;
  356.  
  357.  
  358. static unsigned int
  359. detect_cpu_features (void)
  360. {
  361.     unsigned int features = 0;
  362.     unsigned int result = 0;
  363.  
  364. #ifdef HAVE_GETISAX
  365.     if (getisax (&result, 1))
  366.     {
  367.         if (result & AV_386_CMOV)
  368.             features |= CMOV;
  369.         if (result & AV_386_MMX)
  370.             features |= MMX;
  371.         if (result & AV_386_AMD_MMX)
  372.             features |= MMX_EXTENSIONS;
  373.         if (result & AV_386_SSE)
  374.             features |= SSE;
  375.         if (result & AV_386_SSE2)
  376.             features |= SSE2;
  377.     }
  378. #else
  379.     char vendor[13];
  380. #ifdef _MSC_VER
  381.     int vendor0 = 0, vendor1, vendor2;
  382. #endif
  383.     vendor[0] = 0;
  384.     vendor[12] = 0;
  385.  
  386. #ifdef __GNUC__
  387.     /* see p. 118 of amd64 instruction set manual Vol3 */
  388.     /* We need to be careful about the handling of %ebx and
  389.      * %esp here. We can't declare either one as clobbered
  390.      * since they are special registers (%ebx is the "PIC
  391.      * register" holding an offset to global data, %esp the
  392.      * stack pointer), so we need to make sure they have their
  393.      * original values when we access the output operands.
  394.      */
  395.     __asm__ (
  396.         "pushf\n"
  397.         "pop %%eax\n"
  398.         "mov %%eax, %%ecx\n"
  399.         "xor $0x00200000, %%eax\n"
  400.         "push %%eax\n"
  401.         "popf\n"
  402.         "pushf\n"
  403.         "pop %%eax\n"
  404.         "mov $0x0, %%edx\n"
  405.         "xor %%ecx, %%eax\n"
  406.         "jz 1f\n"
  407.  
  408.         "mov $0x00000000, %%eax\n"
  409.         "push %%ebx\n"
  410.         "cpuid\n"
  411.         "mov %%ebx, %%eax\n"
  412.         "pop %%ebx\n"
  413.         "mov %%eax, %1\n"
  414.         "mov %%edx, %2\n"
  415.         "mov %%ecx, %3\n"
  416.         "mov $0x00000001, %%eax\n"
  417.         "push %%ebx\n"
  418.         "cpuid\n"
  419.         "pop %%ebx\n"
  420.         "1:\n"
  421.         "mov %%edx, %0\n"
  422.         : "=r" (result),
  423.         "=m" (vendor[0]),
  424.         "=m" (vendor[4]),
  425.         "=m" (vendor[8])
  426.         :
  427.         : "%eax", "%ecx", "%edx"
  428.         );
  429.  
  430. #elif defined (_MSC_VER)
  431.  
  432.     _asm {
  433.         pushfd
  434.         pop eax
  435.         mov ecx, eax
  436.         xor eax, 00200000h
  437.         push eax
  438.         popfd
  439.         pushfd
  440.         pop eax
  441.         mov edx, 0
  442.         xor eax, ecx
  443.         jz nocpuid
  444.  
  445.         mov eax, 0
  446.         push ebx
  447.         cpuid
  448.         mov eax, ebx
  449.         pop ebx
  450.         mov vendor0, eax
  451.         mov vendor1, edx
  452.         mov vendor2, ecx
  453.         mov eax, 1
  454.         push ebx
  455.         cpuid
  456.         pop ebx
  457.     nocpuid:
  458.         mov result, edx
  459.     }
  460.     memmove (vendor + 0, &vendor0, 4);
  461.     memmove (vendor + 4, &vendor1, 4);
  462.     memmove (vendor + 8, &vendor2, 4);
  463.  
  464. #else
  465. #   error unsupported compiler
  466. #endif
  467.  
  468.     features = 0;
  469.     if (result)
  470.     {
  471.         /* result now contains the standard feature bits */
  472.         if (result & (1 << 15))
  473.             features |= CMOV;
  474.         if (result & (1 << 23))
  475.             features |= MMX;
  476.         if (result & (1 << 25))
  477.             features |= SSE;
  478.         if (result & (1 << 26))
  479.             features |= SSE2;
  480.         if ((features & MMX) && !(features & SSE) &&
  481.             (strcmp (vendor, "AuthenticAMD") == 0 ||
  482.              strcmp (vendor, "Geode by NSC") == 0))
  483.         {
  484.             /* check for AMD MMX extensions */
  485. #ifdef __GNUC__
  486.             __asm__ (
  487.                 "       push %%ebx\n"
  488.                 "       mov $0x80000000, %%eax\n"
  489.                 "       cpuid\n"
  490.                 "       xor %%edx, %%edx\n"
  491.                 "       cmp $0x1, %%eax\n"
  492.                 "       jge 2f\n"
  493.                 "       mov $0x80000001, %%eax\n"
  494.                 "       cpuid\n"
  495.                 "2:\n"
  496.                 "       pop %%ebx\n"
  497.                 "       mov %%edx, %0\n"
  498.                 : "=r" (result)
  499.                 :
  500.                 : "%eax", "%ecx", "%edx"
  501.                 );
  502. #elif defined _MSC_VER
  503.             _asm {
  504.                 push ebx
  505.                 mov eax, 80000000h
  506.                 cpuid
  507.                 xor edx, edx
  508.                 cmp eax, 1
  509.                 jge notamd
  510.                 mov eax, 80000001h
  511.                 cpuid
  512.             notamd:
  513.                 pop ebx
  514.                 mov result, edx
  515.             }
  516. #endif
  517.             if (result & (1 << 22))
  518.                 features |= MMX_EXTENSIONS;
  519.         }
  520.     }
  521. #endif /* HAVE_GETISAX */
  522.  
  523.     return features;
  524. }
  525.  
  526. static pixman_bool_t
  527. pixman_have_mmx (void)
  528. {
  529.     static pixman_bool_t initialized = FALSE;
  530.     static pixman_bool_t mmx_present;
  531.  
  532.     if (!initialized)
  533.     {
  534.         unsigned int features = detect_cpu_features ();
  535.         mmx_present = (features & (MMX | MMX_EXTENSIONS)) == (MMX | MMX_EXTENSIONS);
  536.         initialized = TRUE;
  537.     }
  538.  
  539.     return mmx_present;
  540. }
  541.  
  542. #ifdef USE_SSE2
  543. static pixman_bool_t
  544. pixman_have_sse2 (void)
  545. {
  546.     static pixman_bool_t initialized = FALSE;
  547.     static pixman_bool_t sse2_present;
  548.  
  549.     if (!initialized)
  550.     {
  551.         unsigned int features = detect_cpu_features ();
  552.         sse2_present = (features & (MMX | MMX_EXTENSIONS | SSE | SSE2)) == (MMX | MMX_EXTENSIONS | SSE | SSE2);
  553.         initialized = TRUE;
  554.     }
  555.  
  556.     return sse2_present;
  557. }
  558.  
  559. #endif
  560.  
  561. #else /* __amd64__ */
  562. #ifdef USE_MMX
  563. #define pixman_have_mmx() TRUE
  564. #endif
  565. #ifdef USE_SSE2
  566. #define pixman_have_sse2() TRUE
  567. #endif
  568. #endif /* __amd64__ */
  569. #endif
  570.  
  571. pixman_implementation_t *
  572. _pixman_choose_implementation (void)
  573. {
  574. #ifdef USE_SSE2
  575.     if (pixman_have_sse2 ())
  576.         return _pixman_implementation_create_sse2 ();
  577. #endif
  578. #ifdef USE_MMX
  579.     if (pixman_have_mmx ())
  580.         return _pixman_implementation_create_mmx ();
  581. #endif
  582.  
  583. #ifdef USE_ARM_NEON
  584.     if (pixman_have_arm_neon ())
  585.         return _pixman_implementation_create_arm_neon ();
  586. #endif
  587. #ifdef USE_ARM_SIMD
  588.     if (pixman_have_arm_simd ())
  589.         return _pixman_implementation_create_arm_simd ();
  590. #endif
  591. #ifdef USE_VMX
  592.     if (pixman_have_vmx ())
  593.         return _pixman_implementation_create_vmx ();
  594. #endif
  595.  
  596.     return _pixman_implementation_create_fast_path ();
  597. }
  598.  
  599.