Commit 620b1550 authored by Paul Burton's avatar Paul Burton Committed by Ralf Baechle

MIPS: fix FP mode selection in lieu of .MIPS.abiflags data

Commit 46490b57 ("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: default avatarPaul 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: default avatarRalf Baechle <ralf@linux-mips.org>
parent cafb45b2
...@@ -76,14 +76,6 @@ int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf, ...@@ -76,14 +76,6 @@ int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf,
/* Lets see if this is an O32 ELF */ /* Lets see if this is an O32 ELF */
if (ehdr32->e_ident[EI_CLASS] == ELFCLASS32) { if (ehdr32->e_ident[EI_CLASS] == ELFCLASS32) {
/* FR = 1 for N32 */
if (ehdr32->e_flags & EF_MIPS_ABI2)
state->overall_fp_mode = FP_FR1;
else
/* Set a good default FPU mode for O32 */
state->overall_fp_mode = cpu_has_mips_r6 ?
FP_FRE : FP_FR0;
if (ehdr32->e_flags & EF_MIPS_FP64) { if (ehdr32->e_flags & EF_MIPS_FP64) {
/* /*
* Set MIPS_ABI_FP_OLD_64 for EF_MIPS_FP64. We will override it * 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,9 +96,6 @@ int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf,
(char *)&abiflags, (char *)&abiflags,
sizeof(abiflags)); sizeof(abiflags));
} else { } else {
/* FR=1 is really the only option for 64-bit */
state->overall_fp_mode = FP_FR1;
if (phdr64->p_type != PT_MIPS_ABIFLAGS) if (phdr64->p_type != PT_MIPS_ABIFLAGS)
return 0; return 0;
if (phdr64->p_filesz < sizeof(abiflags)) if (phdr64->p_filesz < sizeof(abiflags))
...@@ -137,6 +126,7 @@ int arch_check_elf(void *_ehdr, bool has_interpreter, ...@@ -137,6 +126,7 @@ int arch_check_elf(void *_ehdr, bool has_interpreter,
struct elf32_hdr *ehdr = _ehdr; struct elf32_hdr *ehdr = _ehdr;
struct mode_req prog_req, interp_req; struct mode_req prog_req, interp_req;
int fp_abi, interp_fp_abi, abi0, abi1, max_abi; int fp_abi, interp_fp_abi, abi0, abi1, max_abi;
bool is_mips64;
if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT)) if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT))
return 0; return 0;
...@@ -152,10 +142,22 @@ int arch_check_elf(void *_ehdr, bool has_interpreter, ...@@ -152,10 +142,22 @@ int arch_check_elf(void *_ehdr, bool has_interpreter,
abi0 = abi1 = fp_abi; abi0 = abi1 = fp_abi;
} }
/* ABI limits. O32 = FP_64A, N32/N64 = FP_SOFT */ is_mips64 = (ehdr->e_ident[EI_CLASS] == ELFCLASS64) ||
max_abi = ((ehdr->e_ident[EI_CLASS] == ELFCLASS32) && (ehdr->e_flags & EF_MIPS_ABI2);
(!(ehdr->e_flags & EF_MIPS_ABI2))) ?
MIPS_ABI_FP_64A : MIPS_ABI_FP_SOFT; if (is_mips64) {
/* MIPS64 code always uses FR=1, thus the default is easy */
state->overall_fp_mode = FP_FR1;
/* Disallow access to the various FPXX & FP64 ABIs */
max_abi = MIPS_ABI_FP_SOFT;
} else {
/* Default to a mode capable of running code expecting FR=0 */
state->overall_fp_mode = cpu_has_mips_r6 ? FP_FRE : FP_FR0;
/* Allow all ABIs we know about */
max_abi = MIPS_ABI_FP_64A;
}
if ((abi0 > max_abi && abi0 != MIPS_ABI_FP_UNKNOWN) || if ((abi0 > max_abi && abi0 != MIPS_ABI_FP_UNKNOWN) ||
(abi1 > max_abi && abi1 != MIPS_ABI_FP_UNKNOWN)) (abi1 > max_abi && abi1 != MIPS_ABI_FP_UNKNOWN))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment