diff options
Diffstat (limited to 'sysdeps/mips')
-rw-r--r-- | sysdeps/mips/Makefile | 50 | ||||
-rw-r--r-- | sysdeps/mips/bits/linkmap.h | 2 | ||||
-rw-r--r-- | sysdeps/mips/dl-machine-reject-phdr.h | 326 | ||||
-rw-r--r-- | sysdeps/mips/dl-machine.h | 5 | ||||
-rw-r--r-- | sysdeps/mips/tst-abi-fp32mod.c | 22 | ||||
-rw-r--r-- | sysdeps/mips/tst-abi-fp64amod.c | 22 | ||||
-rw-r--r-- | sysdeps/mips/tst-abi-fp64mod.c | 22 | ||||
-rw-r--r-- | sysdeps/mips/tst-abi-fpxxmod.c | 22 | ||||
-rw-r--r-- | sysdeps/mips/tst-abi-fpxxomod.c | 22 | ||||
-rw-r--r-- | sysdeps/mips/tst-abi-interlink.c | 844 | ||||
-rw-r--r-- | sysdeps/mips/tst-mode-switch-1.c | 123 | ||||
-rw-r--r-- | sysdeps/mips/tst-mode-switch-2.c | 163 | ||||
-rw-r--r-- | sysdeps/mips/tst-mode-switch-3.c | 90 |
13 files changed, 1713 insertions, 0 deletions
diff --git a/sysdeps/mips/Makefile b/sysdeps/mips/Makefile index a1526998e0..463b121758 100644 --- a/sysdeps/mips/Makefile +++ b/sysdeps/mips/Makefile @@ -26,3 +26,53 @@ CPPFLAGS-crtn.S += $(pic-ccflag) endif ASFLAGS-.os += $(pic-ccflag) + +ifeq ($(subdir),elf) +ifneq ($(o32-fpabi),) +tests += tst-abi-interlink + +fpabi-modules-names = +fpabi_list = +ifneq (,$(filter $(o32-fpabi),32 xx xxo)) +fpabi-modules-names += tst-abi-fp32mod +CFLAGS-tst-abi-fp32mod.c += -mfp32 +endif +ifneq (,$(filter $(o32-fpabi),xx)) +fpabi-modules-names += tst-abi-fpxxmod +CFLAGS-tst-abi-fpxxmod.c += -mfpxx -mno-odd-spreg +endif +ifneq (,$(filter $(o32-fpabi),xx xxo)) +fpabi-modules-names += tst-abi-fpxxomod +CFLAGS-tst-abi-fpxxomod.c += -mfpxx -modd-spreg +endif +ifneq (,$(filter $(o32-fpabi),xx 64a)) +fpabi-modules-names += tst-abi-fp64amod +CFLAGS-tst-abi-fp64amod.c += -mfp64 -mno-odd-spreg +endif +ifneq (,$(filter $(o32-fpabi),xx xxo 64a 64)) +fpabi-modules-names += tst-abi-fp64mod +CFLAGS-tst-abi-fp64mod.c += -mfp64 -modd-spreg +endif +modules-names += $(fpabi-modules-names) + +comma:=, +empty:= +space:=$(empty) $(empty) +fpabi_list=$(subst $(space),$(comma),$(patsubst tst-abi-%mod,o_%,\ + $(fpabi-modules-names))) +CPPFLAGS-tst-abi-interlink.c += -DFPABI_LIST=$(fpabi_list) +CPPFLAGS-tst-abi-interlink.c += -DFPABI_COUNT=$(words $(fpabi-modules-names)) +CPPFLAGS-tst-abi-interlink.c += -DFPABI_NATIVE=o_fp$(o32-fpabi) +$(objpfx)tst-abi-interlink: $(libdl) +$(objpfx)tst-abi-interlink.out: $(patsubst %,$(objpfx)%.so,\ + $(fpabi-modules-names)) +endif + +ifeq ($(mips-mode-switch),yes) +ifeq ($(o32-fpabi),xx) +tests += tst-mode-switch-1 tst-mode-switch-2 tst-mode-switch-3 +$(objpfx)tst-mode-switch-1: $(shared-thread-library) +$(objpfx)tst-mode-switch-2: $(shared-thread-library) +endif +endif +endif diff --git a/sysdeps/mips/bits/linkmap.h b/sysdeps/mips/bits/linkmap.h index a6df7821e6..1fb9678a6d 100644 --- a/sysdeps/mips/bits/linkmap.h +++ b/sysdeps/mips/bits/linkmap.h @@ -1,4 +1,6 @@ struct link_map_machine { ElfW(Addr) plt; /* Address of .plt */ + ElfW(Word) fpabi; /* FP ABI of the object */ + unsigned int odd_spreg; /* Does the object require odd_spreg support? */ }; diff --git a/sysdeps/mips/dl-machine-reject-phdr.h b/sysdeps/mips/dl-machine-reject-phdr.h new file mode 100644 index 0000000000..b277450c4d --- /dev/null +++ b/sysdeps/mips/dl-machine-reject-phdr.h @@ -0,0 +1,326 @@ +/* Machine-dependent program header inspection for the ELF loader. + Copyright (C) 2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _DL_MACHINE_REJECT_PHDR_H +#define _DL_MACHINE_REJECT_PHDR_H 1 + +#include <unistd.h> +#include <sys/prctl.h> + +#if defined PR_GET_FP_MODE && defined PR_SET_FP_MODE +# define HAVE_PRCTL_FP_MODE 1 +#else +# define HAVE_PRCTL_FP_MODE 0 +#endif + +/* Reject an object with a debug message. */ +#define REJECT(str, args...) \ + { \ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) \ + _dl_debug_printf (str, ##args); \ + return true; \ + } + +/* Search the program headers for the ABI Flags. */ + +static inline const ElfW(Phdr) * +find_mips_abiflags (const ElfW(Phdr) *phdr, ElfW(Half) phnum) +{ + const ElfW(Phdr) *ph; + + for (ph = phdr; ph < &phdr[phnum]; ++ph) + if (ph->p_type == PT_MIPS_ABIFLAGS) + return ph; + return NULL; +} + +/* Cache the FP ABI value from the PT_MIPS_ABIFLAGS program header. */ + +static bool +cached_fpabi_reject_phdr_p (struct link_map *l) +{ + if (l->l_mach.fpabi == 0) + { + const ElfW(Phdr) *ph = find_mips_abiflags (l->l_phdr, l->l_phnum); + + if (ph) + { + Elf_MIPS_ABIFlags_v0 * mips_abiflags; + if (ph->p_filesz < sizeof (Elf_MIPS_ABIFlags_v0)) + REJECT (" %s: malformed PT_MIPS_ABIFLAGS found\n", l->l_name); + + mips_abiflags = (Elf_MIPS_ABIFlags_v0 *) (l->l_addr + ph->p_vaddr); + + if (__glibc_unlikely (mips_abiflags->flags2 != 0)) + REJECT (" %s: unknown MIPS.abiflags flags2: %u\n", l->l_name, + mips_abiflags->flags2); + + l->l_mach.fpabi = mips_abiflags->fp_abi; + l->l_mach.odd_spreg = (mips_abiflags->flags1 + & MIPS_AFL_FLAGS1_ODDSPREG) != 0; + } + else + { + l->l_mach.fpabi = -1; + l->l_mach.odd_spreg = true; + } + } + return false; +} + +/* Return a description of the specified floating-point ABI. */ + +static const char * +fpabi_string (int fpabi) +{ + switch (fpabi) + { + case Val_GNU_MIPS_ABI_FP_ANY: + return "Hard or soft float"; + case Val_GNU_MIPS_ABI_FP_DOUBLE: + return "Hard float (double precision)"; + case Val_GNU_MIPS_ABI_FP_SINGLE: + return "Hard float (single precision)"; + case Val_GNU_MIPS_ABI_FP_SOFT: + return "Soft float"; + case Val_GNU_MIPS_ABI_FP_OLD_64: + return "Unsupported FP64"; + case Val_GNU_MIPS_ABI_FP_XX: + return "Hard float (32-bit CPU, Any FPU)"; + case Val_GNU_MIPS_ABI_FP_64: + return "Hard float (32-bit CPU, 64-bit FPU)"; + case Val_GNU_MIPS_ABI_FP_64A: + return "Hard float compat (32-bit CPU, 64-bit FPU)"; + case -1: + return "Double precision, single precision or soft float"; + default: + return "Unknown FP ABI"; + } +} + +/* A structure to describe the requirements of each FP ABI extension. + Each field says whether the ABI can be executed in that mode. The FR0 field + is actually overloaded and means 'default' FR mode for the ABI. I.e. For + O32 it is FR0 and for N32/N64 it is actually FR1. Since this logic is + focussed on the intricacies of mode management for O32 we call the field + FR0. */ + +struct abi_req +{ + bool single; + bool soft; + bool fr0; + bool fr1; + bool fre; +}; + +/* FP ABI requirements for all Val_GNU_MIPS_ABI_FP_* values. */ + +static const struct abi_req reqs[Val_GNU_MIPS_ABI_FP_MAX + 1] = + {{true, true, true, true, true}, /* Any */ + {false, false, true, false, true}, /* Double-float */ + {true, false, false, false, false}, /* Single-float */ + {false, true, false, false, false}, /* Soft-float */ + {false, false, false, false, false}, /* old-FP64 */ + {false, false, true, true, true}, /* FPXX */ + {false, false, false, true, false}, /* FP64 */ + {false, false, false, true, true}}; /* FP64A */ + +/* FP ABI requirements for objects without a PT_MIPS_ABIFLAGS segment. */ + +static const struct abi_req none_req = { true, true, true, false, true }; + +/* Return true iff ELF program headers are incompatible with the running + host. This verifies that floating-point ABIs are compatible and + re-configures the hardware mode if necessary. This code handles both the + DT_NEEDED libraries and the dlopen'ed libraries. It also accounts for the + impact of dlclose. */ + +static bool __attribute_used__ +elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum, + const char *buf, size_t len, struct link_map *map, + int fd) +{ + const ElfW(Phdr) *ph = find_mips_abiflags (phdr, phnum); + struct link_map *l; + Lmid_t nsid; + int in_abi = -1; + struct abi_req in_req; + Elf_MIPS_ABIFlags_v0 *mips_abiflags = NULL; + bool perfect_match = false; +#if _MIPS_SIM == _ABIO32 + unsigned int cur_mode = -1; +# if HAVE_PRCTL_FP_MODE + bool cannot_mode_switch = false; + + /* Get the current hardware mode. */ + cur_mode = __prctl (PR_GET_FP_MODE); +# endif +#endif + + /* Read the attributes section. */ + if (ph != NULL) + { + ElfW(Addr) size = ph->p_filesz; + + if (ph->p_offset + size <= len) + mips_abiflags = (Elf_MIPS_ABIFlags_v0 *) (buf + ph->p_offset); + else + { + mips_abiflags = alloca (size); + __lseek (fd, ph->p_offset, SEEK_SET); + if (__libc_read (fd, (void *) mips_abiflags, size) != size) + REJECT (" unable to read PT_MIPS_ABIFLAGS\n"); + } + + if (size < sizeof (Elf_MIPS_ABIFlags_v0)) + REJECT (" contains malformed PT_MIPS_ABIFLAGS\n"); + + if (__glibc_unlikely (mips_abiflags->flags2 != 0)) + REJECT (" unknown MIPS.abiflags flags2: %u\n", mips_abiflags->flags2); + + in_abi = mips_abiflags->fp_abi; + } + + /* ANY is compatible with anything. */ + perfect_match |= (in_abi == Val_GNU_MIPS_ABI_FP_ANY); + + /* Unknown ABIs are rejected. */ + if (in_abi != -1 && in_abi > Val_GNU_MIPS_ABI_FP_MAX) + REJECT (" uses unknown FP ABI: %u\n", in_abi); + + /* Obtain the initial requirements. */ + in_req = (in_abi == -1) ? none_req : reqs[in_abi]; + + /* Check that the new requirement does not conflict with any currently + loaded object. */ + for (nsid = 0; nsid < DL_NNS; ++nsid) + for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next) + { + struct abi_req existing_req; + + if (cached_fpabi_reject_phdr_p (l)) + return true; + +#if _MIPS_SIM == _ABIO32 + /* A special case arises for O32 FP64 and FP64A where the kernel + pre-dates PT_MIPS_ABIFLAGS. These ABIs will be blindly loaded even + if the hardware mode is unavailable or disabled. In this + circumstance the prctl call to obtain the current mode will fail. + Detect this situation here and reject everything. This will + effectively prevent dynamically linked applications from failing in + unusual ways but there is nothing we can do to help static + applications. */ + if ((l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_64A + || l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_64) + && cur_mode == -1) + REJECT (" found %s running in the wrong mode\n", + fpabi_string (l->l_mach.fpabi)); +#endif + + /* Found a perfect match, success. */ + perfect_match |= (in_abi == l->l_mach.fpabi); + + /* Unknown ABIs are rejected. */ + if (l->l_mach.fpabi != -1 && l->l_mach.fpabi > Val_GNU_MIPS_ABI_FP_MAX) + REJECT (" found unknown FP ABI: %u\n", l->l_mach.fpabi); + + existing_req = (l->l_mach.fpabi == -1 ? none_req + : reqs[l->l_mach.fpabi]); + + /* Merge requirements. */ + in_req.soft &= existing_req.soft; + in_req.single &= existing_req.single; + in_req.fr0 &= existing_req.fr0; + in_req.fr1 &= existing_req.fr1; + in_req.fre &= existing_req.fre; + + /* If there is at least one mode which is still usable then the new + object can be loaded. */ + if (in_req.single || in_req.soft || in_req.fr1 || in_req.fr0 + || in_req.fre) + { +#if _MIPS_SIM == _ABIO32 && HAVE_PRCTL_FP_MODE + /* Account for loaded ABIs which prohibit mode switching. */ + if (l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_XX) + cannot_mode_switch |= l->l_mach.odd_spreg; +#endif + } + else + REJECT (" uses %s, already loaded %s\n", + fpabi_string (in_abi), + fpabi_string (l->l_mach.fpabi)); + } + +#if _MIPS_SIM == _ABIO32 + /* At this point we know that the newly loaded object is compatible with all + existing objects but the hardware mode may not be correct. */ + if ((in_req.fr1 || in_req.fre || in_req.fr0) + && !perfect_match) + { + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) + _dl_debug_printf (" needs %s%s mode\n", in_req.fr0 ? "FR0 or " : "", + (in_req.fre && !in_req.fr1) ? "FRE" : "FR1"); + + /* If the PR_GET_FP_MODE is not supported then only FR0 is available. + If the overall requirements cannot be met by FR0 then reject the + object. */ + if (cur_mode == -1) + return !in_req.fr0; + +# if HAVE_PRCTL_FP_MODE + { + unsigned int fr1_mode = PR_FP_MODE_FR; + + /* It is not possible to change the mode of a thread which may be + executing FPXX code with odd-singles. If an FPXX object with + odd-singles is loaded then just check the current mode is OK. This + can be either the FR1 mode or FR0 if the requirements are met by + FR0. */ + if (cannot_mode_switch) + return (!(in_req.fre && cur_mode == (PR_FP_MODE_FR | PR_FP_MODE_FRE)) + && !(in_req.fr1 && cur_mode == PR_FP_MODE_FR) + && !(in_req.fr0 && cur_mode == 0)); + + /* If the overall requirements can be satisfied by FRE but not FR1 then + fr1_mode must become FRE. */ + if (in_req.fre && !in_req.fr1) + fr1_mode |= PR_FP_MODE_FRE; + + /* Set the new mode. Use fr1_mode if the requirements cannot be met by + FR0. */ + if (!in_req.fr0) + return __prctl (PR_SET_FP_MODE, fr1_mode) != 0; + else if (__prctl (PR_SET_FP_MODE, /* fr0_mode */ 0) != 0) + { + /* Setting FR0 can validly fail on an R6 core so retry with the FR1 + mode as a fall back. */ + if (errno != ENOTSUP) + return true; + + return __prctl (PR_SET_FP_MODE, fr1_mode) != 0; + } + } +# endif /* HAVE_PRCTL_FP_MODE */ + } +#endif /* _MIPS_SIM == _ABIO32 */ + + return false; +} + +#endif /* dl-machine-reject-phdr.h */ diff --git a/sysdeps/mips/dl-machine.h b/sysdeps/mips/dl-machine.h index 5000d2a300..b463232067 100644 --- a/sysdeps/mips/dl-machine.h +++ b/sysdeps/mips/dl-machine.h @@ -99,6 +99,11 @@ elf_machine_matches_host (const ElfW(Ehdr) *ehdr) if ((ehdr->e_flags & EF_MIPS_NAN2008) != ELF_MACHINE_NAN2008) return 0; + /* Ensure that the old O32 FP64 ABI is never loaded, it is not supported + on linux. */ + if (ehdr->e_flags & EF_MIPS_FP64) + return 0; + switch (ehdr->e_machine) { case EM_MIPS: diff --git a/sysdeps/mips/tst-abi-fp32mod.c b/sysdeps/mips/tst-abi-fp32mod.c new file mode 100644 index 0000000000..89ff053893 --- /dev/null +++ b/sysdeps/mips/tst-abi-fp32mod.c @@ -0,0 +1,22 @@ +/* Copyright (C) 2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +int +fp32 (void) +{ + return 1; +} diff --git a/sysdeps/mips/tst-abi-fp64amod.c b/sysdeps/mips/tst-abi-fp64amod.c new file mode 100644 index 0000000000..be3d781a14 --- /dev/null +++ b/sysdeps/mips/tst-abi-fp64amod.c @@ -0,0 +1,22 @@ +/* Copyright (C) 2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +int +fp64a (void) +{ + return 7; +} diff --git a/sysdeps/mips/tst-abi-fp64mod.c b/sysdeps/mips/tst-abi-fp64mod.c new file mode 100644 index 0000000000..04b5c3600a --- /dev/null +++ b/sysdeps/mips/tst-abi-fp64mod.c @@ -0,0 +1,22 @@ +/* Copyright (C) 2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +int +fp64 (void) +{ + return 6; +} diff --git a/sysdeps/mips/tst-abi-fpxxmod.c b/sysdeps/mips/tst-abi-fpxxmod.c new file mode 100644 index 0000000000..69f73a17f3 --- /dev/null +++ b/sysdeps/mips/tst-abi-fpxxmod.c @@ -0,0 +1,22 @@ +/* Copyright (C) 2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +int +fpxx (void) +{ + return 5; +} diff --git a/sysdeps/mips/tst-abi-fpxxomod.c b/sysdeps/mips/tst-abi-fpxxomod.c new file mode 100644 index 0000000000..795973b7f3 --- /dev/null +++ b/sysdeps/mips/tst-abi-fpxxomod.c @@ -0,0 +1,22 @@ +/* Copyright (C) 2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +int +fpxxo (void) +{ + return 5 + 100; +} diff --git a/sysdeps/mips/tst-abi-interlink.c b/sysdeps/mips/tst-abi-interlink.c new file mode 100644 index 0000000000..c9a29cce92 --- /dev/null +++ b/sysdeps/mips/tst-abi-interlink.c @@ -0,0 +1,844 @@ +/* Copyright (C) 2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <sys/prctl.h> +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include <errno.h> + +#if defined PR_GET_FP_MODE && defined PR_SET_FP_MODE +# define HAVE_PRCTL_FP_MODE 1 +# define FR1_MODE (PR_FP_MODE_FR) +# define FRE_MODE (PR_FP_MODE_FR | PR_FP_MODE_FRE) +#else +# define HAVE_PRCTL_FP_MODE 0 +# define FR1_MODE 0x1 +# define FRE_MODE 0x2 +#endif + +#define STR_VAL(VAL) #VAL +#define N_STR(VAL) STR_VAL(VAL) + +#define START_STATE(NAME) \ +case s_ ## NAME: \ + { \ + switch (obj) \ + { + +#define END_STATE \ + default: \ + return false; \ + } \ + break; \ + } + +#define NEXT(OBJ, NEXT_STATE) \ +case o_ ## OBJ: \ + current_fp_state = s_ ## NEXT_STATE; \ + break; + +#define NEXT_REQ_FR1(OBJ, NEXT_STATE) \ +case o_ ## OBJ: \ + { \ + if (has_fr1) \ + current_fp_state = s_ ## NEXT_STATE; \ + else \ + return false; \ + } \ + break; + +#define NEXT_REQ_FR0(OBJ, NEXT_STATE) \ +case o_ ## OBJ: \ + { \ + if (!is_r6 \ + || (is_r6 && has_fr1 && has_fre)) \ + current_fp_state = s_ ## NEXT_STATE; \ + else \ + return false; \ + } \ + break; + +#define NEXT_REQ_FRE(OBJ, NEXT_STATE) \ +case o_ ## OBJ: \ + { \ + if (has_fr1 && has_fre) \ + current_fp_state = s_ ## NEXT_STATE; \ + else \ + return false; \ + } \ + break; + +#define NEXT_NO_MODE_CHANGE(OBJ, NEXT_STATE) \ +case o_ ## OBJ: \ + { \ + if (current_mode_valid_p (s_ ## NEXT_STATE)) \ + { \ + current_fp_state = s_ ## NEXT_STATE; \ + cant_change_mode = true; \ + } \ + else \ + return false; \ + } \ + break; + +static const char * const shared_lib_names[] = + { + "tst-abi-fpanymod.so", "tst-abi-fpsoftmod.so", "tst-abi-fpsinglemod.so", + "tst-abi-fp32mod.so", "tst-abi-fp64mod.so", "tst-abi-fp64amod.so", + "tst-abi-fpxxmod.so", "tst-abi-fpxxomod.so" + }; + +struct fp_mode_req +{ + int mode1; + int mode2; + int mode3; +}; + +enum fp_obj +{ + o_any, + o_soft, + o_single, + o_fp32, + o_fp64, + o_fp64a, + o_fpxx, + o_fpxxo, + o_max +}; + +enum fp_state +{ + s_any, + s_soft, + s_single, + s_fp32, + s_fpxx, + s_fpxxo, + s_fp64a, + s_fp64, + s_fpxxo_fpxx, + s_fp32_fpxx, + s_fp32_fpxxo, + s_fp32_fpxxo_fpxx, + s_fp32_fp64a_fpxx, + s_fp32_fp64a_fpxxo, + s_fp32_fp64a_fpxxo_fpxx, + s_fp64a_fp32, + s_fp64a_fpxx, + s_fp64a_fpxxo, + s_fp64a_fp64, + s_fp64a_fp64_fpxx, + s_fp64a_fp64_fpxxo, + s_fp64a_fpxx_fpxxo, + s_fp64a_fp64_fpxxo_fpxx, + s_fp64_fpxx, + s_fp64_fpxxo, + s_fp64_fpxx_fpxxo +}; + + +static int current_fp_mode; +static bool cant_change_mode = false; +static bool has_fr1 = false; +static bool has_fre = false; +static bool is_r6 = false; +static unsigned int fp_obj_count[o_max]; +void * shared_lib_ptrs[o_max]; +static enum fp_state current_fp_state = s_any; +static enum fp_obj test_objects[FPABI_COUNT] = { FPABI_LIST }; + +/* This function will return the valid FP modes for the specified state. */ + +static struct fp_mode_req +compute_fp_modes (enum fp_state state) +{ + struct fp_mode_req requirements; + + requirements.mode1 = -1; + requirements.mode2 = -1; + requirements.mode3 = -1; + + switch (state) + { + case s_single: + { + if (is_r6) + requirements.mode1 = FR1_MODE; + else + { + requirements.mode1 = 0; + requirements.mode2 = FR1_MODE; + } + break; + } + case s_fp32: + case s_fp32_fpxx: + case s_fp32_fpxxo: + case s_fp32_fpxxo_fpxx: + { + if (is_r6) + requirements.mode1 = FRE_MODE; + else + { + requirements.mode1 = 0; + requirements.mode2 = FRE_MODE; + } + break; + } + case s_fpxx: + case s_fpxxo: + case s_fpxxo_fpxx: + case s_any: + case s_soft: + { + if (is_r6) + { + requirements.mode1 = FR1_MODE; + requirements.mode2 = FRE_MODE; + } + else + { + requirements.mode1 = 0; + requirements.mode2 = FR1_MODE; + requirements.mode3 = FRE_MODE; + } + break; + } + case s_fp64a: + case s_fp64a_fpxx: + case s_fp64a_fpxxo: + case s_fp64a_fpxx_fpxxo: + { + requirements.mode1 = FR1_MODE; + requirements.mode2 = FRE_MODE; + break; + } + case s_fp64: + case s_fp64_fpxx: + case s_fp64_fpxxo: + case s_fp64_fpxx_fpxxo: + case s_fp64a_fp64: + case s_fp64a_fp64_fpxx: + case s_fp64a_fp64_fpxxo: + case s_fp64a_fp64_fpxxo_fpxx: + { + requirements.mode1 = FR1_MODE; + break; + } + case s_fp64a_fp32: + case s_fp32_fp64a_fpxx: + case s_fp32_fp64a_fpxxo: + case s_fp32_fp64a_fpxxo_fpxx: + { + requirements.mode1 = FRE_MODE; + break; + } + } + return requirements; +} + +/* Check the current mode is suitable for the specified state. */ + +static bool +current_mode_valid_p (enum fp_state s) +{ + struct fp_mode_req req = compute_fp_modes (s); + return (req.mode1 == current_fp_mode + || req.mode2 == current_fp_mode + || req.mode3 == current_fp_mode); +} + +/* Run the state machine by adding a new object. */ + +static bool +set_next_fp_state (enum fp_obj obj) +{ + cant_change_mode = false; + switch (current_fp_state) + { + + START_STATE(soft) + NEXT(soft,soft) + NEXT(any,soft) + END_STATE + + START_STATE(single) + NEXT(single,single) + NEXT(any,single) + END_STATE + + START_STATE(any) + NEXT_REQ_FR0(fp32, fp32) + NEXT(fpxx, fpxx) + NEXT(fpxxo, fpxxo) + NEXT_REQ_FR1(fp64a, fp64a) + NEXT_REQ_FR1(fp64, fp64) + NEXT(any,any) + NEXT(soft,soft) + NEXT(single,single) + END_STATE + + START_STATE(fp32) + NEXT_REQ_FR0(fp32,fp32) + NEXT(fpxx, fp32_fpxx) + NEXT(fpxxo, fp32_fpxxo) + NEXT_REQ_FRE(fp64a, fp64a_fp32) + NEXT(any,fp32) + END_STATE + + START_STATE(fpxx) + NEXT_REQ_FR0(fp32, fp32_fpxx) + NEXT_REQ_FR1(fp64, fp64_fpxx) + NEXT_REQ_FR1(fp64a, fp64a_fpxx) + NEXT(fpxxo, fpxxo_fpxx) + NEXT(fpxx,fpxx) + NEXT(any,fpxx) + END_STATE + + START_STATE(fpxxo) + NEXT_NO_MODE_CHANGE(fp32, fp32_fpxxo) + NEXT_NO_MODE_CHANGE(fp64, fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo,fpxxo) + NEXT_NO_MODE_CHANGE(any,fpxxo) + END_STATE + + START_STATE(fp64a) + NEXT_REQ_FRE(fp32, fp64a_fp32) + NEXT_REQ_FR1(fp64, fp64a_fp64) + NEXT(fpxxo, fp64a_fpxxo) + NEXT(fpxx, fp64a_fpxx) + NEXT_REQ_FR1(fp64a, fp64a) + NEXT(any, fp64a) + END_STATE + + START_STATE(fp64) + NEXT_REQ_FR1(fp64a, fp64a_fp64) + NEXT(fpxxo, fp64_fpxxo) + NEXT(fpxx, fp64_fpxx) + NEXT_REQ_FR1(fp64, fp64) + NEXT(any, fp64) + END_STATE + + START_STATE(fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp32, fp32_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64, fp64_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(any, fpxxo_fpxx) + END_STATE + + START_STATE(fp32_fpxx) + NEXT_REQ_FR0(fp32, fp32_fpxx) + NEXT(fpxx, fp32_fpxx) + NEXT(fpxxo, fp32_fpxxo_fpxx) + NEXT_REQ_FRE(fp64a, fp32_fp64a_fpxx) + NEXT(any, fp32_fpxx) + END_STATE + + START_STATE(fp32_fpxxo) + NEXT_NO_MODE_CHANGE(fp32, fp32_fpxxo) + NEXT_NO_MODE_CHANGE(fpxxo, fp32_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp32_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64a, fp32_fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(any, fp32_fpxxo) + END_STATE + + START_STATE(fp32_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp32, fp32_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fp32_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxx, fp32_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64a, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(any, fp32_fpxxo_fpxx) + END_STATE + + START_STATE(fp64a_fp32) + NEXT_REQ_FRE(fp32, fp64a_fp32) + NEXT_REQ_FRE(fp64a, fp64a_fp32) + NEXT(fpxxo, fp32_fp64a_fpxxo) + NEXT(fpxx, fp32_fp64a_fpxx) + NEXT(any, fp64a_fp32) + END_STATE + + START_STATE(fp64a_fpxx) + NEXT_REQ_FRE(fp32, fp32_fp64a_fpxx) + NEXT_REQ_FR1(fp64a, fp64a_fpxx) + NEXT(fpxx, fp64a_fpxx) + NEXT(fpxxo, fp64a_fpxx_fpxxo) + NEXT_REQ_FR1(fp64, fp64a_fp64_fpxx) + NEXT(any, fp64a_fpxx) + END_STATE + + START_STATE(fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fp32, fp32_fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp64a_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fpxxo, fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fp64, fp64a_fp64_fpxxo) + NEXT_NO_MODE_CHANGE(any, fp64a_fpxxo) + END_STATE + + START_STATE(fp64a_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fp32, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp64a_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fpxxo, fp64a_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fp64, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(any, fp64a_fpxx_fpxxo) + END_STATE + + START_STATE(fp64_fpxx) + NEXT_REQ_FR1(fp64a, fp64a_fp64_fpxx) + NEXT(fpxxo, fp64_fpxx_fpxxo) + NEXT(fpxx, fp64_fpxx) + NEXT_REQ_FR1(fp64, fp64_fpxx) + NEXT(any, fp64_fpxx) + END_STATE + + START_STATE(fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fpxxo, fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp64_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fp64, fp64_fpxxo) + NEXT_NO_MODE_CHANGE(any, fp64_fpxxo) + END_STATE + + START_STATE(fp64_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fp64_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp64_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fp64, fp64_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(any, fp64_fpxx_fpxxo) + END_STATE + + START_STATE(fp64a_fp64) + NEXT_REQ_FR1(fp64a, fp64a_fp64) + NEXT(fpxxo, fp64a_fp64_fpxxo) + NEXT(fpxx, fp64a_fp64_fpxx) + NEXT_REQ_FR1(fp64, fp64a_fp64) + NEXT(any, fp64a_fp64) + END_STATE + + START_STATE(fp64a_fp64_fpxx) + NEXT_REQ_FR1(fp64a, fp64a_fp64_fpxx) + NEXT(fpxxo, fp64a_fp64_fpxxo_fpxx) + NEXT(fpxx, fp64a_fp64_fpxx) + NEXT_REQ_FR1(fp64, fp64a_fp64_fpxx) + NEXT(any, fp64a_fp64_fpxx) + END_STATE + + START_STATE(fp64a_fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fp64a_fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fp64, fp64a_fp64_fpxxo) + NEXT_NO_MODE_CHANGE(any, fp64a_fp64_fpxxo) + END_STATE + + START_STATE(fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxx, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(any, fp64a_fp64_fpxxo_fpxx) + END_STATE + + START_STATE(fp32_fp64a_fpxx) + NEXT_REQ_FRE(fp32, fp32_fp64a_fpxx) + NEXT_REQ_FRE(fp64a, fp32_fp64a_fpxx) + NEXT(fpxxo, fp32_fp64a_fpxxo_fpxx) + NEXT(fpxx, fp32_fp64a_fpxx) + NEXT(any, fp32_fp64a_fpxx) + END_STATE + + START_STATE(fp32_fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fp32, fp32_fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp32_fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fp32_fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(any, fp32_fp64a_fpxxo) + END_STATE + + START_STATE(fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp32, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64a, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxx, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(any, fp32_fp64a_fpxxo_fpxx) + END_STATE + } + + if (obj != o_max) + fp_obj_count[obj]++; + + return true; +} + +/* Run the state machine by removing an object. */ + +static bool +remove_object (enum fp_obj obj) +{ + if (obj == o_max) + return false; + + fp_obj_count[obj]--; + + /* We can't change fp state until all the objects + of a particular type have been unloaded. */ + if (fp_obj_count[obj] != 0) + return false; + + switch (current_fp_state) + { + START_STATE(soft) + NEXT(soft,any) + END_STATE + + START_STATE(single) + NEXT(single,any) + END_STATE + + START_STATE(any) + NEXT(any,any) + END_STATE + + START_STATE(fp32) + NEXT (fp32,any) + END_STATE + + START_STATE(fpxx) + NEXT (fpxx,any) + END_STATE + + START_STATE(fpxxo) + NEXT (fpxxo,any) + END_STATE + + START_STATE(fp64a) + NEXT(fp64a, any) + END_STATE + + START_STATE(fp64) + NEXT(fp64, any) + END_STATE + + START_STATE(fpxxo_fpxx) + NEXT(fpxx, fpxxo) + NEXT(fpxxo, fpxx) + END_STATE + + START_STATE(fp32_fpxx) + NEXT(fp32, fpxx) + NEXT(fpxx, fp32) + END_STATE + + START_STATE(fp32_fpxxo) + NEXT(fp32, fpxxo) + NEXT(fpxxo, fp32) + END_STATE + + START_STATE(fp32_fpxxo_fpxx) + NEXT(fp32, fpxxo_fpxx) + NEXT(fpxxo, fp32_fpxx) + NEXT(fpxx, fp32_fpxxo) + END_STATE + + START_STATE(fp64a_fp32) + NEXT(fp32, fp64a) + NEXT(fp64a, fp32) + END_STATE + + START_STATE(fp64a_fpxx) + NEXT(fp64a, fpxx) + NEXT(fpxx, fp64a) + END_STATE + + START_STATE(fp64a_fpxxo) + NEXT(fp64a, fpxxo) + NEXT(fpxxo, fp64a) + END_STATE + + START_STATE(fp64a_fpxx_fpxxo) + NEXT(fp64a, fpxxo_fpxx) + NEXT(fpxx, fp64a_fpxxo) + NEXT(fpxxo, fp64a_fpxx) + END_STATE + + START_STATE(fp64_fpxx) + NEXT(fpxx, fp64) + NEXT(fp64, fpxx) + END_STATE + + START_STATE(fp64_fpxxo) + NEXT(fpxxo, fp64) + NEXT(fp64, fpxxo) + END_STATE + + START_STATE(fp64_fpxx_fpxxo) + NEXT(fp64, fpxxo_fpxx) + NEXT(fpxxo, fp64_fpxx) + NEXT(fpxx, fp64_fpxxo) + END_STATE + + START_STATE(fp64a_fp64) + NEXT(fp64a, fp64) + NEXT(fp64, fp64a) + END_STATE + + START_STATE(fp64a_fp64_fpxx) + NEXT(fp64a, fp64_fpxx) + NEXT(fpxx, fp64a_fp64) + NEXT(fp64, fp64a_fpxx) + END_STATE + + START_STATE(fp64a_fp64_fpxxo) + NEXT(fp64a, fp64_fpxxo) + NEXT(fpxxo, fp64a_fp64) + NEXT(fp64, fp64a_fpxxo) + END_STATE + + START_STATE(fp64a_fp64_fpxxo_fpxx) + NEXT(fp64a, fp64_fpxx_fpxxo) + NEXT(fpxx, fp64a_fp64_fpxxo) + NEXT(fpxxo, fp64a_fp64_fpxx) + NEXT(fp64, fp64a_fpxx_fpxxo) + END_STATE + + START_STATE(fp32_fp64a_fpxx) + NEXT(fp32, fp64a_fpxx) + NEXT(fp64a, fp32_fpxx) + NEXT(fpxx, fp64a_fp32) + END_STATE + + START_STATE(fp32_fp64a_fpxxo) + NEXT(fp32, fp64a_fpxxo) + NEXT(fp64a, fp32_fpxxo) + NEXT(fpxxo, fp64a_fp32) + END_STATE + + START_STATE(fp32_fp64a_fpxxo_fpxx) + NEXT(fp32, fp64a_fpxx_fpxxo) + NEXT(fp64a, fp32_fpxxo_fpxx) + NEXT(fpxx, fp32_fp64a_fpxxo) + NEXT(fpxxo, fp32_fp64a_fpxx) + END_STATE + } + + return true; +} + +static int +mode_transition_valid_p (void) +{ + int prev_fp_mode; + + /* Get the current fp mode. */ + prev_fp_mode = current_fp_mode; +#if HAVE_PRCTL_FP_MODE + current_fp_mode = prctl (PR_GET_FP_MODE); + + /* If the prctl call fails assume the core only has FR0 mode support. */ + if (current_fp_mode == -1) + current_fp_mode = 0; +#endif + + if (!current_mode_valid_p (current_fp_state)) + return 0; + + /* Check if mode changes are not allowed but a mode change happened. */ + if (cant_change_mode + && current_fp_mode != prev_fp_mode) + return 0; + + return 1; +} + +/* Load OBJ and check that it was/was not loaded correctly. */ +bool +load_object (enum fp_obj obj) +{ + bool should_load = set_next_fp_state (obj); + + shared_lib_ptrs[obj] = dlopen (shared_lib_names[obj], RTLD_LAZY); + + /* If we expected an error and the load was successful then fail. */ + if (!should_load && (shared_lib_ptrs[obj] != 0)) + return false; + + if (should_load && (shared_lib_ptrs[obj] == 0)) + return false; + + if (!mode_transition_valid_p ()) + return false; + + return true; +} + +/* Remove an object and check the state remains valid. */ +bool +unload_object (enum fp_obj obj) +{ + if (!shared_lib_ptrs[obj]) + return true; + + remove_object (obj); + + if (dlclose (shared_lib_ptrs[obj]) != 0) + return false; + + shared_lib_ptrs[obj] = 0; + + if (!mode_transition_valid_p ()) + return false; + + return true; +} + +/* Load every permuation of OBJECTS. */ +static bool +test_permutations (enum fp_obj objects[], int count) +{ + int i; + + for (i = 0 ; i < count ; i++) + { + if (!load_object (objects[i])) + return false; + + if (count > 1) + { + enum fp_obj new_objects[count - 1]; + int j; + int k = 0; + + for (j = 0 ; j < count ; j++) + { + if (j != i) + new_objects[k++] = objects[j]; + } + + if (!test_permutations (new_objects, count - 1)) + return false; + } + + if (!unload_object (objects[i])) + return false; + } + return true; +} + +int +do_test (void) +{ +#if HAVE_PRCTL_FP_MODE + /* Determine available hardware support and current mode. */ + current_fp_mode = prctl (PR_GET_FP_MODE); + + /* If the prctl call fails assume the core only has FR0 mode support. */ + if (current_fp_mode == -1) + current_fp_mode = 0; + else + { + if (prctl (PR_SET_FP_MODE, 0) != 0) + { + if (errno == ENOTSUP) + is_r6 = true; + else + { + printf ("unexpected error from PR_SET_FP_MODE, 0: %m\n"); + return 1; + } + } + + if (prctl (PR_SET_FP_MODE, PR_FP_MODE_FR) != 0) + { + if (errno != ENOTSUP) + { + printf ("unexpected error from PR_SET_FP_MODE, " + "PR_FP_MODE_FR: %m\n"); + return 1; + } + } + else + has_fr1 = true; + + if (prctl (PR_SET_FP_MODE, PR_FP_MODE_FR | PR_FP_MODE_FRE) != 0) + { + if (errno != ENOTSUP) + { + printf ("unexpected error from PR_SET_FP_MODE, " + "PR_FP_MODE_FR | PR_FP_MODE_FRE: %m\n"); + return 1; + } + } + else + has_fre = true; + + if (prctl (PR_SET_FP_MODE, current_fp_mode) != 0) + { + printf ("unable to restore initial FP mode: %m\n"); + return 1; + } + } + + if ((is_r6 && !(current_fp_mode & PR_FP_MODE_FR)) + || (!has_fr1 && (current_fp_mode & PR_FP_MODE_FR)) + || (!has_fre && (current_fp_mode & PR_FP_MODE_FRE))) + { + puts ("Inconsistency detected between initial FP mode " + "and supported FP modes\n"); + return 1; + } +#else + current_fp_mode = 0; +#endif + + /* Set up the initial state from executable and LDSO. Assumptions: + 1) All system libraries have the same ABI as ld.so. + 2) Due to the fact that ld.so is tested by invoking it directly + rather than via an interpreter, there is no point in varying + the ABI of the test program. Instead the ABI only varies for + the shared libraries which get loaded. */ + if (!set_next_fp_state (FPABI_NATIVE)) + { + puts ("Unable to enter initial ABI state\n"); + return 1; + } + + /* Compare the computed state with the hardware state. */ + if (!mode_transition_valid_p ()) + return 1; + + /* Run all possible test permutations. */ + if (!test_permutations (test_objects, FPABI_COUNT)) + { + puts ("Mode checks failed\n"); + return 1; + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../../test-skeleton.c" diff --git a/sysdeps/mips/tst-mode-switch-1.c b/sysdeps/mips/tst-mode-switch-1.c new file mode 100644 index 0000000000..eb8d68aae8 --- /dev/null +++ b/sysdeps/mips/tst-mode-switch-1.c @@ -0,0 +1,123 @@ +/* Copyright (C) 2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <sys/prctl.h> + +#if __mips_fpr != 0 || _MIPS_SPFPSET != 16 +# error This test requires -mfpxx -mno-odd-spreg +#endif + +/* This test verifies that mode changes do not clobber register state + in other threads. */ + +static volatile int finished; +static int mode[6] = + { + 0, + PR_FP_MODE_FR, + PR_FP_MODE_FR | PR_FP_MODE_FRE, + PR_FP_MODE_FR, + 0, + PR_FP_MODE_FR | PR_FP_MODE_FRE + }; + +static void * +thread_function (void * arg __attribute__ ((unused))) +{ + volatile int i = 0; + volatile float f = 0.0; + volatile double d = 0.0; + + while (!finished) + { + if ((float) i != f || (double) i != d) + { + printf ("unexpected value: i(%d) f(%f) d(%f)\n", i, f, d); + exit (1); + } + + if (i == 100) + { + i = 0; + f = 0.0; + d = 0.0; + } + + i++; + f++; + d++; + } + return NULL; +} + +int +main (void) +{ + int count = sysconf (_SC_NPROCESSORS_ONLN); + if (count <= 0) + count = 1; + count *= 4; + + pthread_t th[count]; + int i; + int result = 0; + + for (i = 0; i < count; ++i) + if (pthread_create (&th[i], NULL, thread_function, 0) != 0) + { + printf ("creation of thread %d failed\n", i); + exit (1); + } + + for (i = 0 ; i < 1000000 ; i++) + { + if (prctl (PR_SET_FP_MODE, mode[i % 6]) != 0 + && errno != ENOTSUP) + { + printf ("prctl PR_SET_FP_MODE failed: %m\n"); + exit (1); + } + } + + finished = 1; + + for (i = 0; i < count; ++i) + { + void *v; + if (pthread_join (th[i], &v) != 0) + { + printf ("join of thread %d failed\n", i); + result = 1; + } + else if (v != NULL) + { + printf ("join %d successful, but child failed\n", i); + result = 1; + } + else + printf ("join %d successful\n", i); + } + + return result; +} diff --git a/sysdeps/mips/tst-mode-switch-2.c b/sysdeps/mips/tst-mode-switch-2.c new file mode 100644 index 0000000000..c328896c2c --- /dev/null +++ b/sysdeps/mips/tst-mode-switch-2.c @@ -0,0 +1,163 @@ +/* Copyright (C) 2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <pthread.h> +#include <sys/prctl.h> + +#if __mips_fpr != 0 || _MIPS_SPFPSET != 16 +# error This test requires -mfpxx -mno-odd-spreg +#endif + +/* This test verifies that all threads in a process see a mode + change when any thread causes a mode change. */ + +static int mode[6] = + { + 0, + PR_FP_MODE_FR, + PR_FP_MODE_FR | PR_FP_MODE_FRE, + PR_FP_MODE_FR, + 0, + PR_FP_MODE_FR | PR_FP_MODE_FRE + }; +static volatile int current_mode; +static volatile int finished; +static pthread_barrier_t barr_ready; +static pthread_barrier_t barr_cont; + +static void * +thread_function (void * arg __attribute__ ((unused))) +{ + while (!finished) + { + int res = pthread_barrier_wait (&barr_ready); + + if (res != 0 && res != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("barrier wait failed: %m\n"); + exit (1); + } + + int mode = prctl (PR_GET_FP_MODE); + + if (mode != current_mode) + { + printf ("unexpected mode: %d != %d\n", mode, current_mode); + exit (1); + } + + res = pthread_barrier_wait (&barr_cont); + + if (res != 0 && res != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("barrier wait failed: %m\n"); + exit (1); + } + } + return NULL; +} + +int +main (void) +{ + int count = sysconf (_SC_NPROCESSORS_ONLN); + if (count <= 0) + count = 1; + count *= 4; + + pthread_t th[count]; + int i; + int result = 0; + + if (pthread_barrier_init (&barr_ready, NULL, count + 1) != 0) + { + printf ("failed to initialize barrier: %m\n"); + exit (1); + } + + if (pthread_barrier_init (&barr_cont, NULL, count + 1) != 0) + { + printf ("failed to initialize barrier: %m\n"); + exit (1); + } + + for (i = 0; i < count; ++i) + if (pthread_create (&th[i], NULL, thread_function, 0) != 0) + { + printf ("creation of thread %d failed\n", i); + exit (1); + } + + for (i = 0 ; i < 7 ; i++) + { + if (prctl (PR_SET_FP_MODE, mode[i % 6]) != 0) + { + if (errno != ENOTSUP) + { + printf ("prctl PR_SET_FP_MODE failed: %m"); + exit (1); + } + } + else + current_mode = mode[i % 6]; + + + int res = pthread_barrier_wait (&barr_ready); + + if (res != 0 && res != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("barrier wait failed: %m\n"); + exit (1); + } + + if (i == 6) + finished = 1; + + res = pthread_barrier_wait (&barr_cont); + + if (res != 0 && res != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("barrier wait failed: %m\n"); + exit (1); + } + } + + for (i = 0; i < count; ++i) + { + void *v; + if (pthread_join (th[i], &v) != 0) + { + printf ("join of thread %d failed\n", i); + result = 1; + } + else if (v != NULL) + { + printf ("join %d successful, but child failed\n", i); + result = 1; + } + else + printf ("join %d successful\n", i); + } + + return result; +} diff --git a/sysdeps/mips/tst-mode-switch-3.c b/sysdeps/mips/tst-mode-switch-3.c new file mode 100644 index 0000000000..9cc725be1a --- /dev/null +++ b/sysdeps/mips/tst-mode-switch-3.c @@ -0,0 +1,90 @@ +/* Copyright (C) 2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <setjmp.h> +#include <sys/prctl.h> + +#if __mips_fpr != 0 || _MIPS_SPFPSET != 16 +# error This test requires -mfpxx -mno-odd-spreg +#endif + +/* This test verifies that mode changes between a setjmp and longjmp do + not corrupt the state of callee-saved registers. */ + +static int mode[6] = + { + 0, + PR_FP_MODE_FR, + PR_FP_MODE_FR | PR_FP_MODE_FRE, + PR_FP_MODE_FR, + 0, + PR_FP_MODE_FR | PR_FP_MODE_FRE + }; +static jmp_buf env; +float check1 = 2.0; +double check2 = 3.0; + +int +main (void) +{ + int i; + int result = 0; + + for (i = 0 ; i < 7 ; i++) + { + int retval; + register float test1 __asm ("$f20"); + register double test2 __asm ("$f22"); + + /* Hide what we are doing to $f20 and $f22 from the compiler. */ + __asm __volatile ("l.s %0,%2\n" + "l.d %1,%3\n" + : "=f" (test1), "=f" (test2) + : "m" (check1), "m" (check2)); + + retval = setjmp (env); + + /* Make sure the compiler knows we want to access the variables + via the named registers again. */ + __asm __volatile ("" : : "f" (test1), "f" (test2)); + + if (test1 != check1 || test2 != check2) + { + printf ("Corrupt register detected: $20 %f = %f, $22 %f = %f\n", + test1, check1, test2, check2); + result = 1; + } + + if (retval == 0) + { + if (prctl (PR_SET_FP_MODE, mode[i % 6]) != 0 + && errno != ENOTSUP) + { + printf ("prctl PR_SET_FP_MODE failed: %m"); + exit (1); + } + longjmp (env, 0); + } + } + + return result; +} |