diff options
author | Paul Burton <paul.burton@imgtec.com> | 2015-05-06 06:52:32 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2015-05-12 12:54:05 -0400 |
commit | 620b155034570f577470cf5309f741bac6a6e32b (patch) | |
tree | c3264695ac23aee0873484bf466f9ba7bc13f7f3 /arch/mips | |
parent | cafb45b2562baa57cb58bef0636c073705954cc4 (diff) |
MIPS: fix FP mode selection in lieu of .MIPS.abiflags data
Commit 46490b572544 ("MIPS: kernel: elf: Improve the overall ABI and FPU
mode checks") reworked the ELF FP ABI mode selection logic, but when
CONFIG_MIPS_O32_FP64_SUPPORT is enabled it breaks the use of binaries
which have no PT_MIPS_ABIFLAGS program header & associated
.MIPS.abiflags section.
A default mode is selected based upon whether the ELF contains MIPS32 or
MIPS64 code, but that selection is made in arch_elf_pt_proc.
arch_elf_pt_proc only executes when a PT_MIPS_ABIFLAGS program header is
found. If one is not found then arch_elf_pt_proc is never called, and no
default overall_fp_mode value is selected. When arch_check_elf is
called, both abi0 & abi1 are MIPS_ABI_FP_UNKNOWN which leads to both
prog_req & interp_req being set to none_req. none_req matches none of
the conditions for mode selection at the end of arch_check_elf, so
overall_fp_mode is left untouched. Finally once mips_set_personality_fp
is called the BUG() in the default case is then hit & the kernel likely
panics.
Fix this by moving the selection of a default overall mode to the start
of arch_check_elf, which runs once per ELF executed regardless of
whether it has a PT_MIPS_ABIFLAGS program header.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: Markos Chandras <markos.chandras@imgtec.com>
Cc: Matthew Fortune <matthew.fortune@imgtec.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-mips@linux-mips.org
Cc: stable@vger.kernel.org # v4.0+
Patchwork: http://patchwork.linux-mips.org/patch/9978/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/kernel/elf.c | 32 |
1 files changed, 17 insertions, 15 deletions
diff --git a/arch/mips/kernel/elf.c b/arch/mips/kernel/elf.c index be4899f3c393..4a4d9e067c89 100644 --- a/arch/mips/kernel/elf.c +++ b/arch/mips/kernel/elf.c | |||
@@ -76,14 +76,6 @@ int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf, | |||
76 | 76 | ||
77 | /* Lets see if this is an O32 ELF */ | 77 | /* Lets see if this is an O32 ELF */ |
78 | if (ehdr32->e_ident[EI_CLASS] == ELFCLASS32) { | 78 | if (ehdr32->e_ident[EI_CLASS] == ELFCLASS32) { |
79 | /* FR = 1 for N32 */ | ||
80 | if (ehdr32->e_flags & EF_MIPS_ABI2) | ||
81 | state->overall_fp_mode = FP_FR1; | ||
82 | else | ||
83 | /* Set a good default FPU mode for O32 */ | ||
84 | state->overall_fp_mode = cpu_has_mips_r6 ? | ||
85 | FP_FRE : FP_FR0; | ||
86 | |||
87 | if (ehdr32->e_flags & EF_MIPS_FP64) { | 79 | if (ehdr32->e_flags & EF_MIPS_FP64) { |
88 | /* | 80 | /* |
89 | * Set MIPS_ABI_FP_OLD_64 for EF_MIPS_FP64. We will override it | 81 | * Set MIPS_ABI_FP_OLD_64 for EF_MIPS_FP64. We will override it |
@@ -104,9 +96,6 @@ int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf, | |||
104 | (char *)&abiflags, | 96 | (char *)&abiflags, |
105 | sizeof(abiflags)); | 97 | sizeof(abiflags)); |
106 | } else { | 98 | } else { |
107 | /* FR=1 is really the only option for 64-bit */ | ||
108 | state->overall_fp_mode = FP_FR1; | ||
109 | |||
110 | if (phdr64->p_type != PT_MIPS_ABIFLAGS) | 99 | if (phdr64->p_type != PT_MIPS_ABIFLAGS) |
111 | return 0; | 100 | return 0; |
112 | if (phdr64->p_filesz < sizeof(abiflags)) | 101 | if (phdr64->p_filesz < sizeof(abiflags)) |
@@ -137,6 +126,7 @@ int arch_check_elf(void *_ehdr, bool has_interpreter, | |||
137 | struct elf32_hdr *ehdr = _ehdr; | 126 | struct elf32_hdr *ehdr = _ehdr; |
138 | struct mode_req prog_req, interp_req; | 127 | struct mode_req prog_req, interp_req; |
139 | int fp_abi, interp_fp_abi, abi0, abi1, max_abi; | 128 | int fp_abi, interp_fp_abi, abi0, abi1, max_abi; |
129 | bool is_mips64; | ||
140 | 130 | ||
141 | if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT)) | 131 | if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT)) |
142 | return 0; | 132 | return 0; |
@@ -152,10 +142,22 @@ int arch_check_elf(void *_ehdr, bool has_interpreter, | |||
152 | abi0 = abi1 = fp_abi; | 142 | abi0 = abi1 = fp_abi; |
153 | } | 143 | } |
154 | 144 | ||
155 | /* ABI limits. O32 = FP_64A, N32/N64 = FP_SOFT */ | 145 | is_mips64 = (ehdr->e_ident[EI_CLASS] == ELFCLASS64) || |
156 | max_abi = ((ehdr->e_ident[EI_CLASS] == ELFCLASS32) && | 146 | (ehdr->e_flags & EF_MIPS_ABI2); |
157 | (!(ehdr->e_flags & EF_MIPS_ABI2))) ? | 147 | |
158 | MIPS_ABI_FP_64A : MIPS_ABI_FP_SOFT; | 148 | if (is_mips64) { |
149 | /* MIPS64 code always uses FR=1, thus the default is easy */ | ||
150 | state->overall_fp_mode = FP_FR1; | ||
151 | |||
152 | /* Disallow access to the various FPXX & FP64 ABIs */ | ||
153 | max_abi = MIPS_ABI_FP_SOFT; | ||
154 | } else { | ||
155 | /* Default to a mode capable of running code expecting FR=0 */ | ||
156 | state->overall_fp_mode = cpu_has_mips_r6 ? FP_FRE : FP_FR0; | ||
157 | |||
158 | /* Allow all ABIs we know about */ | ||
159 | max_abi = MIPS_ABI_FP_64A; | ||
160 | } | ||
159 | 161 | ||
160 | if ((abi0 > max_abi && abi0 != MIPS_ABI_FP_UNKNOWN) || | 162 | if ((abi0 > max_abi && abi0 != MIPS_ABI_FP_UNKNOWN) || |
161 | (abi1 > max_abi && abi1 != MIPS_ABI_FP_UNKNOWN)) | 163 | (abi1 > max_abi && abi1 != MIPS_ABI_FP_UNKNOWN)) |