Home | History | Annotate | Download | only in arm-neon
      1 /* contrib/arm-neon/linux-auxv.c
      2  *
      3  * Copyright (c) 2014 Glenn Randers-Pehrson
      4  * Written by Mans Rullgard, 2011.
      5  * Last changed in libpng 1.6.10 [March 6, 2014]
      6  *
      7  * This code is released under the libpng license.
      8  * For conditions of distribution and use, see the disclaimer
      9  * and license in png.h
     10  *
     11  * SEE contrib/arm-neon/README before reporting bugs
     12  *
     13  * STATUS: COMPILED, TESTED
     14  * BUG REPORTS: png-mng-implement (at) sourceforge.net
     15  *
     16  * png_have_neon implemented for Linux versions which allow access to
     17  * /proc/self/auxv.  This is probably faster, cleaner and safer than the code to
     18  * read /proc/cpuinfo in contrib/arm-neon/linux, however it is yet another piece
     19  * of potentially untested code and has more complex dependencies than the code
     20  * to read cpuinfo.
     21  *
     22  * This generic __linux__ implementation requires reading /proc/self/auxv and
     23  * looking at each element for one that records NEON capabilities.
     24  */
     25 #include <unistd.h> /* for POSIX 1003.1 */
     26 #include <errno.h>  /* for EINTR */
     27 
     28 #include <sys/types.h>
     29 #include <sys/stat.h>
     30 #include <fcntl.h>
     31 #include <elf.h>
     32 #include <asm/hwcap.h>
     33 
     34 /* A read call may be interrupted, in which case it returns -1 and sets errno to
     35  * EINTR if nothing was done, otherwise (if something was done) a partial read
     36  * may result.
     37  */
     38 static size_t
     39 safe_read(png_structp png_ptr, int fd, void *buffer_in, size_t nbytes)
     40 {
     41    size_t ntotal = 0;
     42    char *buffer = png_voidcast(char*, buffer_in);
     43 
     44    while (nbytes > 0)
     45    {
     46       unsigned int nread;
     47       int iread;
     48 
     49       /* Passing nread > INT_MAX to read is implementation defined in POSIX
     50        * 1003.1, therefore despite the unsigned argument portable code must
     51        * limit the value to INT_MAX!
     52        */
     53       if (nbytes > INT_MAX)
     54          nread = INT_MAX;
     55 
     56       else
     57          nread = (unsigned int)/*SAFE*/nbytes;
     58 
     59       iread = read(fd, buffer, nread);
     60 
     61       if (iread == -1)
     62       {
     63          /* This is the devil in the details, a read can terminate early with 0
     64           * bytes read because of EINTR, yet it still returns -1 otherwise end
     65           * of file cannot be distinguished.
     66           */
     67          if (errno != EINTR)
     68          {
     69             png_warning(png_ptr, "/proc read failed");
     70             return 0; /* I.e., a permanent failure */
     71          }
     72       }
     73 
     74       else if (iread < 0)
     75       {
     76          /* Not a valid 'read' result: */
     77          png_warning(png_ptr, "OS /proc read bug");
     78          return 0;
     79       }
     80 
     81       else if (iread > 0)
     82       {
     83          /* Continue reading until a permanent failure, or EOF */
     84          buffer += iread;
     85          nbytes -= (unsigned int)/*SAFE*/iread;
     86          ntotal += (unsigned int)/*SAFE*/iread;
     87       }
     88 
     89       else
     90          return ntotal;
     91    }
     92 
     93    return ntotal; /* nbytes == 0 */
     94 }
     95 
     96 static int
     97 png_have_neon(png_structp png_ptr)
     98 {
     99    int fd = open("/proc/self/auxv", O_RDONLY);
    100    Elf32_auxv_t aux;
    101 
    102    /* Failsafe: failure to open means no NEON */
    103    if (fd == -1)
    104    {
    105       png_warning(png_ptr, "/proc/self/auxv open failed");
    106       return 0;
    107    }
    108 
    109    while (safe_read(png_ptr, fd, &aux, sizeof aux) == sizeof aux)
    110    {
    111       if (aux.a_type == AT_HWCAP && (aux.a_un.a_val & HWCAP_NEON) != 0)
    112       {
    113          close(fd);
    114          return 1;
    115       }
    116    }
    117 
    118    close(fd);
    119    return 0;
    120 }
    121