root/exec/mipsfpu.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. valid_abi_p
  2. fp_mode_for_abi
  3. cpu_supports_fr0_p
  4. determine_fpu_mode

     1 /* Program execution for Emacs.
     2 
     3 Copyright (C) 2023 Free Software Foundation, Inc.
     4 
     5 This file is part of GNU Emacs.
     6 
     7 GNU Emacs is free software: you can redistribute it and/or modify
     8 it under the terms of the GNU General Public License as published by
     9 the Free Software Foundation, either version 3 of the License, or (at
    10 your option) any later version.
    11 
    12 GNU Emacs is distributed in the hope that it will be useful,
    13 but WITHOUT ANY WARRANTY; without even the implied warranty of
    14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15 GNU General Public License for more details.
    16 
    17 You should have received a copy of the GNU General Public License
    18 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    19 
    20 #include <config.h>
    21 #include <errno.h>
    22 
    23 #include "mipsfpu.h"
    24 
    25 
    26 
    27 /* OABI MIPS systems support several different modes of execution.
    28    Each mode differs in the size and utilization of the hardware
    29    floating-point registers.
    30 
    31    Linux normally sets the floating point mode to one appropriate for
    32    execution, taking into account the floating point modes of the
    33    interpreter and executable binaries.  However, this logic is
    34    forsaken when the `execve' system call is overwritten.
    35 
    36    Thus, the correct floating point mode must be determined and set
    37    within the loader binary.  */
    38 
    39 
    40 
    41 /* Various constants used throughout this code.  */
    42 
    43 #define MIPS_ABI_FP_ANY         0       /* FP ABI doesn't matter */
    44 #define MIPS_ABI_FP_DOUBLE      1       /* -mdouble-float */
    45 #define MIPS_ABI_FP_SINGLE      2       /* -msingle-float */
    46 #define MIPS_ABI_FP_SOFT        3       /* -msoft-float */
    47 #define MIPS_ABI_FP_OLD_64      4       /* -mips32r2 -mfp64 */
    48 #define MIPS_ABI_FP_XX          5       /* -mfpxx */
    49 #define MIPS_ABI_FP_64          6       /* -mips32r2 -mfp64 */
    50 #define MIPS_ABI_FP_64A         7       /* -mips32r2 -mfp64 -mno-odd-spreg */
    51 
    52 #define EF_MIPS_NOREORDER       1     /* A .noreorder directive was used.  */
    53 #define EF_MIPS_PIC             2     /* Contains PIC code.  */
    54 #define EF_MIPS_CPIC            4     /* Uses PIC calling sequence.  */
    55 #define EF_MIPS_XGOT            8
    56 #define EF_MIPS_64BIT_WHIRL     16
    57 #define EF_MIPS_ABI2            32
    58 #define EF_MIPS_ABI_ON32        64
    59 #define EF_MIPS_FP64            512  /* Uses FP64 (12 callee-saved).  */
    60 #define EF_MIPS_NAN2008         1024  /* Uses IEEE 754-2008 NaN encoding.  */
    61 #define EF_MIPS_ARCH            0xf0000000 /* MIPS architecture level.  */
    62 
    63 
    64 
    65 /* Structure describing the requirements of a single floating-point
    66    ABI.  */
    67 
    68 struct mode_description
    69 {
    70   /* Whether or not the ABI only executes single precision
    71      instructions, and can operate in both 32-bit or 64-bit floating
    72      point mode.  */
    73   bool single;
    74 
    75   /* Whether or not the ABI performs floating point operations in
    76      software, using integer registers.  */
    77   bool soft;
    78 
    79   /* Whether or not the ABI requires the use of 64-bit floating point
    80      registers.  */
    81   bool fr1;
    82 
    83   /* Whether or not the ABI requires the use of 64-bit floating point
    84      registers on NABI systems, and 32-bit ones on OABI systems.  */
    85   bool frdefault;
    86 
    87   /* Whether or not this ABI requires single precision floating point
    88      emulation.  */
    89   bool fre;
    90 };
    91 
    92 static struct mode_description fpu_reqs[] =
    93   {
    94     [MIPS_ABI_FP_ANY]    = { true,  true,  true,  true,  true,  },
    95     [MIPS_ABI_FP_DOUBLE] = { false, false, false, true,  true,  },
    96     [MIPS_ABI_FP_SINGLE] = { true,  false, false, false, false, },
    97     [MIPS_ABI_FP_SOFT]   = { false, true,  false, false, false, },
    98     [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false, },
    99     [MIPS_ABI_FP_XX]     = { false, false, true,  true,  true,  },
   100     [MIPS_ABI_FP_64]     = { false, false, true,  false, false, },
   101     [MIPS_ABI_FP_64A]    = { false, false, true,  false, true,  },
   102   };
   103 
   104 
   105 
   106 /* Return whether or not the given floating-point ABI is valid.  */
   107 
   108 static bool
   109 valid_abi_p (int abi)
   110 {
   111   switch (abi)
   112     {
   113     case MIPS_ABI_FP_ANY:
   114     case MIPS_ABI_FP_DOUBLE:
   115     case MIPS_ABI_FP_SINGLE:
   116     case MIPS_ABI_FP_SOFT:
   117     case MIPS_ABI_FP_OLD_64:
   118     case MIPS_ABI_FP_XX:
   119     case MIPS_ABI_FP_64:
   120     case MIPS_ABI_FP_64A:
   121       return true;
   122 
   123     default:
   124       return false;
   125     }
   126 }
   127 
   128 /* Return the floating point mode appropriate for the specified
   129    floating point ABI.  */
   130 
   131 static int
   132 fp_mode_for_abi (int abi)
   133 {
   134   struct mode_description *desc;
   135 
   136   desc = &fpu_reqs[abi];
   137 
   138   if (desc->fre)
   139     return FP_FRE;
   140   else if (desc->fr1)
   141     return FP_FR1;
   142 
   143   return FP_FR0;
   144 }
   145 
   146 /* Determine whether or not the CPU is capable of operating in FR0
   147    floating point mode.  */
   148 
   149 bool
   150 cpu_supports_fr0_p (void)
   151 {
   152 #if defined __mips_isa_rev && __mips_isa_rev >= 6
   153   return true;
   154 #else /* !defined __mips_isa_rev | mips_isa_rev < 6 */
   155   return false;
   156 #endif /* defined __mips_isa_rev && mips_isa_rev >= 6 */
   157 }
   158 
   159 /* Determine the FPU mode for the executable whose ELF header is
   160    HEADER.  If INTERPRETER is non-NULL, also take an interpreter whose
   161    header is INTERPRETER into account.
   162 
   163    ABIFLAGS should be HEADER's corresponding PT_MIPS_ABIFLAGS program
   164    header, and ABIFLAGS1 should be that of INTERPRETER, if set.  Both
   165    fields may be NULL if no PT_MIPS_ABIFLAGS header is present; in
   166    that case, use HEADER->e_flags to determine the ABI instead.
   167 
   168    Return the FPU mode in *MODE.  Value is 0 upon success, 1
   169    otherwise, with errno set.  */
   170 
   171 int
   172 determine_fpu_mode (elf_header *header, elf_header *interpreter,
   173                     int *mode, struct mips_elf_abi_flags *abiflags,
   174                     struct mips_elf_abi_flags *abiflags1)
   175 {
   176   int exec_abi, interpreter_abi;
   177   struct mode_description *exec_desc, *interpreter_desc, common;
   178 
   179   /* Figure out the executable's floating point ABI.  First, consult
   180      header->e_flags, and use the old 64-bit floating point ABI if it
   181      is specified.  */
   182 
   183   exec_abi = MIPS_ABI_FP_ANY;
   184 
   185   /* First, check HEADER->e_flags.  */
   186 
   187   if (header->e_flags & EF_MIPS_FP64)
   188     exec_abi = MIPS_ABI_FP_OLD_64;
   189 
   190   /* Next, use ABIFLAGS if it exists.  */
   191 
   192   if (abiflags && valid_abi_p (abiflags->fp_abi))
   193     exec_abi = abiflags->fp_abi;
   194   else if (abiflags)
   195     {
   196       errno = ENOEXEC;
   197       return 1;
   198     }
   199 
   200   /* Now determine that of the interpreter.  */
   201 
   202   interpreter_abi = MIPS_ABI_FP_ANY;
   203 
   204   if (interpreter)
   205     {
   206       if (interpreter->e_flags & EF_MIPS_FP64)
   207         interpreter_abi = MIPS_ABI_FP_OLD_64;
   208 
   209       if (abiflags1 && valid_abi_p (abiflags->fp_abi))
   210         interpreter_abi = abiflags->fp_abi;
   211       else if (abiflags1)
   212         {
   213           errno = ELIBBAD;
   214           return 1;
   215         }
   216     }
   217 
   218   /* If no interpreter flag is set, just return that of the
   219      executable.  */
   220 
   221   if (!interpreter)
   222     {
   223       *mode = fp_mode_for_abi (exec_abi);
   224       return 0;
   225     }
   226 
   227   /* Otherwise, compare both ABIs and try to find one which will run
   228      both kinds of code.
   229 
   230      First, see if there's an easy way out: both ABIs are identical,
   231      or one ABI is MIPS_ABI_FP_ANY.  */
   232 
   233   if (exec_abi == interpreter_abi)
   234     {
   235       *mode = fp_mode_for_abi (exec_abi);
   236       return 0;
   237     }
   238   else if (exec_abi == MIPS_ABI_FP_ANY)
   239     {
   240       *mode = fp_mode_for_abi (interpreter_abi);
   241       return 0;
   242     }
   243   else if (interpreter_abi == MIPS_ABI_FP_ANY)
   244     {
   245       *mode = fp_mode_for_abi (exec_abi);
   246       return 0;
   247     }
   248 
   249   /* If that doesn't work, compare various characteristics of both
   250      ABIs and select an appropriate floating point mode.  */
   251 
   252   exec_desc = &fpu_reqs[exec_abi];
   253   interpreter_desc = &fpu_reqs[interpreter_abi];
   254 
   255   /* Merge both sets of requirements.  */
   256   common.single = exec_desc->single && interpreter_desc->single;
   257   common.soft = exec_desc->soft && interpreter_desc->soft;
   258   common.fr1 = exec_desc->fr1 && interpreter_desc->fr1;
   259   common.frdefault = exec_desc->frdefault && interpreter_desc->frdefault;
   260   common.fre = exec_desc->fre && interpreter_desc->fre;
   261 
   262   /* Default to a mode capable of running code expecting 32-bit
   263      registers.  */
   264 
   265   if (!(header->e_flags & EF_MIPS_ABI2))
   266     *mode = FP_FR0;
   267   else
   268     /* But in this case, use FR1.  */
   269     *mode = FP_FR1;
   270 
   271   if (common.fre && !common.frdefault && !common.fr1)
   272     /* Floating point emulation mode is required.  */
   273     *mode = FP_FRE;
   274   else if ((common.fr1 && common.frdefault)
   275            || (common.single && !common.frdefault)
   276            || common.fr1)
   277     /* 64-bit mode is required.  */
   278     *mode = FP_FR1;
   279   else if (!common.fre && !common.frdefault
   280            && !common.fr1 && !common.single
   281            && !common.soft)
   282     {
   283       /* The floating point modes specified are incompatible.  */
   284       errno = ELIBBAD;
   285       return -1;
   286     }
   287 
   288   return 0;
   289 }

/* [<][>][^][v][top][bottom][index][help] */