summaryrefslogtreecommitdiff
path: root/exec/mipsfpu.c
blob: 5fd81fb92376e98a2d382571a5107888382ff84c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
/* Program execution for Emacs.

Copyright (C) 2023-2024 Free Software Foundation, Inc.

This file is part of GNU Emacs.

GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */

#include <config.h>
#include <errno.h>

#include "mipsfpu.h"



/* OABI MIPS systems support several different modes of execution.
   Each mode differs in the size and utilization of the hardware
   floating-point registers.

   Linux normally sets the floating point mode to one appropriate for
   execution, taking into account the floating point modes of the
   interpreter and executable binaries.  However, this logic is
   forsaken when the `execve' system call is overwritten.

   Thus, the correct floating point mode must be determined and set
   within the loader binary.  */



/* Various constants used throughout this code.  */

#define MIPS_ABI_FP_ANY		0	/* FP ABI doesn't matter */
#define MIPS_ABI_FP_DOUBLE	1	/* -mdouble-float */
#define MIPS_ABI_FP_SINGLE	2	/* -msingle-float */
#define MIPS_ABI_FP_SOFT	3	/* -msoft-float */
#define MIPS_ABI_FP_OLD_64	4	/* -mips32r2 -mfp64 */
#define MIPS_ABI_FP_XX		5	/* -mfpxx */
#define MIPS_ABI_FP_64		6	/* -mips32r2 -mfp64 */
#define MIPS_ABI_FP_64A		7	/* -mips32r2 -mfp64 -mno-odd-spreg */

#define EF_MIPS_NOREORDER	1     /* A .noreorder directive was used.  */
#define EF_MIPS_PIC		2     /* Contains PIC code.  */
#define EF_MIPS_CPIC		4     /* Uses PIC calling sequence.  */
#define EF_MIPS_XGOT		8
#define EF_MIPS_64BIT_WHIRL	16
#define EF_MIPS_ABI2		32
#define EF_MIPS_ABI_ON32	64
#define EF_MIPS_FP64		512  /* Uses FP64 (12 callee-saved).  */
#define EF_MIPS_NAN2008		1024  /* Uses IEEE 754-2008 NaN encoding.  */
#define EF_MIPS_ARCH		0xf0000000 /* MIPS architecture level.  */



/* Structure describing the requirements of a single floating-point
   ABI.  */

struct mode_description
{
  /* Whether or not the ABI only executes single precision
     instructions, and can operate in both 32-bit or 64-bit floating
     point mode.  */
  bool single;

  /* Whether or not the ABI performs floating point operations in
     software, using integer registers.  */
  bool soft;

  /* Whether or not the ABI requires the use of 64-bit floating point
     registers.  */
  bool fr1;

  /* Whether or not the ABI requires the use of 64-bit floating point
     registers on NABI systems, and 32-bit ones on OABI systems.  */
  bool frdefault;

  /* Whether or not this ABI requires single precision floating point
     emulation.  */
  bool fre;
};

static struct mode_description fpu_reqs[] =
  {
    [MIPS_ABI_FP_ANY]    = { true,  true,  true,  true,  true,  },
    [MIPS_ABI_FP_DOUBLE] = { false, false, false, true,  true,  },
    [MIPS_ABI_FP_SINGLE] = { true,  false, false, false, false, },
    [MIPS_ABI_FP_SOFT]   = { false, true,  false, false, false, },
    [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false, },
    [MIPS_ABI_FP_XX]     = { false, false, true,  true,  true,  },
    [MIPS_ABI_FP_64]     = { false, false, true,  false, false, },
    [MIPS_ABI_FP_64A]    = { false, false, true,  false, true,  },
  };



/* Return whether or not the given floating-point ABI is valid.  */

static bool
valid_abi_p (int abi)
{
  switch (abi)
    {
    case MIPS_ABI_FP_ANY:
    case MIPS_ABI_FP_DOUBLE:
    case MIPS_ABI_FP_SINGLE:
    case MIPS_ABI_FP_SOFT:
    case MIPS_ABI_FP_OLD_64:
    case MIPS_ABI_FP_XX:
    case MIPS_ABI_FP_64:
    case MIPS_ABI_FP_64A:
      return true;

    default:
      return false;
    }
}

/* Return the floating point mode appropriate for the specified
   floating point ABI.  */

static int
fp_mode_for_abi (int abi)
{
  struct mode_description *desc;

  desc = &fpu_reqs[abi];

  if (desc->fre)
    return FP_FRE;
  else if (desc->fr1)
    return FP_FR1;

  return FP_FR0;
}

/* Determine whether or not the CPU is capable of operating in FR0
   floating point mode.  */

bool
cpu_supports_fr0_p (void)
{
#if defined __mips_isa_rev && __mips_isa_rev >= 6
  return true;
#else /* !defined __mips_isa_rev | mips_isa_rev < 6 */
  return false;
#endif /* defined __mips_isa_rev && mips_isa_rev >= 6 */
}

/* Determine the FPU mode for the executable whose ELF header is
   HEADER.  If INTERPRETER is non-NULL, also take an interpreter whose
   header is INTERPRETER into account.

   ABIFLAGS should be HEADER's corresponding PT_MIPS_ABIFLAGS program
   header, and ABIFLAGS1 should be that of INTERPRETER, if set.  Both
   fields may be NULL if no PT_MIPS_ABIFLAGS header is present; in
   that case, use HEADER->e_flags to determine the ABI instead.

   Return the FPU mode in *MODE.  Value is 0 upon success, 1
   otherwise, with errno set.  */

int
determine_fpu_mode (elf_header *header, elf_header *interpreter,
		    int *mode, struct mips_elf_abi_flags *abiflags,
		    struct mips_elf_abi_flags *abiflags1)
{
  int exec_abi, interpreter_abi;
  struct mode_description *exec_desc, *interpreter_desc, common;

  /* Figure out the executable's floating point ABI.  First, consult
     header->e_flags, and use the old 64-bit floating point ABI if it
     is specified.  */

  exec_abi = MIPS_ABI_FP_ANY;

  /* First, check HEADER->e_flags.  */

  if (header->e_flags & EF_MIPS_FP64)
    exec_abi = MIPS_ABI_FP_OLD_64;

  /* Next, use ABIFLAGS if it exists.  */

  if (abiflags && valid_abi_p (abiflags->fp_abi))
    exec_abi = abiflags->fp_abi;
  else if (abiflags)
    {
      errno = ENOEXEC;
      return 1;
    }

  /* Now determine that of the interpreter.  */

  interpreter_abi = MIPS_ABI_FP_ANY;

  if (interpreter)
    {
      if (interpreter->e_flags & EF_MIPS_FP64)
	interpreter_abi = MIPS_ABI_FP_OLD_64;

      if (abiflags1 && valid_abi_p (abiflags->fp_abi))
	interpreter_abi = abiflags->fp_abi;
      else if (abiflags1)
	{
	  errno = ELIBBAD;
	  return 1;
	}
    }

  /* If no interpreter flag is set, just return that of the
     executable.  */

  if (!interpreter)
    {
      *mode = fp_mode_for_abi (exec_abi);
      return 0;
    }

  /* Otherwise, compare both ABIs and try to find one which will run
     both kinds of code.

     First, see if there's an easy way out: both ABIs are identical,
     or one ABI is MIPS_ABI_FP_ANY.  */

  if (exec_abi == interpreter_abi)
    {
      *mode = fp_mode_for_abi (exec_abi);
      return 0;
    }
  else if (exec_abi == MIPS_ABI_FP_ANY)
    {
      *mode = fp_mode_for_abi (interpreter_abi);
      return 0;
    }
  else if (interpreter_abi == MIPS_ABI_FP_ANY)
    {
      *mode = fp_mode_for_abi (exec_abi);
      return 0;
    }

  /* If that doesn't work, compare various characteristics of both
     ABIs and select an appropriate floating point mode.  */

  exec_desc = &fpu_reqs[exec_abi];
  interpreter_desc = &fpu_reqs[interpreter_abi];

  /* Merge both sets of requirements.  */
  common.single = exec_desc->single && interpreter_desc->single;
  common.soft = exec_desc->soft && interpreter_desc->soft;
  common.fr1 = exec_desc->fr1 && interpreter_desc->fr1;
  common.frdefault = exec_desc->frdefault && interpreter_desc->frdefault;
  common.fre = exec_desc->fre && interpreter_desc->fre;

  /* Default to a mode capable of running code expecting 32-bit
     registers.  */

  if (!(header->e_flags & EF_MIPS_ABI2))
    *mode = FP_FR0;
  else
    /* But in this case, use FR1.  */
    *mode = FP_FR1;

  if (common.fre && !common.frdefault && !common.fr1)
    /* Floating point emulation mode is required.  */
    *mode = FP_FRE;
  else if ((common.fr1 && common.frdefault)
	   || (common.single && !common.frdefault)
	   || common.fr1)
    /* 64-bit mode is required.  */
    *mode = FP_FR1;
  else if (!common.fre && !common.frdefault
	   && !common.fr1 && !common.single
	   && !common.soft)
    {
      /* The floating point modes specified are incompatible.  */
      errno = ELIBBAD;
      return -1;
    }

  return 0;
}