All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/10] MIPS O32 new FP ABI support
@ 2014-09-11  7:30 ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

This series introduces support for reading the FP mode requirements of
ELF binaries from their .MIPS.abiflags section when present, and
configuring the system appropriately. The motivation for these new ABIs
is to enable O32 binaries to operate with a 64b FPU (Status.FR=1) and
therefore to run on systems where that is the only FPU available, allow
use of MSA & do so without rebuilding the world. For further details on
the new ABIs please see:

  https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking

The first 3 patches of the series extend binfmt_elf with hooks such that
the MIPS architecture code can access the FP mode information & reject
invalid FP modes.

Patches 4-7 introduce support for operating with "hybrid FPRs" - a
scheme in which odd single precision registers alias with the upper 32b
of the preceeding even double as is typical with FR=0, but where all
32 64b doubles are also available. This is implemented by trapping &
emulating single precision register accesses using a new Config5.FRE
bit.

Patches 8 & 9 introduce the .MIPS.abiflags section along with the kernel
support for reading it & treating binaries accordingly.

Patch 10 adds a simple debug option to test the implementation of hybrid
FPRs by forcing all code that can operate in that mode to do so, which
is usually avoided due to the overhead of trapping & emulating.

Paul Burton (10):
  binfmt_elf: hoist ELF program header loading to a function
  binfmt_elf: load interpreter program headers earlier
  binfmt_elf: allow arch code to examine PT_LOPROC ... PT_HIPROC headers
  MIPS: define bits introduced for hybrid FPRs
  MIPS: detect presence of the FRE & UFR bits
  MIPS: ensure Config5.UFE is clear on boot
  MIPS: support for hybrid FPRs
  MIPS: ELF: add definition for the .MIPS.abiflags section
  MIPS: ELF: set FP mode according to .MIPS.abiflags
  MIPS: Kconfig option to better exercise/debug hybrid FPRs

 arch/mips/Kconfig                    |   1 +
 arch/mips/Kconfig.debug              |  13 +++
 arch/mips/include/asm/cpu-features.h |   4 +
 arch/mips/include/asm/cpu.h          |   1 +
 arch/mips/include/asm/elf.h          |  74 +++++++++++---
 arch/mips/include/asm/fpu.h          |  49 ++++++++--
 arch/mips/include/asm/mipsregs.h     |   3 +
 arch/mips/include/asm/thread_info.h  |   2 +
 arch/mips/kernel/Makefile            |   7 +-
 arch/mips/kernel/cpu-probe.c         |   4 +-
 arch/mips/kernel/elf.c               | 181 +++++++++++++++++++++++++++++++++++
 arch/mips/kernel/traps.c             |  47 +++++++++
 arch/mips/math-emu/cp1emu.c          |   9 +-
 fs/Kconfig.binfmt                    |   3 +
 fs/binfmt_elf.c                      | 169 ++++++++++++++++++++------------
 include/linux/elf.h                  |  73 ++++++++++++++
 16 files changed, 549 insertions(+), 91 deletions(-)
 create mode 100644 arch/mips/kernel/elf.c

-- 
2.0.4


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH 00/10] MIPS O32 new FP ABI support
@ 2014-09-11  7:30 ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

This series introduces support for reading the FP mode requirements of
ELF binaries from their .MIPS.abiflags section when present, and
configuring the system appropriately. The motivation for these new ABIs
is to enable O32 binaries to operate with a 64b FPU (Status.FR=1) and
therefore to run on systems where that is the only FPU available, allow
use of MSA & do so without rebuilding the world. For further details on
the new ABIs please see:

  https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking

The first 3 patches of the series extend binfmt_elf with hooks such that
the MIPS architecture code can access the FP mode information & reject
invalid FP modes.

Patches 4-7 introduce support for operating with "hybrid FPRs" - a
scheme in which odd single precision registers alias with the upper 32b
of the preceeding even double as is typical with FR=0, but where all
32 64b doubles are also available. This is implemented by trapping &
emulating single precision register accesses using a new Config5.FRE
bit.

Patches 8 & 9 introduce the .MIPS.abiflags section along with the kernel
support for reading it & treating binaries accordingly.

Patch 10 adds a simple debug option to test the implementation of hybrid
FPRs by forcing all code that can operate in that mode to do so, which
is usually avoided due to the overhead of trapping & emulating.

Paul Burton (10):
  binfmt_elf: hoist ELF program header loading to a function
  binfmt_elf: load interpreter program headers earlier
  binfmt_elf: allow arch code to examine PT_LOPROC ... PT_HIPROC headers
  MIPS: define bits introduced for hybrid FPRs
  MIPS: detect presence of the FRE & UFR bits
  MIPS: ensure Config5.UFE is clear on boot
  MIPS: support for hybrid FPRs
  MIPS: ELF: add definition for the .MIPS.abiflags section
  MIPS: ELF: set FP mode according to .MIPS.abiflags
  MIPS: Kconfig option to better exercise/debug hybrid FPRs

 arch/mips/Kconfig                    |   1 +
 arch/mips/Kconfig.debug              |  13 +++
 arch/mips/include/asm/cpu-features.h |   4 +
 arch/mips/include/asm/cpu.h          |   1 +
 arch/mips/include/asm/elf.h          |  74 +++++++++++---
 arch/mips/include/asm/fpu.h          |  49 ++++++++--
 arch/mips/include/asm/mipsregs.h     |   3 +
 arch/mips/include/asm/thread_info.h  |   2 +
 arch/mips/kernel/Makefile            |   7 +-
 arch/mips/kernel/cpu-probe.c         |   4 +-
 arch/mips/kernel/elf.c               | 181 +++++++++++++++++++++++++++++++++++
 arch/mips/kernel/traps.c             |  47 +++++++++
 arch/mips/math-emu/cp1emu.c          |   9 +-
 fs/Kconfig.binfmt                    |   3 +
 fs/binfmt_elf.c                      | 169 ++++++++++++++++++++------------
 include/linux/elf.h                  |  73 ++++++++++++++
 16 files changed, 549 insertions(+), 91 deletions(-)
 create mode 100644 arch/mips/kernel/elf.c

-- 
2.0.4

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH 01/10] binfmt_elf: hoist ELF program header loading to a function
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

load_elf_binary & load_elf_interp both load program headers from an ELF
executable in the same way, duplicating the code. This patch introduces
a helper function (load_elf_phdrs) which performs this common task &
calls it from both load_elf_binary & load_elf_interp. In addition to
reducing code duplication, this is part of preparing to load the ELF
interpreter headers earlier such that they can be examined before it's
too late to return an error from an exec syscall.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 fs/binfmt_elf.c | 99 ++++++++++++++++++++++++++++++++-------------------------
 1 file changed, 56 insertions(+), 43 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 3892c1a..64ca110 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -386,6 +386,59 @@ static unsigned long total_mapping_size(struct elf_phdr *cmds, int nr)
 				ELF_PAGESTART(cmds[first_idx].p_vaddr);
 }
 
+/**
+ * load_elf_phdrs() - load ELF program headers
+ * @elf_ex:   ELF header of the binary whose program headers should be loaded
+ * @elf_file: the opened ELF binary file
+ *
+ * Loads ELF program headers from the binary file elf_file, which has the ELF
+ * header pointed to by elf_ex, into a newly allocated array. The caller is
+ * responsible for freeing the allocated data. Returns an ERR_PTR upon failure.
+ */
+static struct elf_phdr *load_elf_phdrs(struct elfhdr *elf_ex,
+				       struct file *elf_file)
+{
+	struct elf_phdr *elf_phdata = NULL;
+	int retval, size, err = -1;
+
+	/*
+	 * If the size of this structure has changed, then punt, since
+	 * we will be doing the wrong thing.
+	 */
+	if (elf_ex->e_phentsize != sizeof(struct elf_phdr))
+		goto out;
+
+	/* Sanity check the number of program headers... */
+	if (elf_ex->e_phnum < 1 ||
+		elf_ex->e_phnum > 65536U / sizeof(struct elf_phdr))
+		goto out;
+
+	/* ...and their total size. */
+	size = sizeof(struct elf_phdr) * elf_ex->e_phnum;
+	if (size > ELF_MIN_ALIGN)
+		goto out;
+
+	elf_phdata = kmalloc(size, GFP_KERNEL);
+	if (!elf_phdata)
+		goto out;
+
+	/* Read in the program headers */
+	retval = kernel_read(elf_file, elf_ex->e_phoff,
+			     (char *)elf_phdata, size);
+	if (retval != size) {
+		err = (retval < 0) ? retval : -EIO;
+		goto out;
+	}
+
+	/* Success! */
+	err = 0;
+out:
+	if (err) {
+		kfree(elf_phdata);
+		elf_phdata = NULL;
+	}
+	return elf_phdata;
+}
 
 /* This is much more generalized than the library routine read function,
    so we keep this separate.  Technically the library read function
@@ -403,7 +456,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 	unsigned long last_bss = 0, elf_bss = 0;
 	unsigned long error = ~0UL;
 	unsigned long total_size;
-	int retval, i, size;
+	int i;
 
 	/* First of all, some simple consistency checks */
 	if (interp_elf_ex->e_type != ET_EXEC &&
@@ -414,33 +467,10 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 	if (!interpreter->f_op->mmap)
 		goto out;
 
-	/*
-	 * If the size of this structure has changed, then punt, since
-	 * we will be doing the wrong thing.
-	 */
-	if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr))
-		goto out;
-	if (interp_elf_ex->e_phnum < 1 ||
-		interp_elf_ex->e_phnum > 65536U / sizeof(struct elf_phdr))
-		goto out;
-
-	/* Now read in all of the header information */
-	size = sizeof(struct elf_phdr) * interp_elf_ex->e_phnum;
-	if (size > ELF_MIN_ALIGN)
-		goto out;
-	elf_phdata = kmalloc(size, GFP_KERNEL);
+	elf_phdata = load_elf_phdrs(interp_elf_ex, interpreter);
 	if (!elf_phdata)
 		goto out;
 
-	retval = kernel_read(interpreter, interp_elf_ex->e_phoff,
-			     (char *)elf_phdata, size);
-	error = -EIO;
-	if (retval != size) {
-		if (retval < 0)
-			error = retval;	
-		goto out_close;
-	}
-
 	total_size = total_mapping_size(elf_phdata, interp_elf_ex->e_phnum);
 	if (!total_size) {
 		error = -EINVAL;
@@ -578,7 +608,6 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	struct elf_phdr *elf_ppnt, *elf_phdata;
 	unsigned long elf_bss, elf_brk;
 	int retval, i;
-	unsigned int size;
 	unsigned long elf_entry;
 	unsigned long interp_load_addr = 0;
 	unsigned long start_code, end_code, start_data, end_data;
@@ -611,26 +640,10 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	if (!bprm->file->f_op->mmap)
 		goto out;
 
-	/* Now read in all of the header information */
-	if (loc->elf_ex.e_phentsize != sizeof(struct elf_phdr))
-		goto out;
-	if (loc->elf_ex.e_phnum < 1 ||
-	 	loc->elf_ex.e_phnum > 65536U / sizeof(struct elf_phdr))
-		goto out;
-	size = loc->elf_ex.e_phnum * sizeof(struct elf_phdr);
-	retval = -ENOMEM;
-	elf_phdata = kmalloc(size, GFP_KERNEL);
+	elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file);
 	if (!elf_phdata)
 		goto out;
 
-	retval = kernel_read(bprm->file, loc->elf_ex.e_phoff,
-			     (char *)elf_phdata, size);
-	if (retval != size) {
-		if (retval >= 0)
-			retval = -EIO;
-		goto out_free_ph;
-	}
-
 	elf_ppnt = elf_phdata;
 	elf_bss = 0;
 	elf_brk = 0;
-- 
2.0.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 01/10] binfmt_elf: hoist ELF program header loading to a function
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

load_elf_binary & load_elf_interp both load program headers from an ELF
executable in the same way, duplicating the code. This patch introduces
a helper function (load_elf_phdrs) which performs this common task &
calls it from both load_elf_binary & load_elf_interp. In addition to
reducing code duplication, this is part of preparing to load the ELF
interpreter headers earlier such that they can be examined before it's
too late to return an error from an exec syscall.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 fs/binfmt_elf.c | 99 ++++++++++++++++++++++++++++++++-------------------------
 1 file changed, 56 insertions(+), 43 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 3892c1a..64ca110 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -386,6 +386,59 @@ static unsigned long total_mapping_size(struct elf_phdr *cmds, int nr)
 				ELF_PAGESTART(cmds[first_idx].p_vaddr);
 }
 
+/**
+ * load_elf_phdrs() - load ELF program headers
+ * @elf_ex:   ELF header of the binary whose program headers should be loaded
+ * @elf_file: the opened ELF binary file
+ *
+ * Loads ELF program headers from the binary file elf_file, which has the ELF
+ * header pointed to by elf_ex, into a newly allocated array. The caller is
+ * responsible for freeing the allocated data. Returns an ERR_PTR upon failure.
+ */
+static struct elf_phdr *load_elf_phdrs(struct elfhdr *elf_ex,
+				       struct file *elf_file)
+{
+	struct elf_phdr *elf_phdata = NULL;
+	int retval, size, err = -1;
+
+	/*
+	 * If the size of this structure has changed, then punt, since
+	 * we will be doing the wrong thing.
+	 */
+	if (elf_ex->e_phentsize != sizeof(struct elf_phdr))
+		goto out;
+
+	/* Sanity check the number of program headers... */
+	if (elf_ex->e_phnum < 1 ||
+		elf_ex->e_phnum > 65536U / sizeof(struct elf_phdr))
+		goto out;
+
+	/* ...and their total size. */
+	size = sizeof(struct elf_phdr) * elf_ex->e_phnum;
+	if (size > ELF_MIN_ALIGN)
+		goto out;
+
+	elf_phdata = kmalloc(size, GFP_KERNEL);
+	if (!elf_phdata)
+		goto out;
+
+	/* Read in the program headers */
+	retval = kernel_read(elf_file, elf_ex->e_phoff,
+			     (char *)elf_phdata, size);
+	if (retval != size) {
+		err = (retval < 0) ? retval : -EIO;
+		goto out;
+	}
+
+	/* Success! */
+	err = 0;
+out:
+	if (err) {
+		kfree(elf_phdata);
+		elf_phdata = NULL;
+	}
+	return elf_phdata;
+}
 
 /* This is much more generalized than the library routine read function,
    so we keep this separate.  Technically the library read function
@@ -403,7 +456,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 	unsigned long last_bss = 0, elf_bss = 0;
 	unsigned long error = ~0UL;
 	unsigned long total_size;
-	int retval, i, size;
+	int i;
 
 	/* First of all, some simple consistency checks */
 	if (interp_elf_ex->e_type != ET_EXEC &&
@@ -414,33 +467,10 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 	if (!interpreter->f_op->mmap)
 		goto out;
 
-	/*
-	 * If the size of this structure has changed, then punt, since
-	 * we will be doing the wrong thing.
-	 */
-	if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr))
-		goto out;
-	if (interp_elf_ex->e_phnum < 1 ||
-		interp_elf_ex->e_phnum > 65536U / sizeof(struct elf_phdr))
-		goto out;
-
-	/* Now read in all of the header information */
-	size = sizeof(struct elf_phdr) * interp_elf_ex->e_phnum;
-	if (size > ELF_MIN_ALIGN)
-		goto out;
-	elf_phdata = kmalloc(size, GFP_KERNEL);
+	elf_phdata = load_elf_phdrs(interp_elf_ex, interpreter);
 	if (!elf_phdata)
 		goto out;
 
-	retval = kernel_read(interpreter, interp_elf_ex->e_phoff,
-			     (char *)elf_phdata, size);
-	error = -EIO;
-	if (retval != size) {
-		if (retval < 0)
-			error = retval;	
-		goto out_close;
-	}
-
 	total_size = total_mapping_size(elf_phdata, interp_elf_ex->e_phnum);
 	if (!total_size) {
 		error = -EINVAL;
@@ -578,7 +608,6 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	struct elf_phdr *elf_ppnt, *elf_phdata;
 	unsigned long elf_bss, elf_brk;
 	int retval, i;
-	unsigned int size;
 	unsigned long elf_entry;
 	unsigned long interp_load_addr = 0;
 	unsigned long start_code, end_code, start_data, end_data;
@@ -611,26 +640,10 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	if (!bprm->file->f_op->mmap)
 		goto out;
 
-	/* Now read in all of the header information */
-	if (loc->elf_ex.e_phentsize != sizeof(struct elf_phdr))
-		goto out;
-	if (loc->elf_ex.e_phnum < 1 ||
-	 	loc->elf_ex.e_phnum > 65536U / sizeof(struct elf_phdr))
-		goto out;
-	size = loc->elf_ex.e_phnum * sizeof(struct elf_phdr);
-	retval = -ENOMEM;
-	elf_phdata = kmalloc(size, GFP_KERNEL);
+	elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file);
 	if (!elf_phdata)
 		goto out;
 
-	retval = kernel_read(bprm->file, loc->elf_ex.e_phoff,
-			     (char *)elf_phdata, size);
-	if (retval != size) {
-		if (retval >= 0)
-			retval = -EIO;
-		goto out_free_ph;
-	}
-
 	elf_ppnt = elf_phdata;
 	elf_bss = 0;
 	elf_brk = 0;
-- 
2.0.4

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 02/10] binfmt_elf: load interpreter program headers earlier
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

Load the program headers of an ELF interpreter early enough in
load_elf_binary that they can be examined before it's too late to return
an error from an exec syscall. This patch does not perform any such
checking, it merely lays the groundwork for a further patch to do so.

No functional change is intended.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 fs/binfmt_elf.c | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 64ca110..61dabe0 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -447,9 +447,8 @@ out:
 
 static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 		struct file *interpreter, unsigned long *interp_map_addr,
-		unsigned long no_base)
+		unsigned long no_base, struct elf_phdr *interp_elf_phdata)
 {
-	struct elf_phdr *elf_phdata;
 	struct elf_phdr *eppnt;
 	unsigned long load_addr = 0;
 	int load_addr_set = 0;
@@ -467,17 +466,14 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 	if (!interpreter->f_op->mmap)
 		goto out;
 
-	elf_phdata = load_elf_phdrs(interp_elf_ex, interpreter);
-	if (!elf_phdata)
-		goto out;
-
-	total_size = total_mapping_size(elf_phdata, interp_elf_ex->e_phnum);
+	total_size = total_mapping_size(interp_elf_phdata,
+					interp_elf_ex->e_phnum);
 	if (!total_size) {
 		error = -EINVAL;
-		goto out_close;
+		goto out;
 	}
 
-	eppnt = elf_phdata;
+	eppnt = interp_elf_phdata;
 	for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++) {
 		if (eppnt->p_type == PT_LOAD) {
 			int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
@@ -504,7 +500,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 				*interp_map_addr = map_addr;
 			error = map_addr;
 			if (BAD_ADDR(map_addr))
-				goto out_close;
+				goto out;
 
 			if (!load_addr_set &&
 			    interp_elf_ex->e_type == ET_DYN) {
@@ -523,7 +519,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 			    eppnt->p_memsz > TASK_SIZE ||
 			    TASK_SIZE - eppnt->p_memsz < k) {
 				error = -ENOMEM;
-				goto out_close;
+				goto out;
 			}
 
 			/*
@@ -553,7 +549,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 		 */
 		if (padzero(elf_bss)) {
 			error = -EFAULT;
-			goto out_close;
+			goto out;
 		}
 
 		/* What we have mapped so far */
@@ -562,13 +558,10 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 		/* Map the last of the bss segment */
 		error = vm_brk(elf_bss, last_bss - elf_bss);
 		if (BAD_ADDR(error))
-			goto out_close;
+			goto out;
 	}
 
 	error = load_addr;
-
-out_close:
-	kfree(elf_phdata);
 out:
 	return error;
 }
@@ -605,7 +598,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	int load_addr_set = 0;
 	char * elf_interpreter = NULL;
 	unsigned long error;
-	struct elf_phdr *elf_ppnt, *elf_phdata;
+	struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
 	unsigned long elf_bss, elf_brk;
 	int retval, i;
 	unsigned long elf_entry;
@@ -729,6 +722,12 @@ static int load_elf_binary(struct linux_binprm *bprm)
 		/* Verify the interpreter has a valid arch */
 		if (!elf_check_arch(&loc->interp_elf_ex))
 			goto out_free_dentry;
+
+		/* Load the interpreter program headers */
+		interp_elf_phdata = load_elf_phdrs(&loc->interp_elf_ex,
+						   interpreter);
+		if (!interp_elf_phdata)
+			goto out_free_dentry;
 	}
 
 	/* Flush all traces of the currently running executable */
@@ -912,7 +911,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 		elf_entry = load_elf_interp(&loc->interp_elf_ex,
 					    interpreter,
 					    &interp_map_addr,
-					    load_bias);
+					    load_bias, interp_elf_phdata);
 		if (!IS_ERR((void *)elf_entry)) {
 			/*
 			 * load_elf_interp() returns relocation
@@ -1009,6 +1008,7 @@ out_ret:
 
 	/* error cleanup */
 out_free_dentry:
+	kfree(interp_elf_phdata);
 	allow_write_access(interpreter);
 	if (interpreter)
 		fput(interpreter);
-- 
2.0.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 02/10] binfmt_elf: load interpreter program headers earlier
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

Load the program headers of an ELF interpreter early enough in
load_elf_binary that they can be examined before it's too late to return
an error from an exec syscall. This patch does not perform any such
checking, it merely lays the groundwork for a further patch to do so.

No functional change is intended.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 fs/binfmt_elf.c | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 64ca110..61dabe0 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -447,9 +447,8 @@ out:
 
 static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 		struct file *interpreter, unsigned long *interp_map_addr,
-		unsigned long no_base)
+		unsigned long no_base, struct elf_phdr *interp_elf_phdata)
 {
-	struct elf_phdr *elf_phdata;
 	struct elf_phdr *eppnt;
 	unsigned long load_addr = 0;
 	int load_addr_set = 0;
@@ -467,17 +466,14 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 	if (!interpreter->f_op->mmap)
 		goto out;
 
-	elf_phdata = load_elf_phdrs(interp_elf_ex, interpreter);
-	if (!elf_phdata)
-		goto out;
-
-	total_size = total_mapping_size(elf_phdata, interp_elf_ex->e_phnum);
+	total_size = total_mapping_size(interp_elf_phdata,
+					interp_elf_ex->e_phnum);
 	if (!total_size) {
 		error = -EINVAL;
-		goto out_close;
+		goto out;
 	}
 
-	eppnt = elf_phdata;
+	eppnt = interp_elf_phdata;
 	for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++) {
 		if (eppnt->p_type == PT_LOAD) {
 			int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
@@ -504,7 +500,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 				*interp_map_addr = map_addr;
 			error = map_addr;
 			if (BAD_ADDR(map_addr))
-				goto out_close;
+				goto out;
 
 			if (!load_addr_set &&
 			    interp_elf_ex->e_type == ET_DYN) {
@@ -523,7 +519,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 			    eppnt->p_memsz > TASK_SIZE ||
 			    TASK_SIZE - eppnt->p_memsz < k) {
 				error = -ENOMEM;
-				goto out_close;
+				goto out;
 			}
 
 			/*
@@ -553,7 +549,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 		 */
 		if (padzero(elf_bss)) {
 			error = -EFAULT;
-			goto out_close;
+			goto out;
 		}
 
 		/* What we have mapped so far */
@@ -562,13 +558,10 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 		/* Map the last of the bss segment */
 		error = vm_brk(elf_bss, last_bss - elf_bss);
 		if (BAD_ADDR(error))
-			goto out_close;
+			goto out;
 	}
 
 	error = load_addr;
-
-out_close:
-	kfree(elf_phdata);
 out:
 	return error;
 }
@@ -605,7 +598,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	int load_addr_set = 0;
 	char * elf_interpreter = NULL;
 	unsigned long error;
-	struct elf_phdr *elf_ppnt, *elf_phdata;
+	struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
 	unsigned long elf_bss, elf_brk;
 	int retval, i;
 	unsigned long elf_entry;
@@ -729,6 +722,12 @@ static int load_elf_binary(struct linux_binprm *bprm)
 		/* Verify the interpreter has a valid arch */
 		if (!elf_check_arch(&loc->interp_elf_ex))
 			goto out_free_dentry;
+
+		/* Load the interpreter program headers */
+		interp_elf_phdata = load_elf_phdrs(&loc->interp_elf_ex,
+						   interpreter);
+		if (!interp_elf_phdata)
+			goto out_free_dentry;
 	}
 
 	/* Flush all traces of the currently running executable */
@@ -912,7 +911,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 		elf_entry = load_elf_interp(&loc->interp_elf_ex,
 					    interpreter,
 					    &interp_map_addr,
-					    load_bias);
+					    load_bias, interp_elf_phdata);
 		if (!IS_ERR((void *)elf_entry)) {
 			/*
 			 * load_elf_interp() returns relocation
@@ -1009,6 +1008,7 @@ out_ret:
 
 	/* error cleanup */
 out_free_dentry:
+	kfree(interp_elf_phdata);
 	allow_write_access(interpreter);
 	if (interpreter)
 		fput(interpreter);
-- 
2.0.4

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 03/10] binfmt_elf: allow arch code to examine PT_LOPROC ... PT_HIPROC headers
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

MIPS is introducing new variants of its O32 ABI which differ in their
handling of floating point, in order to enable a gradual transition
towards a world where mips32 binaries can take advantage of new hardware
features only available when configured for certain FP modes. In order
to do this ELF binaries are being augmented with a new section that
indicates, amongst other things, the FP mode requirements of the binary.
The presence & location of such a section is indicated by a program
header in the PT_LOPROC ... PT_HIPROC range.

In order to allow the MIPS architecture code to examine the program
header & section in question, pass all program headers in this range
to an architecture-specific arch_elf_pt_proc function. This function
may return an error if the header is deemed invalid or unsuitable for
the system, in which case that error will be returned from
load_elf_binary and upwards through the execve syscall.

A means is required for the architecture code to make a decision once
it is known that all such headers have been seen, but before it is too
late to return from an execve syscall. For this purpose the
arch_check_elf function is added, and called once, after all PT_LOPROC
to PT_HIPROC headers have been passed to arch_elf_pt_proc but before
the code which invoked execve has been lost. This enables the
architecture code to make a decision based upon all the headers present
in an ELF binary and its interpreter, as is required to forbid
conflicting FP ABI requirements between an ELF & its interpreter.

In order to allow data to be stored throughout the calls to the above
functions, struct arch_elf_state is introduced.

Finally a variant of the SET_PERSONALITY macro is introduced which
accepts a pointer to the struct arch_elf_state, allowing it to act
based upon state observed from the architecture specific program
headers.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 fs/Kconfig.binfmt   |  3 +++
 fs/binfmt_elf.c     | 36 ++++++++++++++++++++++++--
 include/linux/elf.h | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 110 insertions(+), 2 deletions(-)

diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt
index 370b24c..c055d56 100644
--- a/fs/Kconfig.binfmt
+++ b/fs/Kconfig.binfmt
@@ -30,6 +30,9 @@ config COMPAT_BINFMT_ELF
 config ARCH_BINFMT_ELF_RANDOMIZE_PIE
 	bool
 
+config ARCH_BINFMT_ELF_STATE
+	bool
+
 config BINFMT_ELF_FDPIC
 	bool "Kernel support for FDPIC ELF binaries"
 	default y
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 61dabe0..ceabb0d 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -611,6 +611,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 		struct elfhdr elf_ex;
 		struct elfhdr interp_elf_ex;
 	} *loc;
+	struct arch_elf_state arch_state = INIT_ARCH_ELF_STATE;
 
 	loc = kmalloc(sizeof(*loc), GFP_KERNEL);
 	if (!loc) {
@@ -705,12 +706,21 @@ static int load_elf_binary(struct linux_binprm *bprm)
 
 	elf_ppnt = elf_phdata;
 	for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
-		if (elf_ppnt->p_type == PT_GNU_STACK) {
+		switch (elf_ppnt->p_type) {
+		case PT_GNU_STACK:
 			if (elf_ppnt->p_flags & PF_X)
 				executable_stack = EXSTACK_ENABLE_X;
 			else
 				executable_stack = EXSTACK_DISABLE_X;
 			break;
+
+		case PT_LOPROC ... PT_HIPROC:
+			retval = arch_elf_pt_proc(&loc->elf_ex, elf_ppnt,
+						  bprm->file, false,
+						  &arch_state);
+			if (retval)
+				goto out_free_dentry;
+			break;
 		}
 
 	/* Some simple consistency checks for the interpreter */
@@ -728,8 +738,30 @@ static int load_elf_binary(struct linux_binprm *bprm)
 						   interpreter);
 		if (!interp_elf_phdata)
 			goto out_free_dentry;
+
+		/* Pass PT_LOPROC..PT_HIPROC headers to arch code */
+		elf_ppnt = interp_elf_phdata;
+		for (i = 0; i < loc->interp_elf_ex.e_phnum; i++, elf_ppnt++)
+			switch (elf_ppnt->p_type) {
+			case PT_LOPROC ... PT_HIPROC:
+				retval = arch_elf_pt_proc(&loc->interp_elf_ex,
+							  elf_ppnt, interpreter,
+							  true, &arch_state);
+				if (retval)
+					goto out_free_dentry;
+				break;
+			}
 	}
 
+	/*
+	 * Allow arch code to reject the ELF at this point, whilst it's
+	 * still possible to return an error to the code that invoked
+	 * the exec syscall.
+	 */
+	retval = arch_check_elf(&loc->elf_ex, !!interpreter, &arch_state);
+	if (retval)
+		goto out_free_dentry;
+
 	/* Flush all traces of the currently running executable */
 	retval = flush_old_exec(bprm);
 	if (retval)
@@ -737,7 +769,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 
 	/* Do this immediately, since STACK_TOP as used in setup_arg_pages
 	   may depend on the personality.  */
-	SET_PERSONALITY(loc->elf_ex);
+	SET_PERSONALITY2(loc->elf_ex, &arch_state);
 	if (elf_read_implies_exec(loc->elf_ex, executable_stack))
 		current->personality |= READ_IMPLIES_EXEC;
 
diff --git a/include/linux/elf.h b/include/linux/elf.h
index 67a5fa7..6bd1504 100644
--- a/include/linux/elf.h
+++ b/include/linux/elf.h
@@ -15,6 +15,11 @@
 	set_personality(PER_LINUX | (current->personality & (~PER_MASK)))
 #endif
 
+#ifndef SET_PERSONALITY2
+#define SET_PERSONALITY2(ex, state) \
+	SET_PERSONALITY(ex)
+#endif
+
 #if ELF_CLASS == ELFCLASS32
 
 extern Elf32_Dyn _DYNAMIC [];
@@ -37,6 +42,74 @@ extern Elf64_Dyn _DYNAMIC [];
 
 #endif
 
+#ifndef CONFIG_ARCH_BINFMT_ELF_STATE
+
+/**
+ * struct arch_elf_state - arch-specific ELF loading state
+ *
+ * This structure is used to preserve architecture specific data during
+ * the loading of an ELF file, throughout the checking of architecture
+ * specific ELF headers & through to the point where the ELF load is
+ * known to be proceeding (ie. SET_PERSONALITY).
+ *
+ * This implementation is a dummy for architectures which require no
+ * specific state.
+ */
+struct arch_elf_state {
+};
+
+#define INIT_ARCH_ELF_STATE {}
+
+/**
+ * arch_elf_pt_proc() - check a PT_LOPROC..PT_HIPROC ELF program header
+ * @ehdr:	The main ELF header
+ * @phdr:	The program header to check
+ * @elf:	The open ELF file
+ * @is_interp:	True if the phdr is from the interpreter of the ELF being
+ *		loaded, else false.
+ * @state:	Architecture-specific state preserved throughout the process
+ *		of loading the ELF.
+ *
+ * Inspects the program header phdr to validate its correctness and/or
+ * suitability for the system. Called once per ELF program header in the
+ * range PT_LOPROC to PT_HIPROC, for both the ELF being loaded and its
+ * interpreter.
+ *
+ * Return: Zero to proceed with the ELF load, non-zero to fail the ELF load
+ *         with that return code.
+ */
+static inline int arch_elf_pt_proc(struct elfhdr *ehdr,
+				   struct elf_phdr *phdr,
+				   struct file *elf, bool is_interp,
+				   struct arch_elf_state *state)
+{
+	/* Dummy implementation, always proceed */
+	return 0;
+}
+
+/**
+ * arch_elf_pt_proc() - check a PT_LOPROC..PT_HIPROC ELF program header
+ * @ehdr:	The main ELF header
+ * @has_interp:	True if the ELF has an interpreter, else false.
+ * @state:	Architecture-specific state preserved throughout the process
+ *		of loading the ELF.
+ *
+ * Provides a final opportunity for architecture code to reject the loading
+ * of the ELF & cause an exec syscall to return an error. This is called after
+ * all program headers to be checked by arch_elf_pt_proc have been.
+ *
+ * Return: Zero to proceed with the ELF load, non-zero to fail the ELF load
+ *         with that return code.
+ */
+static inline int arch_check_elf(struct elfhdr *ehdr, bool has_interp,
+				 struct arch_elf_state *state)
+{
+	/* Dummy implementation, always proceed */
+	return 0;
+}
+
+#endif /* !CONFIG_ARCH_BINFMT_ELF_STATE */
+
 /* Optional callbacks to write extra ELF notes. */
 struct file;
 struct coredump_params;
-- 
2.0.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 03/10] binfmt_elf: allow arch code to examine PT_LOPROC ... PT_HIPROC headers
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

MIPS is introducing new variants of its O32 ABI which differ in their
handling of floating point, in order to enable a gradual transition
towards a world where mips32 binaries can take advantage of new hardware
features only available when configured for certain FP modes. In order
to do this ELF binaries are being augmented with a new section that
indicates, amongst other things, the FP mode requirements of the binary.
The presence & location of such a section is indicated by a program
header in the PT_LOPROC ... PT_HIPROC range.

In order to allow the MIPS architecture code to examine the program
header & section in question, pass all program headers in this range
to an architecture-specific arch_elf_pt_proc function. This function
may return an error if the header is deemed invalid or unsuitable for
the system, in which case that error will be returned from
load_elf_binary and upwards through the execve syscall.

A means is required for the architecture code to make a decision once
it is known that all such headers have been seen, but before it is too
late to return from an execve syscall. For this purpose the
arch_check_elf function is added, and called once, after all PT_LOPROC
to PT_HIPROC headers have been passed to arch_elf_pt_proc but before
the code which invoked execve has been lost. This enables the
architecture code to make a decision based upon all the headers present
in an ELF binary and its interpreter, as is required to forbid
conflicting FP ABI requirements between an ELF & its interpreter.

In order to allow data to be stored throughout the calls to the above
functions, struct arch_elf_state is introduced.

Finally a variant of the SET_PERSONALITY macro is introduced which
accepts a pointer to the struct arch_elf_state, allowing it to act
based upon state observed from the architecture specific program
headers.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 fs/Kconfig.binfmt   |  3 +++
 fs/binfmt_elf.c     | 36 ++++++++++++++++++++++++--
 include/linux/elf.h | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 110 insertions(+), 2 deletions(-)

diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt
index 370b24c..c055d56 100644
--- a/fs/Kconfig.binfmt
+++ b/fs/Kconfig.binfmt
@@ -30,6 +30,9 @@ config COMPAT_BINFMT_ELF
 config ARCH_BINFMT_ELF_RANDOMIZE_PIE
 	bool
 
+config ARCH_BINFMT_ELF_STATE
+	bool
+
 config BINFMT_ELF_FDPIC
 	bool "Kernel support for FDPIC ELF binaries"
 	default y
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 61dabe0..ceabb0d 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -611,6 +611,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 		struct elfhdr elf_ex;
 		struct elfhdr interp_elf_ex;
 	} *loc;
+	struct arch_elf_state arch_state = INIT_ARCH_ELF_STATE;
 
 	loc = kmalloc(sizeof(*loc), GFP_KERNEL);
 	if (!loc) {
@@ -705,12 +706,21 @@ static int load_elf_binary(struct linux_binprm *bprm)
 
 	elf_ppnt = elf_phdata;
 	for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
-		if (elf_ppnt->p_type == PT_GNU_STACK) {
+		switch (elf_ppnt->p_type) {
+		case PT_GNU_STACK:
 			if (elf_ppnt->p_flags & PF_X)
 				executable_stack = EXSTACK_ENABLE_X;
 			else
 				executable_stack = EXSTACK_DISABLE_X;
 			break;
+
+		case PT_LOPROC ... PT_HIPROC:
+			retval = arch_elf_pt_proc(&loc->elf_ex, elf_ppnt,
+						  bprm->file, false,
+						  &arch_state);
+			if (retval)
+				goto out_free_dentry;
+			break;
 		}
 
 	/* Some simple consistency checks for the interpreter */
@@ -728,8 +738,30 @@ static int load_elf_binary(struct linux_binprm *bprm)
 						   interpreter);
 		if (!interp_elf_phdata)
 			goto out_free_dentry;
+
+		/* Pass PT_LOPROC..PT_HIPROC headers to arch code */
+		elf_ppnt = interp_elf_phdata;
+		for (i = 0; i < loc->interp_elf_ex.e_phnum; i++, elf_ppnt++)
+			switch (elf_ppnt->p_type) {
+			case PT_LOPROC ... PT_HIPROC:
+				retval = arch_elf_pt_proc(&loc->interp_elf_ex,
+							  elf_ppnt, interpreter,
+							  true, &arch_state);
+				if (retval)
+					goto out_free_dentry;
+				break;
+			}
 	}
 
+	/*
+	 * Allow arch code to reject the ELF at this point, whilst it's
+	 * still possible to return an error to the code that invoked
+	 * the exec syscall.
+	 */
+	retval = arch_check_elf(&loc->elf_ex, !!interpreter, &arch_state);
+	if (retval)
+		goto out_free_dentry;
+
 	/* Flush all traces of the currently running executable */
 	retval = flush_old_exec(bprm);
 	if (retval)
@@ -737,7 +769,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 
 	/* Do this immediately, since STACK_TOP as used in setup_arg_pages
 	   may depend on the personality.  */
-	SET_PERSONALITY(loc->elf_ex);
+	SET_PERSONALITY2(loc->elf_ex, &arch_state);
 	if (elf_read_implies_exec(loc->elf_ex, executable_stack))
 		current->personality |= READ_IMPLIES_EXEC;
 
diff --git a/include/linux/elf.h b/include/linux/elf.h
index 67a5fa7..6bd1504 100644
--- a/include/linux/elf.h
+++ b/include/linux/elf.h
@@ -15,6 +15,11 @@
 	set_personality(PER_LINUX | (current->personality & (~PER_MASK)))
 #endif
 
+#ifndef SET_PERSONALITY2
+#define SET_PERSONALITY2(ex, state) \
+	SET_PERSONALITY(ex)
+#endif
+
 #if ELF_CLASS == ELFCLASS32
 
 extern Elf32_Dyn _DYNAMIC [];
@@ -37,6 +42,74 @@ extern Elf64_Dyn _DYNAMIC [];
 
 #endif
 
+#ifndef CONFIG_ARCH_BINFMT_ELF_STATE
+
+/**
+ * struct arch_elf_state - arch-specific ELF loading state
+ *
+ * This structure is used to preserve architecture specific data during
+ * the loading of an ELF file, throughout the checking of architecture
+ * specific ELF headers & through to the point where the ELF load is
+ * known to be proceeding (ie. SET_PERSONALITY).
+ *
+ * This implementation is a dummy for architectures which require no
+ * specific state.
+ */
+struct arch_elf_state {
+};
+
+#define INIT_ARCH_ELF_STATE {}
+
+/**
+ * arch_elf_pt_proc() - check a PT_LOPROC..PT_HIPROC ELF program header
+ * @ehdr:	The main ELF header
+ * @phdr:	The program header to check
+ * @elf:	The open ELF file
+ * @is_interp:	True if the phdr is from the interpreter of the ELF being
+ *		loaded, else false.
+ * @state:	Architecture-specific state preserved throughout the process
+ *		of loading the ELF.
+ *
+ * Inspects the program header phdr to validate its correctness and/or
+ * suitability for the system. Called once per ELF program header in the
+ * range PT_LOPROC to PT_HIPROC, for both the ELF being loaded and its
+ * interpreter.
+ *
+ * Return: Zero to proceed with the ELF load, non-zero to fail the ELF load
+ *         with that return code.
+ */
+static inline int arch_elf_pt_proc(struct elfhdr *ehdr,
+				   struct elf_phdr *phdr,
+				   struct file *elf, bool is_interp,
+				   struct arch_elf_state *state)
+{
+	/* Dummy implementation, always proceed */
+	return 0;
+}
+
+/**
+ * arch_elf_pt_proc() - check a PT_LOPROC..PT_HIPROC ELF program header
+ * @ehdr:	The main ELF header
+ * @has_interp:	True if the ELF has an interpreter, else false.
+ * @state:	Architecture-specific state preserved throughout the process
+ *		of loading the ELF.
+ *
+ * Provides a final opportunity for architecture code to reject the loading
+ * of the ELF & cause an exec syscall to return an error. This is called after
+ * all program headers to be checked by arch_elf_pt_proc have been.
+ *
+ * Return: Zero to proceed with the ELF load, non-zero to fail the ELF load
+ *         with that return code.
+ */
+static inline int arch_check_elf(struct elfhdr *ehdr, bool has_interp,
+				 struct arch_elf_state *state)
+{
+	/* Dummy implementation, always proceed */
+	return 0;
+}
+
+#endif /* !CONFIG_ARCH_BINFMT_ELF_STATE */
+
 /* Optional callbacks to write extra ELF notes. */
 struct file;
 struct coredump_params;
-- 
2.0.4

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 04/10] MIPS: define bits introduced for hybrid FPRs
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

Add definitions for the FRE & UFE bits in Config5, and the FREP bit in
FPIR. These bits are used to support a hybrid FPR scheme allowing a
mixture of FP32 & FP64 code to execute within a task.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/include/asm/mipsregs.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h
index cf3b580..5f4dde3 100644
--- a/arch/mips/include/asm/mipsregs.h
+++ b/arch/mips/include/asm/mipsregs.h
@@ -653,6 +653,8 @@
 #define MIPS_CONF5_NF		(_ULCAST_(1) << 0)
 #define MIPS_CONF5_UFR		(_ULCAST_(1) << 2)
 #define MIPS_CONF5_MRP		(_ULCAST_(1) << 3)
+#define MIPS_CONF5_FRE		(_ULCAST_(1) << 8)
+#define MIPS_CONF5_UFE		(_ULCAST_(1) << 9)
 #define MIPS_CONF5_MSAEN	(_ULCAST_(1) << 27)
 #define MIPS_CONF5_EVA		(_ULCAST_(1) << 28)
 #define MIPS_CONF5_CV		(_ULCAST_(1) << 29)
@@ -692,6 +694,7 @@
 #define MIPS_FPIR_W		(_ULCAST_(1) << 20)
 #define MIPS_FPIR_L		(_ULCAST_(1) << 21)
 #define MIPS_FPIR_F64		(_ULCAST_(1) << 22)
+#define MIPS_FPIR_FREP		(_ULCAST_(1) << 29)
 
 /*
  * Bits in the MIPS32 Memory Segmentation registers.
-- 
2.0.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 04/10] MIPS: define bits introduced for hybrid FPRs
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

Add definitions for the FRE & UFE bits in Config5, and the FREP bit in
FPIR. These bits are used to support a hybrid FPR scheme allowing a
mixture of FP32 & FP64 code to execute within a task.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/include/asm/mipsregs.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h
index cf3b580..5f4dde3 100644
--- a/arch/mips/include/asm/mipsregs.h
+++ b/arch/mips/include/asm/mipsregs.h
@@ -653,6 +653,8 @@
 #define MIPS_CONF5_NF		(_ULCAST_(1) << 0)
 #define MIPS_CONF5_UFR		(_ULCAST_(1) << 2)
 #define MIPS_CONF5_MRP		(_ULCAST_(1) << 3)
+#define MIPS_CONF5_FRE		(_ULCAST_(1) << 8)
+#define MIPS_CONF5_UFE		(_ULCAST_(1) << 9)
 #define MIPS_CONF5_MSAEN	(_ULCAST_(1) << 27)
 #define MIPS_CONF5_EVA		(_ULCAST_(1) << 28)
 #define MIPS_CONF5_CV		(_ULCAST_(1) << 29)
@@ -692,6 +694,7 @@
 #define MIPS_FPIR_W		(_ULCAST_(1) << 20)
 #define MIPS_FPIR_L		(_ULCAST_(1) << 21)
 #define MIPS_FPIR_F64		(_ULCAST_(1) << 22)
+#define MIPS_FPIR_FREP		(_ULCAST_(1) << 29)
 
 /*
  * Bits in the MIPS32 Memory Segmentation registers.
-- 
2.0.4

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 05/10] MIPS: detect presence of the FRE & UFR bits
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

Detect the presence of the Config5 FRE & UFE bits, as indicated by the
FREP bit in FPIR. Record this as a CPU option bit, and provide a
cpu_has_fre macro to ease checking of that option bit.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/include/asm/cpu-features.h | 4 ++++
 arch/mips/include/asm/cpu.h          | 1 +
 arch/mips/kernel/cpu-probe.c         | 2 ++
 3 files changed, 7 insertions(+)

diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h
index e079598..b9420d8 100644
--- a/arch/mips/include/asm/cpu-features.h
+++ b/arch/mips/include/asm/cpu-features.h
@@ -334,4 +334,8 @@
 # define cpu_has_msa		0
 #endif
 
+#ifndef cpu_has_fre
+# define cpu_has_fre		(cpu_data[0].options & MIPS_CPU_FRE)
+#endif
+
 #endif /* __ASM_CPU_FEATURES_H */
diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h
index dfdc77e..11025bf 100644
--- a/arch/mips/include/asm/cpu.h
+++ b/arch/mips/include/asm/cpu.h
@@ -368,6 +368,7 @@ enum cpu_type_enum {
 #define MIPS_CPU_HTW		0x100000000ull /* CPU support Hardware Page Table Walker */
 #define MIPS_CPU_RIXIEX		0x200000000ull /* CPU has unique exception codes for {Read, Execute}-Inhibit exceptions */
 #define MIPS_CPU_MAAR		0x400000000ull /* MAAR(I) registers are present */
+#define MIPS_CPU_FRE		0x800000000ull /* FRE & UFE bits implemented */
 
 /*
  * CPU ASE encodings
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index e34b10b..db929a0 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -1233,6 +1233,8 @@ void cpu_probe(void)
 				    MIPS_CPU_ISA_M64R1 | MIPS_CPU_ISA_M64R2)) {
 			if (c->fpu_id & MIPS_FPIR_3D)
 				c->ases |= MIPS_ASE_MIPS3D;
+			if (c->fpu_id & MIPS_FPIR_FREP)
+				c->options |= MIPS_CPU_FRE;
 		}
 	}
 
-- 
2.0.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 05/10] MIPS: detect presence of the FRE & UFR bits
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

Detect the presence of the Config5 FRE & UFE bits, as indicated by the
FREP bit in FPIR. Record this as a CPU option bit, and provide a
cpu_has_fre macro to ease checking of that option bit.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/include/asm/cpu-features.h | 4 ++++
 arch/mips/include/asm/cpu.h          | 1 +
 arch/mips/kernel/cpu-probe.c         | 2 ++
 3 files changed, 7 insertions(+)

diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h
index e079598..b9420d8 100644
--- a/arch/mips/include/asm/cpu-features.h
+++ b/arch/mips/include/asm/cpu-features.h
@@ -334,4 +334,8 @@
 # define cpu_has_msa		0
 #endif
 
+#ifndef cpu_has_fre
+# define cpu_has_fre		(cpu_data[0].options & MIPS_CPU_FRE)
+#endif
+
 #endif /* __ASM_CPU_FEATURES_H */
diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h
index dfdc77e..11025bf 100644
--- a/arch/mips/include/asm/cpu.h
+++ b/arch/mips/include/asm/cpu.h
@@ -368,6 +368,7 @@ enum cpu_type_enum {
 #define MIPS_CPU_HTW		0x100000000ull /* CPU support Hardware Page Table Walker */
 #define MIPS_CPU_RIXIEX		0x200000000ull /* CPU has unique exception codes for {Read, Execute}-Inhibit exceptions */
 #define MIPS_CPU_MAAR		0x400000000ull /* MAAR(I) registers are present */
+#define MIPS_CPU_FRE		0x800000000ull /* FRE & UFE bits implemented */
 
 /*
  * CPU ASE encodings
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index e34b10b..db929a0 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -1233,6 +1233,8 @@ void cpu_probe(void)
 				    MIPS_CPU_ISA_M64R1 | MIPS_CPU_ISA_M64R2)) {
 			if (c->fpu_id & MIPS_FPIR_3D)
 				c->ases |= MIPS_ASE_MIPS3D;
+			if (c->fpu_id & MIPS_FPIR_FREP)
+				c->options |= MIPS_CPU_FRE;
 		}
 	}
 
-- 
2.0.4

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 06/10] MIPS: ensure Config5.UFE is clear on boot
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

As is done for UFR, ensure that userland cannot directly manipulate the
mode by clearing the UFE bit during boot.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/kernel/cpu-probe.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index db929a0..4e2a6e0 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -400,7 +400,7 @@ static inline unsigned int decode_config5(struct cpuinfo_mips *c)
 	unsigned int config5;
 
 	config5 = read_c0_config5();
-	config5 &= ~MIPS_CONF5_UFR;
+	config5 &= ~(MIPS_CONF5_UFR | MIPS_CONF5_UFE);
 	write_c0_config5(config5);
 
 	if (config5 & MIPS_CONF5_EVA)
-- 
2.0.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 06/10] MIPS: ensure Config5.UFE is clear on boot
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

As is done for UFR, ensure that userland cannot directly manipulate the
mode by clearing the UFE bit during boot.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/kernel/cpu-probe.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index db929a0..4e2a6e0 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -400,7 +400,7 @@ static inline unsigned int decode_config5(struct cpuinfo_mips *c)
 	unsigned int config5;
 
 	config5 = read_c0_config5();
-	config5 &= ~MIPS_CONF5_UFR;
+	config5 &= ~(MIPS_CONF5_UFR | MIPS_CONF5_UFE);
 	write_c0_config5(config5);
 
 	if (config5 & MIPS_CONF5_EVA)
-- 
2.0.4

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 07/10] MIPS: support for hybrid FPRs
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

Hybrid FPRs is a scheme where scalar FP registers are 64b wide, but
accesses to odd indexed single registers use bits 63:32 of the
preceeding even indexed 64b register. In this mode all FP code
except that built for the plain FP64 ABI can execute correctly. Most
notably a combination of FP64A & FP32 code can execute correctly,
allowing for existing FP32 binaries to be linked with new FP64A binaries
that can make use of 64 bit FP & MSA.

Hybrid FPRs are implemented by setting both the FR & FRE bits, trapping
& emulating single precision FP instructions (via Reserved Instruction
exceptions) whilst allowing others to execute natively. It therefore has
a penalty in terms of execution speed, and should only be used when no
fully native mode can be. As more binaries are recompiled to use either
the FPXX or FP64(A) ABIs, the need for hybrid FPRs should diminish.
However in the short to mid term it allows for a gradual transition
towards that world, rather than a complete ABI break which is not
feasible for some users & not desirable for many.

A task will be executed using the hybrid FPR scheme when its
TIF_HYBRID_FPREGS flag is set & TIF_32BIT_FPREGS is clear. A further
patch will set the flags as necessary, this patch simply adds the
infrastructure necessary for the hybrid FPR mode to work.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/include/asm/elf.h         |  3 +++
 arch/mips/include/asm/fpu.h         | 49 +++++++++++++++++++++++++++++++------
 arch/mips/include/asm/thread_info.h |  2 ++
 arch/mips/kernel/traps.c            | 47 +++++++++++++++++++++++++++++++++++
 arch/mips/math-emu/cp1emu.c         |  9 +++++--
 5 files changed, 100 insertions(+), 10 deletions(-)

diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h
index 1d38fe0..9343529 100644
--- a/arch/mips/include/asm/elf.h
+++ b/arch/mips/include/asm/elf.h
@@ -269,6 +269,8 @@ do {									\
 	else								\
 		set_thread_flag(TIF_32BIT_FPREGS);			\
 									\
+	clear_thread_flag(TIF_HYBRID_FPREGS);				\
+									\
 	if (personality(current->personality) != PER_LINUX)		\
 		set_personality(PER_LINUX);				\
 									\
@@ -325,6 +327,7 @@ do {									\
 									\
 	clear_thread_flag(TIF_32BIT_REGS);				\
 	clear_thread_flag(TIF_32BIT_FPREGS);				\
+	clear_thread_flag(TIF_HYBRID_FPREGS);				\
 	clear_thread_flag(TIF_32BIT_ADDR);				\
 									\
 	if ((ex).e_ident[EI_CLASS] == ELFCLASS32)			\
diff --git a/arch/mips/include/asm/fpu.h b/arch/mips/include/asm/fpu.h
index 4d0aeda..6e60431 100644
--- a/arch/mips/include/asm/fpu.h
+++ b/arch/mips/include/asm/fpu.h
@@ -36,14 +36,16 @@ extern void _restore_fp(struct task_struct *);
 
 /*
  * This enum specifies a mode in which we want the FPU to operate, for cores
- * which implement the Status.FR bit. Note that FPU_32BIT & FPU_64BIT
- * purposefully have the values 0 & 1 respectively, so that an integer value
- * of Status.FR can be trivially casted to the corresponding enum fpu_mode.
+ * which implement the Status.FR bit. Note that the bottom bit of the value
+ * purposefully matches the desired value of the Status.FR bit.
  */
 enum fpu_mode {
 	FPU_32BIT = 0,		/* FR = 0 */
-	FPU_64BIT,		/* FR = 1 */
+	FPU_64BIT,		/* FR = 1, FRE = 0 */
 	FPU_AS_IS,
+	FPU_HYBRID,		/* FR = 1, FRE = 1 */
+
+#define FPU_FR_MASK		0x1
 };
 
 static inline int __enable_fpu(enum fpu_mode mode)
@@ -57,6 +59,14 @@ static inline int __enable_fpu(enum fpu_mode mode)
 		enable_fpu_hazard();
 		return 0;
 
+	case FPU_HYBRID:
+		if (!cpu_has_fre)
+			return SIGFPE;
+
+		/* set FRE */
+		write_c0_config5(read_c0_config5() | MIPS_CONF5_FRE);
+		goto fr_common;
+
 	case FPU_64BIT:
 #if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_64BIT))
 		/* we only have a 32-bit FPU */
@@ -64,8 +74,11 @@ static inline int __enable_fpu(enum fpu_mode mode)
 #endif
 		/* fall through */
 	case FPU_32BIT:
+		/* clear FRE */
+		write_c0_config5(read_c0_config5() & ~MIPS_CONF5_FRE);
+fr_common:
 		/* set CU1 & change FR appropriately */
-		fr = (int)mode;
+		fr = (int)mode & FPU_FR_MASK;
 		change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
 		enable_fpu_hazard();
 
@@ -102,13 +115,17 @@ static inline int __own_fpu(void)
 	enum fpu_mode mode;
 	int ret;
 
-	mode = !test_thread_flag(TIF_32BIT_FPREGS);
+	if (test_thread_flag(TIF_HYBRID_FPREGS))
+		mode = FPU_HYBRID;
+	else
+		mode = !test_thread_flag(TIF_32BIT_FPREGS);
+
 	ret = __enable_fpu(mode);
 	if (ret)
 		return ret;
 
 	KSTK_STATUS(current) |= ST0_CU1;
-	if (mode == FPU_64BIT)
+	if (mode == FPU_64BIT || mode == FPU_HYBRID)
 		KSTK_STATUS(current) |= ST0_FR;
 	else /* mode == FPU_32BIT */
 		KSTK_STATUS(current) &= ~ST0_FR;
@@ -166,8 +183,24 @@ static inline int init_fpu(void)
 
 	if (cpu_has_fpu) {
 		ret = __own_fpu();
-		if (!ret)
+		if (!ret) {
+			unsigned int config5 = read_c0_config5();
+
+			/*
+			 * Ensure FRE is clear whilst running _init_fpu, since
+			 * single precision FP instructions are used. If FRE
+			 * was set then we'll just end up initialising all 32
+			 * 64b registers.
+			 */
+			write_c0_config5(config5 & ~MIPS_CONF5_FRE);
+			enable_fpu_hazard();
+
 			_init_fpu();
+
+			/* Restore FRE */
+			write_c0_config5(config5);
+			enable_fpu_hazard();
+		}
 	} else
 		fpu_emulator_init_fpu();
 
diff --git a/arch/mips/include/asm/thread_info.h b/arch/mips/include/asm/thread_info.h
index 7de8658..99eea59 100644
--- a/arch/mips/include/asm/thread_info.h
+++ b/arch/mips/include/asm/thread_info.h
@@ -116,6 +116,7 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_LOAD_WATCH		25	/* If set, load watch registers */
 #define TIF_SYSCALL_TRACEPOINT	26	/* syscall tracepoint instrumentation */
 #define TIF_32BIT_FPREGS	27	/* 32-bit floating point registers */
+#define TIF_HYBRID_FPREGS	28	/* 64b FP registers, odd singles in bits 63:32 of even doubles */
 #define TIF_USEDMSA		29	/* MSA has been used this quantum */
 #define TIF_MSA_CTX_LIVE	30	/* MSA context must be preserved */
 #define TIF_SYSCALL_TRACE	31	/* syscall trace active */
@@ -135,6 +136,7 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_FPUBOUND		(1<<TIF_FPUBOUND)
 #define _TIF_LOAD_WATCH		(1<<TIF_LOAD_WATCH)
 #define _TIF_32BIT_FPREGS	(1<<TIF_32BIT_FPREGS)
+#define _TIF_HYBRID_FPREGS	(1<<TIF_HYBRID_FPREGS)
 #define _TIF_USEDMSA		(1<<TIF_USEDMSA)
 #define _TIF_MSA_CTX_LIVE	(1<<TIF_MSA_CTX_LIVE)
 #define _TIF_SYSCALL_TRACEPOINT	(1<<TIF_SYSCALL_TRACEPOINT)
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 22b19c2..165c275 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -724,6 +724,50 @@ int process_fpemu_return(int sig, void __user *fault_addr)
 	}
 }
 
+static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
+		       unsigned long old_epc, unsigned long old_ra)
+{
+	union mips_instruction inst = { .word = opcode };
+	void __user *fault_addr = NULL;
+	int sig;
+
+	/* If it's obviously not an FP instruction, skip it */
+	switch (inst.i_format.opcode) {
+	case cop1_op:
+	case cop1x_op:
+	case lwc1_op:
+	case ldc1_op:
+	case swc1_op:
+	case sdc1_op:
+		break;
+
+	default:
+		return -1;
+	}
+
+	/*
+	 * do_ri skipped over the instruction via compute_return_epc, undo
+	 * that for the FPU emulator.
+	 */
+	regs->cp0_epc = old_epc;
+	regs->regs[31] = old_ra;
+
+	/* Save the FP context to struct thread_struct */
+	lose_fpu(1);
+
+	/* Run the emulator */
+	sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
+				       &fault_addr);
+
+	/* If something went wrong, signal */
+	process_fpemu_return(sig, fault_addr);
+
+	/* Restore the hardware register state */
+	own_fpu(1);
+
+	return 0;
+}
+
 /*
  * XXX Delayed fp exceptions when doing a lazy ctx switch XXX
  */
@@ -1016,6 +1060,9 @@ asmlinkage void do_ri(struct pt_regs *regs)
 
 		if (status < 0)
 			status = simulate_sync(regs, opcode);
+
+		if (status < 0)
+			status = simulate_fp(regs, opcode, old_epc, old31);
 	}
 
 	if (status < 0)
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index bf0fc6b..ef8447f 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -647,9 +647,14 @@ static inline int cop1_64bit(struct pt_regs *xcp)
 	return !test_thread_flag(TIF_32BIT_FPREGS);
 }
 
+static inline bool hybrid_fprs(void)
+{
+	return test_thread_flag(TIF_HYBRID_FPREGS);
+}
+
 #define SIFROMREG(si, x)						\
 do {									\
-	if (cop1_64bit(xcp))						\
+	if (cop1_64bit(xcp) && !hybrid_fprs())				\
 		(si) = get_fpr32(&ctx->fpr[x], 0);			\
 	else								\
 		(si) = get_fpr32(&ctx->fpr[(x) & ~1], (x) & 1);		\
@@ -657,7 +662,7 @@ do {									\
 
 #define SITOREG(si, x)							\
 do {									\
-	if (cop1_64bit(xcp)) {						\
+	if (cop1_64bit(xcp) && !hybrid_fprs()) {			\
 		unsigned i;						\
 		set_fpr32(&ctx->fpr[x], 0, si);				\
 		for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val32); i++)	\
-- 
2.0.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 07/10] MIPS: support for hybrid FPRs
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

Hybrid FPRs is a scheme where scalar FP registers are 64b wide, but
accesses to odd indexed single registers use bits 63:32 of the
preceeding even indexed 64b register. In this mode all FP code
except that built for the plain FP64 ABI can execute correctly. Most
notably a combination of FP64A & FP32 code can execute correctly,
allowing for existing FP32 binaries to be linked with new FP64A binaries
that can make use of 64 bit FP & MSA.

Hybrid FPRs are implemented by setting both the FR & FRE bits, trapping
& emulating single precision FP instructions (via Reserved Instruction
exceptions) whilst allowing others to execute natively. It therefore has
a penalty in terms of execution speed, and should only be used when no
fully native mode can be. As more binaries are recompiled to use either
the FPXX or FP64(A) ABIs, the need for hybrid FPRs should diminish.
However in the short to mid term it allows for a gradual transition
towards that world, rather than a complete ABI break which is not
feasible for some users & not desirable for many.

A task will be executed using the hybrid FPR scheme when its
TIF_HYBRID_FPREGS flag is set & TIF_32BIT_FPREGS is clear. A further
patch will set the flags as necessary, this patch simply adds the
infrastructure necessary for the hybrid FPR mode to work.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/include/asm/elf.h         |  3 +++
 arch/mips/include/asm/fpu.h         | 49 +++++++++++++++++++++++++++++++------
 arch/mips/include/asm/thread_info.h |  2 ++
 arch/mips/kernel/traps.c            | 47 +++++++++++++++++++++++++++++++++++
 arch/mips/math-emu/cp1emu.c         |  9 +++++--
 5 files changed, 100 insertions(+), 10 deletions(-)

diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h
index 1d38fe0..9343529 100644
--- a/arch/mips/include/asm/elf.h
+++ b/arch/mips/include/asm/elf.h
@@ -269,6 +269,8 @@ do {									\
 	else								\
 		set_thread_flag(TIF_32BIT_FPREGS);			\
 									\
+	clear_thread_flag(TIF_HYBRID_FPREGS);				\
+									\
 	if (personality(current->personality) != PER_LINUX)		\
 		set_personality(PER_LINUX);				\
 									\
@@ -325,6 +327,7 @@ do {									\
 									\
 	clear_thread_flag(TIF_32BIT_REGS);				\
 	clear_thread_flag(TIF_32BIT_FPREGS);				\
+	clear_thread_flag(TIF_HYBRID_FPREGS);				\
 	clear_thread_flag(TIF_32BIT_ADDR);				\
 									\
 	if ((ex).e_ident[EI_CLASS] == ELFCLASS32)			\
diff --git a/arch/mips/include/asm/fpu.h b/arch/mips/include/asm/fpu.h
index 4d0aeda..6e60431 100644
--- a/arch/mips/include/asm/fpu.h
+++ b/arch/mips/include/asm/fpu.h
@@ -36,14 +36,16 @@ extern void _restore_fp(struct task_struct *);
 
 /*
  * This enum specifies a mode in which we want the FPU to operate, for cores
- * which implement the Status.FR bit. Note that FPU_32BIT & FPU_64BIT
- * purposefully have the values 0 & 1 respectively, so that an integer value
- * of Status.FR can be trivially casted to the corresponding enum fpu_mode.
+ * which implement the Status.FR bit. Note that the bottom bit of the value
+ * purposefully matches the desired value of the Status.FR bit.
  */
 enum fpu_mode {
 	FPU_32BIT = 0,		/* FR = 0 */
-	FPU_64BIT,		/* FR = 1 */
+	FPU_64BIT,		/* FR = 1, FRE = 0 */
 	FPU_AS_IS,
+	FPU_HYBRID,		/* FR = 1, FRE = 1 */
+
+#define FPU_FR_MASK		0x1
 };
 
 static inline int __enable_fpu(enum fpu_mode mode)
@@ -57,6 +59,14 @@ static inline int __enable_fpu(enum fpu_mode mode)
 		enable_fpu_hazard();
 		return 0;
 
+	case FPU_HYBRID:
+		if (!cpu_has_fre)
+			return SIGFPE;
+
+		/* set FRE */
+		write_c0_config5(read_c0_config5() | MIPS_CONF5_FRE);
+		goto fr_common;
+
 	case FPU_64BIT:
 #if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_64BIT))
 		/* we only have a 32-bit FPU */
@@ -64,8 +74,11 @@ static inline int __enable_fpu(enum fpu_mode mode)
 #endif
 		/* fall through */
 	case FPU_32BIT:
+		/* clear FRE */
+		write_c0_config5(read_c0_config5() & ~MIPS_CONF5_FRE);
+fr_common:
 		/* set CU1 & change FR appropriately */
-		fr = (int)mode;
+		fr = (int)mode & FPU_FR_MASK;
 		change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
 		enable_fpu_hazard();
 
@@ -102,13 +115,17 @@ static inline int __own_fpu(void)
 	enum fpu_mode mode;
 	int ret;
 
-	mode = !test_thread_flag(TIF_32BIT_FPREGS);
+	if (test_thread_flag(TIF_HYBRID_FPREGS))
+		mode = FPU_HYBRID;
+	else
+		mode = !test_thread_flag(TIF_32BIT_FPREGS);
+
 	ret = __enable_fpu(mode);
 	if (ret)
 		return ret;
 
 	KSTK_STATUS(current) |= ST0_CU1;
-	if (mode == FPU_64BIT)
+	if (mode == FPU_64BIT || mode == FPU_HYBRID)
 		KSTK_STATUS(current) |= ST0_FR;
 	else /* mode == FPU_32BIT */
 		KSTK_STATUS(current) &= ~ST0_FR;
@@ -166,8 +183,24 @@ static inline int init_fpu(void)
 
 	if (cpu_has_fpu) {
 		ret = __own_fpu();
-		if (!ret)
+		if (!ret) {
+			unsigned int config5 = read_c0_config5();
+
+			/*
+			 * Ensure FRE is clear whilst running _init_fpu, since
+			 * single precision FP instructions are used. If FRE
+			 * was set then we'll just end up initialising all 32
+			 * 64b registers.
+			 */
+			write_c0_config5(config5 & ~MIPS_CONF5_FRE);
+			enable_fpu_hazard();
+
 			_init_fpu();
+
+			/* Restore FRE */
+			write_c0_config5(config5);
+			enable_fpu_hazard();
+		}
 	} else
 		fpu_emulator_init_fpu();
 
diff --git a/arch/mips/include/asm/thread_info.h b/arch/mips/include/asm/thread_info.h
index 7de8658..99eea59 100644
--- a/arch/mips/include/asm/thread_info.h
+++ b/arch/mips/include/asm/thread_info.h
@@ -116,6 +116,7 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_LOAD_WATCH		25	/* If set, load watch registers */
 #define TIF_SYSCALL_TRACEPOINT	26	/* syscall tracepoint instrumentation */
 #define TIF_32BIT_FPREGS	27	/* 32-bit floating point registers */
+#define TIF_HYBRID_FPREGS	28	/* 64b FP registers, odd singles in bits 63:32 of even doubles */
 #define TIF_USEDMSA		29	/* MSA has been used this quantum */
 #define TIF_MSA_CTX_LIVE	30	/* MSA context must be preserved */
 #define TIF_SYSCALL_TRACE	31	/* syscall trace active */
@@ -135,6 +136,7 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_FPUBOUND		(1<<TIF_FPUBOUND)
 #define _TIF_LOAD_WATCH		(1<<TIF_LOAD_WATCH)
 #define _TIF_32BIT_FPREGS	(1<<TIF_32BIT_FPREGS)
+#define _TIF_HYBRID_FPREGS	(1<<TIF_HYBRID_FPREGS)
 #define _TIF_USEDMSA		(1<<TIF_USEDMSA)
 #define _TIF_MSA_CTX_LIVE	(1<<TIF_MSA_CTX_LIVE)
 #define _TIF_SYSCALL_TRACEPOINT	(1<<TIF_SYSCALL_TRACEPOINT)
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 22b19c2..165c275 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -724,6 +724,50 @@ int process_fpemu_return(int sig, void __user *fault_addr)
 	}
 }
 
+static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
+		       unsigned long old_epc, unsigned long old_ra)
+{
+	union mips_instruction inst = { .word = opcode };
+	void __user *fault_addr = NULL;
+	int sig;
+
+	/* If it's obviously not an FP instruction, skip it */
+	switch (inst.i_format.opcode) {
+	case cop1_op:
+	case cop1x_op:
+	case lwc1_op:
+	case ldc1_op:
+	case swc1_op:
+	case sdc1_op:
+		break;
+
+	default:
+		return -1;
+	}
+
+	/*
+	 * do_ri skipped over the instruction via compute_return_epc, undo
+	 * that for the FPU emulator.
+	 */
+	regs->cp0_epc = old_epc;
+	regs->regs[31] = old_ra;
+
+	/* Save the FP context to struct thread_struct */
+	lose_fpu(1);
+
+	/* Run the emulator */
+	sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
+				       &fault_addr);
+
+	/* If something went wrong, signal */
+	process_fpemu_return(sig, fault_addr);
+
+	/* Restore the hardware register state */
+	own_fpu(1);
+
+	return 0;
+}
+
 /*
  * XXX Delayed fp exceptions when doing a lazy ctx switch XXX
  */
@@ -1016,6 +1060,9 @@ asmlinkage void do_ri(struct pt_regs *regs)
 
 		if (status < 0)
 			status = simulate_sync(regs, opcode);
+
+		if (status < 0)
+			status = simulate_fp(regs, opcode, old_epc, old31);
 	}
 
 	if (status < 0)
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index bf0fc6b..ef8447f 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -647,9 +647,14 @@ static inline int cop1_64bit(struct pt_regs *xcp)
 	return !test_thread_flag(TIF_32BIT_FPREGS);
 }
 
+static inline bool hybrid_fprs(void)
+{
+	return test_thread_flag(TIF_HYBRID_FPREGS);
+}
+
 #define SIFROMREG(si, x)						\
 do {									\
-	if (cop1_64bit(xcp))						\
+	if (cop1_64bit(xcp) && !hybrid_fprs())				\
 		(si) = get_fpr32(&ctx->fpr[x], 0);			\
 	else								\
 		(si) = get_fpr32(&ctx->fpr[(x) & ~1], (x) & 1);		\
@@ -657,7 +662,7 @@ do {									\
 
 #define SITOREG(si, x)							\
 do {									\
-	if (cop1_64bit(xcp)) {						\
+	if (cop1_64bit(xcp) && !hybrid_fprs()) {			\
 		unsigned i;						\
 		set_fpr32(&ctx->fpr[x], 0, si);				\
 		for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val32); i++)	\
-- 
2.0.4

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 08/10] MIPS: ELF: add definition for the .MIPS.abiflags section
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

New toolchains will generate a .MIPS.abiflags section, referenced by a
new PT_MIPS_ABIFLAGS program header. This section will provide
information about the requirements of the ELF, including the ISA level
the code is built for, the ASEs it requires, the size of various
registers and its expectations of the floating point mode. This patch
introduces a definition of the structure of this section and the program
header, for use in a subsequent patch.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/include/asm/elf.h | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h
index 9343529..bfa4bbd 100644
--- a/arch/mips/include/asm/elf.h
+++ b/arch/mips/include/asm/elf.h
@@ -28,6 +28,7 @@
 #define PT_MIPS_REGINFO		0x70000000
 #define PT_MIPS_RTPROC		0x70000001
 #define PT_MIPS_OPTIONS		0x70000002
+#define PT_MIPS_ABIFLAGS	0x70000003
 
 /* Flags in the e_flags field of the header */
 #define EF_MIPS_NOREORDER	0x00000001
@@ -174,6 +175,30 @@ typedef elf_greg_t elf_gregset_t[ELF_NGREG];
 typedef double elf_fpreg_t;
 typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
 
+struct mips_elf_abiflags_v0 {
+	uint16_t version;	/* Version of flags structure */
+	uint8_t isa_level;	/* The level of the ISA: 1-5, 32, 64 */
+	uint8_t isa_rev;	/* The revision of ISA: 0 for MIPS V and below,
+				   1-n otherwise */
+	uint8_t gpr_size;	/* The size of general purpose registers */
+	uint8_t cpr1_size;	/* The size of co-processor 1 registers */
+	uint8_t cpr2_size;	/* The size of co-processor 2 registers */
+	uint8_t fp_abi;		/* The floating-point ABI */
+	uint32_t isa_ext;	/* Mask of processor-specific extensions */
+	uint32_t ases;		/* Mask of ASEs used */
+	uint32_t flags1;	/* Mask of general flags */
+	uint32_t flags2;
+};
+
+#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 */
+
 #ifdef CONFIG_32BIT
 
 /*
-- 
2.0.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 08/10] MIPS: ELF: add definition for the .MIPS.abiflags section
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

New toolchains will generate a .MIPS.abiflags section, referenced by a
new PT_MIPS_ABIFLAGS program header. This section will provide
information about the requirements of the ELF, including the ISA level
the code is built for, the ASEs it requires, the size of various
registers and its expectations of the floating point mode. This patch
introduces a definition of the structure of this section and the program
header, for use in a subsequent patch.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/include/asm/elf.h | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h
index 9343529..bfa4bbd 100644
--- a/arch/mips/include/asm/elf.h
+++ b/arch/mips/include/asm/elf.h
@@ -28,6 +28,7 @@
 #define PT_MIPS_REGINFO		0x70000000
 #define PT_MIPS_RTPROC		0x70000001
 #define PT_MIPS_OPTIONS		0x70000002
+#define PT_MIPS_ABIFLAGS	0x70000003
 
 /* Flags in the e_flags field of the header */
 #define EF_MIPS_NOREORDER	0x00000001
@@ -174,6 +175,30 @@ typedef elf_greg_t elf_gregset_t[ELF_NGREG];
 typedef double elf_fpreg_t;
 typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
 
+struct mips_elf_abiflags_v0 {
+	uint16_t version;	/* Version of flags structure */
+	uint8_t isa_level;	/* The level of the ISA: 1-5, 32, 64 */
+	uint8_t isa_rev;	/* The revision of ISA: 0 for MIPS V and below,
+				   1-n otherwise */
+	uint8_t gpr_size;	/* The size of general purpose registers */
+	uint8_t cpr1_size;	/* The size of co-processor 1 registers */
+	uint8_t cpr2_size;	/* The size of co-processor 2 registers */
+	uint8_t fp_abi;		/* The floating-point ABI */
+	uint32_t isa_ext;	/* Mask of processor-specific extensions */
+	uint32_t ases;		/* Mask of ASEs used */
+	uint32_t flags1;	/* Mask of general flags */
+	uint32_t flags2;
+};
+
+#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 */
+
 #ifdef CONFIG_32BIT
 
 /*
-- 
2.0.4

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 09/10] MIPS: ELF: set FP mode according to .MIPS.abiflags
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

This patch reads the .MIPS.abiflags section when it is present, and sets
the FP mode of the task accordingly. Any loaded ELF files which do not
contain a .MIPS.abiflags section will continue to observe the previous
behaviour, that is FR=1 if EF_MIPS_FP64 is set else FR=0.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/Kconfig           |   1 +
 arch/mips/include/asm/elf.h |  50 ++++++++-----
 arch/mips/kernel/Makefile   |   7 +-
 arch/mips/kernel/elf.c      | 173 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 211 insertions(+), 20 deletions(-)
 create mode 100644 arch/mips/kernel/elf.c

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 900c7e5..8d73ed0 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -52,6 +52,7 @@ config MIPS
 	select HAVE_CC_STACKPROTECTOR
 	select CPU_PM if CPU_IDLE
 	select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
+	select ARCH_BINFMT_ELF_STATE
 
 menu "Machine selection"
 
diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h
index bfa4bbd..eb4d95d 100644
--- a/arch/mips/include/asm/elf.h
+++ b/arch/mips/include/asm/elf.h
@@ -8,6 +8,8 @@
 #ifndef _ASM_ELF_H
 #define _ASM_ELF_H
 
+#include <linux/fs.h>
+#include <uapi/linux/elf.h>
 
 /* ELF header e_flags defines. */
 /* MIPS architecture level. */
@@ -287,18 +289,13 @@ extern struct mips_abi mips_abi_n32;
 
 #ifdef CONFIG_32BIT
 
-#define SET_PERSONALITY(ex)						\
+#define SET_PERSONALITY2(ex, state)					\
 do {									\
-	if ((ex).e_flags & EF_MIPS_FP64)				\
-		clear_thread_flag(TIF_32BIT_FPREGS);			\
-	else								\
-		set_thread_flag(TIF_32BIT_FPREGS);			\
-									\
-	clear_thread_flag(TIF_HYBRID_FPREGS);				\
-									\
 	if (personality(current->personality) != PER_LINUX)		\
 		set_personality(PER_LINUX);				\
 									\
+	mips_set_personality_fp(state);					\
+									\
 	current->thread.abi = &mips_abi;				\
 } while (0)
 
@@ -318,35 +315,34 @@ do {									\
 #endif
 
 #ifdef CONFIG_MIPS32_O32
-#define __SET_PERSONALITY32_O32(ex)					\
+#define __SET_PERSONALITY32_O32(ex, state)				\
 	do {								\
 		set_thread_flag(TIF_32BIT_REGS);			\
 		set_thread_flag(TIF_32BIT_ADDR);			\
 									\
-		if (!((ex).e_flags & EF_MIPS_FP64))			\
-			set_thread_flag(TIF_32BIT_FPREGS);		\
+		mips_set_personality_fp(state);				\
 									\
 		current->thread.abi = &mips_abi_32;			\
 	} while (0)
 #else
-#define __SET_PERSONALITY32_O32(ex)					\
+#define __SET_PERSONALITY32_O32(ex, state)				\
 	do { } while (0)
 #endif
 
 #ifdef CONFIG_MIPS32_COMPAT
-#define __SET_PERSONALITY32(ex)						\
+#define __SET_PERSONALITY32(ex, state)					\
 do {									\
 	if ((((ex).e_flags & EF_MIPS_ABI2) != 0) &&			\
 	     ((ex).e_flags & EF_MIPS_ABI) == 0)				\
 		__SET_PERSONALITY32_N32();				\
 	else								\
-		__SET_PERSONALITY32_O32(ex);                            \
+		__SET_PERSONALITY32_O32(ex, state);			\
 } while (0)
 #else
-#define __SET_PERSONALITY32(ex) do { } while (0)
+#define __SET_PERSONALITY32(ex, state) do { } while (0)
 #endif
 
-#define SET_PERSONALITY(ex)						\
+#define SET_PERSONALITY2(ex, state)					\
 do {									\
 	unsigned int p;							\
 									\
@@ -356,7 +352,7 @@ do {									\
 	clear_thread_flag(TIF_32BIT_ADDR);				\
 									\
 	if ((ex).e_ident[EI_CLASS] == ELFCLASS32)			\
-		__SET_PERSONALITY32(ex);				\
+		__SET_PERSONALITY32(ex, state);				\
 	else								\
 		current->thread.abi = &mips_abi;			\
 									\
@@ -418,4 +414,24 @@ struct mm_struct;
 extern unsigned long arch_randomize_brk(struct mm_struct *mm);
 #define arch_randomize_brk arch_randomize_brk
 
+struct arch_elf_state {
+	int fp_abi;
+	int interp_fp_abi;
+	int overall_abi;
+};
+
+#define INIT_ARCH_ELF_STATE {			\
+	.fp_abi = -1,				\
+	.interp_fp_abi = -1,			\
+	.overall_abi = -1,			\
+}
+
+extern int arch_elf_pt_proc(void *ehdr, void *phdr, struct file *elf,
+			    bool is_interp, struct arch_elf_state *state);
+
+extern int arch_check_elf(void *ehdr, bool has_interpreter,
+			  struct arch_elf_state *state);
+
+extern void mips_set_personality_fp(struct arch_elf_state *state);
+
 #endif /* _ASM_ELF_H */
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 008a2fe..53a78d9 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -4,9 +4,10 @@
 
 extra-y		:= head.o vmlinux.lds
 
-obj-y		+= cpu-probe.o branch.o entry.o genex.o idle.o irq.o process.o \
-		   prom.o ptrace.o reset.o setup.o signal.o syscall.o \
-		   time.o topology.o traps.o unaligned.o watch.o vdso.o
+obj-y		+= cpu-probe.o branch.o elf.o entry.o genex.o idle.o irq.o \
+		   process.o prom.o ptrace.o reset.o setup.o signal.o \
+		   syscall.o time.o topology.o traps.o unaligned.o watch.o \
+		   vdso.o
 
 ifdef CONFIG_FUNCTION_TRACER
 CFLAGS_REMOVE_ftrace.o = -pg
diff --git a/arch/mips/kernel/elf.c b/arch/mips/kernel/elf.c
new file mode 100644
index 0000000..0933e07
--- /dev/null
+++ b/arch/mips/kernel/elf.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2014 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.com>
+ *
+ * This program 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 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/elf.h>
+#include <linux/sched.h>
+
+enum {
+	FP_ERROR = -1,
+	FP_DOUBLE_64A = -2,
+};
+
+int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf,
+		     bool is_interp, struct arch_elf_state *state)
+{
+	struct elfhdr *ehdr = _ehdr;
+	struct elf_phdr *phdr = _phdr;
+	struct mips_elf_abiflags_v0 abiflags;
+	int ret;
+
+	if (config_enabled(CONFIG_64BIT) &&
+	    (ehdr->e_ident[EI_CLASS] != ELFCLASS32))
+		return 0;
+	if (phdr->p_type != PT_MIPS_ABIFLAGS)
+		return 0;
+	if (phdr->p_filesz < sizeof(abiflags))
+		return -EINVAL;
+
+	ret = kernel_read(elf, phdr->p_offset, (char *)&abiflags,
+			  sizeof(abiflags));
+	if (ret < 0)
+		return ret;
+	if (ret != sizeof(abiflags))
+		return -EIO;
+
+	/* Record the required FP ABIs for use by mips_check_elf */
+	if (is_interp)
+		state->interp_fp_abi = abiflags.fp_abi;
+	else
+		state->fp_abi = abiflags.fp_abi;
+
+	return 0;
+}
+
+static inline unsigned get_fp_abi(struct elfhdr *ehdr, int in_abi)
+{
+	/* If the ABI requirement is provided, simply return that */
+	if (in_abi != -1)
+		return in_abi;
+
+	/* If the EF_MIPS_FP64 flag was set, return MIPS_ABI_FP_64 */
+	if (ehdr->e_flags & EF_MIPS_FP64)
+		return MIPS_ABI_FP_64;
+
+	/* Default to MIPS_ABI_FP_DOUBLE */
+	return MIPS_ABI_FP_DOUBLE;
+}
+
+int arch_check_elf(void *_ehdr, bool has_interpreter,
+		   struct arch_elf_state *state)
+{
+	struct elfhdr *ehdr = _ehdr;
+	unsigned fp_abi, interp_fp_abi, abi0, abi1;
+
+	/* Ignore non-O32 binaries */
+	if (config_enabled(CONFIG_64BIT) &&
+	    (ehdr->e_ident[EI_CLASS] != ELFCLASS32))
+		return 0;
+
+	fp_abi = get_fp_abi(ehdr, state->fp_abi);
+
+	if (has_interpreter) {
+		interp_fp_abi = get_fp_abi(ehdr, state->interp_fp_abi);
+
+		abi0 = min(fp_abi, interp_fp_abi);
+		abi1 = max(fp_abi, interp_fp_abi);
+	} else {
+		abi0 = abi1 = fp_abi;
+	}
+
+	state->overall_abi = FP_ERROR;
+
+	if (abi0 == abi1) {
+		state->overall_abi = abi0;
+	} else if (abi0 == MIPS_ABI_FP_ANY) {
+		state->overall_abi = abi1;
+	} else if (abi0 == MIPS_ABI_FP_DOUBLE) {
+		switch (abi1) {
+		case MIPS_ABI_FP_XX:
+			state->overall_abi = MIPS_ABI_FP_DOUBLE;
+			break;
+
+		case MIPS_ABI_FP_64A:
+			state->overall_abi = FP_DOUBLE_64A;
+			break;
+		}
+	} else if (abi0 == MIPS_ABI_FP_SINGLE ||
+		   abi0 == MIPS_ABI_FP_SOFT) {
+		/* Cannot link with other ABIs */
+	} else if (abi0 == MIPS_ABI_FP_OLD_64) {
+		switch (abi1) {
+		case MIPS_ABI_FP_XX:
+		case MIPS_ABI_FP_64:
+		case MIPS_ABI_FP_64A:
+			state->overall_abi = MIPS_ABI_FP_64;
+			break;
+		}
+	} else if (abi0 == MIPS_ABI_FP_XX ||
+		   abi0 == MIPS_ABI_FP_64 ||
+		   abi0 == MIPS_ABI_FP_64A) {
+		state->overall_abi = MIPS_ABI_FP_64;
+	}
+
+	switch (state->overall_abi) {
+	case MIPS_ABI_FP_64:
+	case MIPS_ABI_FP_64A:
+	case FP_DOUBLE_64A:
+		if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT))
+			return -ELIBBAD;
+		break;
+
+	case FP_ERROR:
+		return -ELIBBAD;
+	}
+
+	return 0;
+}
+
+void mips_set_personality_fp(struct arch_elf_state *state)
+{
+	switch (state->overall_abi) {
+	case MIPS_ABI_FP_DOUBLE:
+	case MIPS_ABI_FP_SINGLE:
+	case MIPS_ABI_FP_SOFT:
+		/* FR=0 */
+		set_thread_flag(TIF_32BIT_FPREGS);
+		clear_thread_flag(TIF_HYBRID_FPREGS);
+		break;
+
+	case FP_DOUBLE_64A:
+		/* FR=1, FRE=1 */
+		clear_thread_flag(TIF_32BIT_FPREGS);
+		set_thread_flag(TIF_HYBRID_FPREGS);
+		break;
+
+	case MIPS_ABI_FP_64:
+	case MIPS_ABI_FP_64A:
+		/* FR=1, FRE=0 */
+		clear_thread_flag(TIF_32BIT_FPREGS);
+		clear_thread_flag(TIF_HYBRID_FPREGS);
+		break;
+
+	case MIPS_ABI_FP_XX:
+	case MIPS_ABI_FP_ANY:
+		if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT))
+			set_thread_flag(TIF_32BIT_FPREGS);
+		else
+			clear_thread_flag(TIF_32BIT_FPREGS);
+
+		clear_thread_flag(TIF_HYBRID_FPREGS);
+		break;
+
+	default:
+	case FP_ERROR:
+		BUG();
+	}
+}
-- 
2.0.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 09/10] MIPS: ELF: set FP mode according to .MIPS.abiflags
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

This patch reads the .MIPS.abiflags section when it is present, and sets
the FP mode of the task accordingly. Any loaded ELF files which do not
contain a .MIPS.abiflags section will continue to observe the previous
behaviour, that is FR=1 if EF_MIPS_FP64 is set else FR=0.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/Kconfig           |   1 +
 arch/mips/include/asm/elf.h |  50 ++++++++-----
 arch/mips/kernel/Makefile   |   7 +-
 arch/mips/kernel/elf.c      | 173 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 211 insertions(+), 20 deletions(-)
 create mode 100644 arch/mips/kernel/elf.c

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 900c7e5..8d73ed0 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -52,6 +52,7 @@ config MIPS
 	select HAVE_CC_STACKPROTECTOR
 	select CPU_PM if CPU_IDLE
 	select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
+	select ARCH_BINFMT_ELF_STATE
 
 menu "Machine selection"
 
diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h
index bfa4bbd..eb4d95d 100644
--- a/arch/mips/include/asm/elf.h
+++ b/arch/mips/include/asm/elf.h
@@ -8,6 +8,8 @@
 #ifndef _ASM_ELF_H
 #define _ASM_ELF_H
 
+#include <linux/fs.h>
+#include <uapi/linux/elf.h>
 
 /* ELF header e_flags defines. */
 /* MIPS architecture level. */
@@ -287,18 +289,13 @@ extern struct mips_abi mips_abi_n32;
 
 #ifdef CONFIG_32BIT
 
-#define SET_PERSONALITY(ex)						\
+#define SET_PERSONALITY2(ex, state)					\
 do {									\
-	if ((ex).e_flags & EF_MIPS_FP64)				\
-		clear_thread_flag(TIF_32BIT_FPREGS);			\
-	else								\
-		set_thread_flag(TIF_32BIT_FPREGS);			\
-									\
-	clear_thread_flag(TIF_HYBRID_FPREGS);				\
-									\
 	if (personality(current->personality) != PER_LINUX)		\
 		set_personality(PER_LINUX);				\
 									\
+	mips_set_personality_fp(state);					\
+									\
 	current->thread.abi = &mips_abi;				\
 } while (0)
 
@@ -318,35 +315,34 @@ do {									\
 #endif
 
 #ifdef CONFIG_MIPS32_O32
-#define __SET_PERSONALITY32_O32(ex)					\
+#define __SET_PERSONALITY32_O32(ex, state)				\
 	do {								\
 		set_thread_flag(TIF_32BIT_REGS);			\
 		set_thread_flag(TIF_32BIT_ADDR);			\
 									\
-		if (!((ex).e_flags & EF_MIPS_FP64))			\
-			set_thread_flag(TIF_32BIT_FPREGS);		\
+		mips_set_personality_fp(state);				\
 									\
 		current->thread.abi = &mips_abi_32;			\
 	} while (0)
 #else
-#define __SET_PERSONALITY32_O32(ex)					\
+#define __SET_PERSONALITY32_O32(ex, state)				\
 	do { } while (0)
 #endif
 
 #ifdef CONFIG_MIPS32_COMPAT
-#define __SET_PERSONALITY32(ex)						\
+#define __SET_PERSONALITY32(ex, state)					\
 do {									\
 	if ((((ex).e_flags & EF_MIPS_ABI2) != 0) &&			\
 	     ((ex).e_flags & EF_MIPS_ABI) == 0)				\
 		__SET_PERSONALITY32_N32();				\
 	else								\
-		__SET_PERSONALITY32_O32(ex);                            \
+		__SET_PERSONALITY32_O32(ex, state);			\
 } while (0)
 #else
-#define __SET_PERSONALITY32(ex) do { } while (0)
+#define __SET_PERSONALITY32(ex, state) do { } while (0)
 #endif
 
-#define SET_PERSONALITY(ex)						\
+#define SET_PERSONALITY2(ex, state)					\
 do {									\
 	unsigned int p;							\
 									\
@@ -356,7 +352,7 @@ do {									\
 	clear_thread_flag(TIF_32BIT_ADDR);				\
 									\
 	if ((ex).e_ident[EI_CLASS] == ELFCLASS32)			\
-		__SET_PERSONALITY32(ex);				\
+		__SET_PERSONALITY32(ex, state);				\
 	else								\
 		current->thread.abi = &mips_abi;			\
 									\
@@ -418,4 +414,24 @@ struct mm_struct;
 extern unsigned long arch_randomize_brk(struct mm_struct *mm);
 #define arch_randomize_brk arch_randomize_brk
 
+struct arch_elf_state {
+	int fp_abi;
+	int interp_fp_abi;
+	int overall_abi;
+};
+
+#define INIT_ARCH_ELF_STATE {			\
+	.fp_abi = -1,				\
+	.interp_fp_abi = -1,			\
+	.overall_abi = -1,			\
+}
+
+extern int arch_elf_pt_proc(void *ehdr, void *phdr, struct file *elf,
+			    bool is_interp, struct arch_elf_state *state);
+
+extern int arch_check_elf(void *ehdr, bool has_interpreter,
+			  struct arch_elf_state *state);
+
+extern void mips_set_personality_fp(struct arch_elf_state *state);
+
 #endif /* _ASM_ELF_H */
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 008a2fe..53a78d9 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -4,9 +4,10 @@
 
 extra-y		:= head.o vmlinux.lds
 
-obj-y		+= cpu-probe.o branch.o entry.o genex.o idle.o irq.o process.o \
-		   prom.o ptrace.o reset.o setup.o signal.o syscall.o \
-		   time.o topology.o traps.o unaligned.o watch.o vdso.o
+obj-y		+= cpu-probe.o branch.o elf.o entry.o genex.o idle.o irq.o \
+		   process.o prom.o ptrace.o reset.o setup.o signal.o \
+		   syscall.o time.o topology.o traps.o unaligned.o watch.o \
+		   vdso.o
 
 ifdef CONFIG_FUNCTION_TRACER
 CFLAGS_REMOVE_ftrace.o = -pg
diff --git a/arch/mips/kernel/elf.c b/arch/mips/kernel/elf.c
new file mode 100644
index 0000000..0933e07
--- /dev/null
+++ b/arch/mips/kernel/elf.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2014 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.com>
+ *
+ * This program 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 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/elf.h>
+#include <linux/sched.h>
+
+enum {
+	FP_ERROR = -1,
+	FP_DOUBLE_64A = -2,
+};
+
+int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf,
+		     bool is_interp, struct arch_elf_state *state)
+{
+	struct elfhdr *ehdr = _ehdr;
+	struct elf_phdr *phdr = _phdr;
+	struct mips_elf_abiflags_v0 abiflags;
+	int ret;
+
+	if (config_enabled(CONFIG_64BIT) &&
+	    (ehdr->e_ident[EI_CLASS] != ELFCLASS32))
+		return 0;
+	if (phdr->p_type != PT_MIPS_ABIFLAGS)
+		return 0;
+	if (phdr->p_filesz < sizeof(abiflags))
+		return -EINVAL;
+
+	ret = kernel_read(elf, phdr->p_offset, (char *)&abiflags,
+			  sizeof(abiflags));
+	if (ret < 0)
+		return ret;
+	if (ret != sizeof(abiflags))
+		return -EIO;
+
+	/* Record the required FP ABIs for use by mips_check_elf */
+	if (is_interp)
+		state->interp_fp_abi = abiflags.fp_abi;
+	else
+		state->fp_abi = abiflags.fp_abi;
+
+	return 0;
+}
+
+static inline unsigned get_fp_abi(struct elfhdr *ehdr, int in_abi)
+{
+	/* If the ABI requirement is provided, simply return that */
+	if (in_abi != -1)
+		return in_abi;
+
+	/* If the EF_MIPS_FP64 flag was set, return MIPS_ABI_FP_64 */
+	if (ehdr->e_flags & EF_MIPS_FP64)
+		return MIPS_ABI_FP_64;
+
+	/* Default to MIPS_ABI_FP_DOUBLE */
+	return MIPS_ABI_FP_DOUBLE;
+}
+
+int arch_check_elf(void *_ehdr, bool has_interpreter,
+		   struct arch_elf_state *state)
+{
+	struct elfhdr *ehdr = _ehdr;
+	unsigned fp_abi, interp_fp_abi, abi0, abi1;
+
+	/* Ignore non-O32 binaries */
+	if (config_enabled(CONFIG_64BIT) &&
+	    (ehdr->e_ident[EI_CLASS] != ELFCLASS32))
+		return 0;
+
+	fp_abi = get_fp_abi(ehdr, state->fp_abi);
+
+	if (has_interpreter) {
+		interp_fp_abi = get_fp_abi(ehdr, state->interp_fp_abi);
+
+		abi0 = min(fp_abi, interp_fp_abi);
+		abi1 = max(fp_abi, interp_fp_abi);
+	} else {
+		abi0 = abi1 = fp_abi;
+	}
+
+	state->overall_abi = FP_ERROR;
+
+	if (abi0 == abi1) {
+		state->overall_abi = abi0;
+	} else if (abi0 == MIPS_ABI_FP_ANY) {
+		state->overall_abi = abi1;
+	} else if (abi0 == MIPS_ABI_FP_DOUBLE) {
+		switch (abi1) {
+		case MIPS_ABI_FP_XX:
+			state->overall_abi = MIPS_ABI_FP_DOUBLE;
+			break;
+
+		case MIPS_ABI_FP_64A:
+			state->overall_abi = FP_DOUBLE_64A;
+			break;
+		}
+	} else if (abi0 == MIPS_ABI_FP_SINGLE ||
+		   abi0 == MIPS_ABI_FP_SOFT) {
+		/* Cannot link with other ABIs */
+	} else if (abi0 == MIPS_ABI_FP_OLD_64) {
+		switch (abi1) {
+		case MIPS_ABI_FP_XX:
+		case MIPS_ABI_FP_64:
+		case MIPS_ABI_FP_64A:
+			state->overall_abi = MIPS_ABI_FP_64;
+			break;
+		}
+	} else if (abi0 == MIPS_ABI_FP_XX ||
+		   abi0 == MIPS_ABI_FP_64 ||
+		   abi0 == MIPS_ABI_FP_64A) {
+		state->overall_abi = MIPS_ABI_FP_64;
+	}
+
+	switch (state->overall_abi) {
+	case MIPS_ABI_FP_64:
+	case MIPS_ABI_FP_64A:
+	case FP_DOUBLE_64A:
+		if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT))
+			return -ELIBBAD;
+		break;
+
+	case FP_ERROR:
+		return -ELIBBAD;
+	}
+
+	return 0;
+}
+
+void mips_set_personality_fp(struct arch_elf_state *state)
+{
+	switch (state->overall_abi) {
+	case MIPS_ABI_FP_DOUBLE:
+	case MIPS_ABI_FP_SINGLE:
+	case MIPS_ABI_FP_SOFT:
+		/* FR=0 */
+		set_thread_flag(TIF_32BIT_FPREGS);
+		clear_thread_flag(TIF_HYBRID_FPREGS);
+		break;
+
+	case FP_DOUBLE_64A:
+		/* FR=1, FRE=1 */
+		clear_thread_flag(TIF_32BIT_FPREGS);
+		set_thread_flag(TIF_HYBRID_FPREGS);
+		break;
+
+	case MIPS_ABI_FP_64:
+	case MIPS_ABI_FP_64A:
+		/* FR=1, FRE=0 */
+		clear_thread_flag(TIF_32BIT_FPREGS);
+		clear_thread_flag(TIF_HYBRID_FPREGS);
+		break;
+
+	case MIPS_ABI_FP_XX:
+	case MIPS_ABI_FP_ANY:
+		if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT))
+			set_thread_flag(TIF_32BIT_FPREGS);
+		else
+			clear_thread_flag(TIF_32BIT_FPREGS);
+
+		clear_thread_flag(TIF_HYBRID_FPREGS);
+		break;
+
+	default:
+	case FP_ERROR:
+		BUG();
+	}
+}
-- 
2.0.4

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 10/10] MIPS: Kconfig option to better exercise/debug hybrid FPRs
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

The hybrid FPR scheme exists to allow for compatibility between existing
FP32 code and newly compiled FP64A code. Such code should hopefully be
rare in the real world, and for the moment is difficult to come across.
All code except that built for the FP64 ABI can correctly execute using
the hybrid FPR scheme, so debugging the hybrid FPR implementation can
be eased by forcing all such code to use it. This is undesirable in
general due to the trap & emulate overhead of the hybrid FPR
implementation, but is a very useful option to have for debugging.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/Kconfig.debug | 13 +++++++++++++
 arch/mips/kernel/elf.c  | 18 ++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/arch/mips/Kconfig.debug b/arch/mips/Kconfig.debug
index 3a2b775..88a9f43 100644
--- a/arch/mips/Kconfig.debug
+++ b/arch/mips/Kconfig.debug
@@ -122,4 +122,17 @@ config SPINLOCK_TEST
 	help
 	  Add several files to the debugfs to test spinlock speed.
 
+config FP32XX_HYBRID_FPRS
+	bool "Run FP32 & FPXX code with hybrid FPRs"
+	depends on MIPS_O32_FP64_SUPPORT
+	help
+	  The hybrid FPR scheme is normally used only when a program needs to
+	  execute a mix of FP32 & FP64A code, since the trapping & emulation
+	  that it entails is expensive. When enabled, this option will lead
+	  to the kernel running programs which use the FP32 & FPXX FP ABIs
+	  using the hybrid FPR scheme, which can be useful for debugging
+	  purposes.
+
+	  If unsure, say N.
+
 endmenu
diff --git a/arch/mips/kernel/elf.c b/arch/mips/kernel/elf.c
index 34e9af5..abf8a1a 100644
--- a/arch/mips/kernel/elf.c
+++ b/arch/mips/kernel/elf.c
@@ -124,6 +124,24 @@ int arch_check_elf(void *_ehdr, bool has_interpreter,
 
 void mips_set_personality_fp(struct arch_elf_state *state)
 {
+	if (config_enabled(CONFIG_FP32XX_HYBRID_FPRS)) {
+		/*
+		 * Use hybrid FPRs for all code which can correctly execute
+		 * with that mode.
+		 */
+		switch (state->overall_abi) {
+		case MIPS_ABI_FP_DOUBLE:
+		case MIPS_ABI_FP_SINGLE:
+		case MIPS_ABI_FP_SOFT:
+		case MIPS_ABI_FP_XX:
+		case MIPS_ABI_FP_ANY:
+			/* FR=1, FRE=1 */
+			clear_thread_flag(TIF_32BIT_FPREGS);
+			set_thread_flag(TIF_HYBRID_FPREGS);
+			return;
+		}
+	}
+
 	switch (state->overall_abi) {
 	case MIPS_ABI_FP_DOUBLE:
 	case MIPS_ABI_FP_SINGLE:
-- 
2.0.4


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 10/10] MIPS: Kconfig option to better exercise/debug hybrid FPRs
@ 2014-09-11  7:30   ` Paul Burton
  0 siblings, 0 replies; 31+ messages in thread
From: Paul Burton @ 2014-09-11  7:30 UTC (permalink / raw)
  To: linux-mips; +Cc: Alexander Viro, linux-fsdevel, linux-kernel, Paul Burton

The hybrid FPR scheme exists to allow for compatibility between existing
FP32 code and newly compiled FP64A code. Such code should hopefully be
rare in the real world, and for the moment is difficult to come across.
All code except that built for the FP64 ABI can correctly execute using
the hybrid FPR scheme, so debugging the hybrid FPR implementation can
be eased by forcing all such code to use it. This is undesirable in
general due to the trap & emulate overhead of the hybrid FPR
implementation, but is a very useful option to have for debugging.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 arch/mips/Kconfig.debug | 13 +++++++++++++
 arch/mips/kernel/elf.c  | 18 ++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/arch/mips/Kconfig.debug b/arch/mips/Kconfig.debug
index 3a2b775..88a9f43 100644
--- a/arch/mips/Kconfig.debug
+++ b/arch/mips/Kconfig.debug
@@ -122,4 +122,17 @@ config SPINLOCK_TEST
 	help
 	  Add several files to the debugfs to test spinlock speed.
 
+config FP32XX_HYBRID_FPRS
+	bool "Run FP32 & FPXX code with hybrid FPRs"
+	depends on MIPS_O32_FP64_SUPPORT
+	help
+	  The hybrid FPR scheme is normally used only when a program needs to
+	  execute a mix of FP32 & FP64A code, since the trapping & emulation
+	  that it entails is expensive. When enabled, this option will lead
+	  to the kernel running programs which use the FP32 & FPXX FP ABIs
+	  using the hybrid FPR scheme, which can be useful for debugging
+	  purposes.
+
+	  If unsure, say N.
+
 endmenu
diff --git a/arch/mips/kernel/elf.c b/arch/mips/kernel/elf.c
index 34e9af5..abf8a1a 100644
--- a/arch/mips/kernel/elf.c
+++ b/arch/mips/kernel/elf.c
@@ -124,6 +124,24 @@ int arch_check_elf(void *_ehdr, bool has_interpreter,
 
 void mips_set_personality_fp(struct arch_elf_state *state)
 {
+	if (config_enabled(CONFIG_FP32XX_HYBRID_FPRS)) {
+		/*
+		 * Use hybrid FPRs for all code which can correctly execute
+		 * with that mode.
+		 */
+		switch (state->overall_abi) {
+		case MIPS_ABI_FP_DOUBLE:
+		case MIPS_ABI_FP_SINGLE:
+		case MIPS_ABI_FP_SOFT:
+		case MIPS_ABI_FP_XX:
+		case MIPS_ABI_FP_ANY:
+			/* FR=1, FRE=1 */
+			clear_thread_flag(TIF_32BIT_FPREGS);
+			set_thread_flag(TIF_HYBRID_FPREGS);
+			return;
+		}
+	}
+
 	switch (state->overall_abi) {
 	case MIPS_ABI_FP_DOUBLE:
 	case MIPS_ABI_FP_SINGLE:
-- 
2.0.4

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH 03/10] binfmt_elf: allow arch code to examine PT_LOPROC ... PT_HIPROC headers
  2014-09-11  7:30   ` Paul Burton
  (?)
@ 2014-11-12 13:41   ` Thierry Reding
  2014-11-13  0:16     ` Ralf Baechle
  -1 siblings, 1 reply; 31+ messages in thread
From: Thierry Reding @ 2014-11-12 13:41 UTC (permalink / raw)
  To: Ralf Baechle
  Cc: Paul Burton, linux-mips, Alexander Viro, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2994 bytes --]

On Thu, Sep 11, 2014 at 08:30:16AM +0100, Paul Burton wrote:
> MIPS is introducing new variants of its O32 ABI which differ in their
> handling of floating point, in order to enable a gradual transition
> towards a world where mips32 binaries can take advantage of new hardware
> features only available when configured for certain FP modes. In order
> to do this ELF binaries are being augmented with a new section that
> indicates, amongst other things, the FP mode requirements of the binary.
> The presence & location of such a section is indicated by a program
> header in the PT_LOPROC ... PT_HIPROC range.
> 
> In order to allow the MIPS architecture code to examine the program
> header & section in question, pass all program headers in this range
> to an architecture-specific arch_elf_pt_proc function. This function
> may return an error if the header is deemed invalid or unsuitable for
> the system, in which case that error will be returned from
> load_elf_binary and upwards through the execve syscall.
> 
> A means is required for the architecture code to make a decision once
> it is known that all such headers have been seen, but before it is too
> late to return from an execve syscall. For this purpose the
> arch_check_elf function is added, and called once, after all PT_LOPROC
> to PT_HIPROC headers have been passed to arch_elf_pt_proc but before
> the code which invoked execve has been lost. This enables the
> architecture code to make a decision based upon all the headers present
> in an ELF binary and its interpreter, as is required to forbid
> conflicting FP ABI requirements between an ELF & its interpreter.
> 
> In order to allow data to be stored throughout the calls to the above
> functions, struct arch_elf_state is introduced.
> 
> Finally a variant of the SET_PERSONALITY macro is introduced which
> accepts a pointer to the struct arch_elf_state, allowing it to act
> based upon state observed from the architecture specific program
> headers.
> 
> Signed-off-by: Paul Burton <paul.burton@imgtec.com>
> ---
>  fs/Kconfig.binfmt   |  3 +++
>  fs/binfmt_elf.c     | 36 ++++++++++++++++++++++++--
>  include/linux/elf.h | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 110 insertions(+), 2 deletions(-)

Hi Ralf,

This commit showed up in linux-next and causes a warning in linux/elf.h
because it doesn't know struct file. I've fixed it locally with this:

---
diff --git a/include/linux/elf.h b/include/linux/elf.h
index 6bd15043a585..dac5caaa3509 100644
--- a/include/linux/elf.h
+++ b/include/linux/elf.h
@@ -4,6 +4,8 @@
 #include <asm/elf.h>
 #include <uapi/linux/elf.h>
 
+struct file;
+
 #ifndef elf_read_implies_exec
   /* Executables for which elf_read_implies_exec() returns TRUE will
      have the READ_IMPLIES_EXEC personality flag set automatically.
---

Would you mind squashing that into the above commit to get rid of the
warning?

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH 03/10] binfmt_elf: allow arch code to examine PT_LOPROC ... PT_HIPROC headers
  2014-11-12 13:41   ` Thierry Reding
@ 2014-11-13  0:16     ` Ralf Baechle
  2014-11-13 12:25       ` Thierry Reding
  0 siblings, 1 reply; 31+ messages in thread
From: Ralf Baechle @ 2014-11-13  0:16 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Paul Burton, linux-mips, Alexander Viro, linux-fsdevel, linux-kernel

On Wed, Nov 12, 2014 at 02:41:04PM +0100, Thierry Reding wrote:

> Hi Ralf,
> 
> This commit showed up in linux-next and causes a warning in linux/elf.h
> because it doesn't know struct file. I've fixed it locally with this:
> 
> ---
> diff --git a/include/linux/elf.h b/include/linux/elf.h
> index 6bd15043a585..dac5caaa3509 100644
> --- a/include/linux/elf.h
> +++ b/include/linux/elf.h
> @@ -4,6 +4,8 @@
>  #include <asm/elf.h>
>  #include <uapi/linux/elf.h>
>  
> +struct file;
> +
>  #ifndef elf_read_implies_exec
>    /* Executables for which elf_read_implies_exec() returns TRUE will
>       have the READ_IMPLIES_EXEC personality flag set automatically.
> ---
> 
> Would you mind squashing that into the above commit to get rid of the
> warning?

To fix the warnings reported by sfr on powerpc64 this morning I moved
most of the code added to <linux/elf.h> into fs/binfmt_elf.c.  That
should also have taken care of the warnings you saw for ARM.

  Ralf

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 02/10] binfmt_elf: load interpreter program headers earlier
  2014-09-11  7:30   ` Paul Burton
  (?)
@ 2014-11-13 12:20   ` Thierry Reding
  2014-11-13 17:29     ` Ralf Baechle
  -1 siblings, 1 reply; 31+ messages in thread
From: Thierry Reding @ 2014-11-13 12:20 UTC (permalink / raw)
  To: Paul Burton, Ralf Baechle
  Cc: linux-mips, Alexander Viro, linux-fsdevel, linux-kernel


[-- Attachment #1.1: Type: text/plain, Size: 2976 bytes --]

On Thu, Sep 11, 2014 at 08:30:15AM +0100, Paul Burton wrote:
> Load the program headers of an ELF interpreter early enough in
> load_elf_binary that they can be examined before it's too late to return
> an error from an exec syscall. This patch does not perform any such
> checking, it merely lays the groundwork for a further patch to do so.
> 
> No functional change is intended.
> 
> Signed-off-by: Paul Burton <paul.burton@imgtec.com>
> ---
>  fs/binfmt_elf.c | 36 ++++++++++++++++++------------------
>  1 file changed, 18 insertions(+), 18 deletions(-)
> 
> diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
[...]

kmemleak started complaining for me recently and the stacktrace (see
below) points to this function:

	unreferenced object 0xec0f77c0 (size 192):
	  comm "kworker/u8:0", pid 169, jiffies 4294939367 (age 86.360s)
	  hex dump (first 32 bytes):
	    01 00 00 70 1c ef 01 00 1c ef 01 00 1c ef 01 00  ...p............
	    a0 00 00 00 a0 00 00 00 04 00 00 00 04 00 00 00  ................
	  backtrace:
	    [<c00ec080>] __kmalloc+0x104/0x190
	    [<c01387d4>] load_elf_phdrs+0x60/0x8c
	    [<c0138cb4>] load_elf_binary+0x280/0x12d8
	    [<c00f8ef0>] search_binary_handler+0x80/0x1f0
	    [<c00fa370>] do_execveat_common+0x570/0x658
	    [<c00fa480>] do_execve+0x28/0x30
	    [<c0038eb4>] ____call_usermodehelper+0x144/0x19c
	    [<c000e638>] ret_from_fork+0x14/0x3c
	    [<ffffffff>] 0xffffffff

> @@ -605,7 +598,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
>  	int load_addr_set = 0;
>  	char * elf_interpreter = NULL;
>  	unsigned long error;
> -	struct elf_phdr *elf_ppnt, *elf_phdata;
> +	struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
>  	unsigned long elf_bss, elf_brk;
>  	int retval, i;
>  	unsigned long elf_entry;
> @@ -729,6 +722,12 @@ static int load_elf_binary(struct linux_binprm *bprm)
>  		/* Verify the interpreter has a valid arch */
>  		if (!elf_check_arch(&loc->interp_elf_ex))
>  			goto out_free_dentry;
> +
> +		/* Load the interpreter program headers */
> +		interp_elf_phdata = load_elf_phdrs(&loc->interp_elf_ex,
> +						   interpreter);
> +		if (!interp_elf_phdata)
> +			goto out_free_dentry;
>  	}
>  
>  	/* Flush all traces of the currently running executable */
> @@ -912,7 +911,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
>  		elf_entry = load_elf_interp(&loc->interp_elf_ex,
>  					    interpreter,
>  					    &interp_map_addr,
> -					    load_bias);
> +					    load_bias, interp_elf_phdata);
>  		if (!IS_ERR((void *)elf_entry)) {
>  			/*
>  			 * load_elf_interp() returns relocation
> @@ -1009,6 +1008,7 @@ out_ret:
>  
>  	/* error cleanup */
>  out_free_dentry:
> +	kfree(interp_elf_phdata);

I think what happens is that the interp_elf_phdata memory is freed only
in the error cleanup path, but not when the function actually succeeds.

The attached patch plugs the leak for me.

Thierry

[-- Attachment #1.2: patch --]
[-- Type: text/plain, Size: 308 bytes --]

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index f95da60e440e..8a9be83e88c2 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1029,6 +1029,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 		}
 	}
 
+	kfree(interp_elf_phdata);
 	kfree(elf_phdata);
 
 	set_binfmt(&elf_format);

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH 03/10] binfmt_elf: allow arch code to examine PT_LOPROC ... PT_HIPROC headers
  2014-11-13  0:16     ` Ralf Baechle
@ 2014-11-13 12:25       ` Thierry Reding
  0 siblings, 0 replies; 31+ messages in thread
From: Thierry Reding @ 2014-11-13 12:25 UTC (permalink / raw)
  To: Ralf Baechle
  Cc: Paul Burton, linux-mips, Alexander Viro, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1246 bytes --]

On Thu, Nov 13, 2014 at 01:16:19AM +0100, Ralf Baechle wrote:
> On Wed, Nov 12, 2014 at 02:41:04PM +0100, Thierry Reding wrote:
> 
> > Hi Ralf,
> > 
> > This commit showed up in linux-next and causes a warning in linux/elf.h
> > because it doesn't know struct file. I've fixed it locally with this:
> > 
> > ---
> > diff --git a/include/linux/elf.h b/include/linux/elf.h
> > index 6bd15043a585..dac5caaa3509 100644
> > --- a/include/linux/elf.h
> > +++ b/include/linux/elf.h
> > @@ -4,6 +4,8 @@
> >  #include <asm/elf.h>
> >  #include <uapi/linux/elf.h>
> >  
> > +struct file;
> > +
> >  #ifndef elf_read_implies_exec
> >    /* Executables for which elf_read_implies_exec() returns TRUE will
> >       have the READ_IMPLIES_EXEC personality flag set automatically.
> > ---
> > 
> > Would you mind squashing that into the above commit to get rid of the
> > warning?
> 
> To fix the warnings reported by sfr on powerpc64 this morning I moved
> most of the code added to <linux/elf.h> into fs/binfmt_elf.c.  That
> should also have taken care of the warnings you saw for ARM.

These changes didn't make it into today's next, but manually applying
them I can indeed verify that the build warning is gone.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 02/10] binfmt_elf: load interpreter program headers earlier
  2014-11-13 12:20   ` Thierry Reding
@ 2014-11-13 17:29     ` Ralf Baechle
  0 siblings, 0 replies; 31+ messages in thread
From: Ralf Baechle @ 2014-11-13 17:29 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Paul Burton, linux-mips, Alexander Viro, linux-fsdevel, linux-kernel

On Thu, Nov 13, 2014 at 01:20:20PM +0100, Thierry Reding wrote:

> kmemleak started complaining for me recently and the stacktrace (see
> below) points to this function:
> 
> 	unreferenced object 0xec0f77c0 (size 192):
> 	  comm "kworker/u8:0", pid 169, jiffies 4294939367 (age 86.360s)
> 	  hex dump (first 32 bytes):
> 	    01 00 00 70 1c ef 01 00 1c ef 01 00 1c ef 01 00  ...p............
> 	    a0 00 00 00 a0 00 00 00 04 00 00 00 04 00 00 00  ................
> 	  backtrace:
> 	    [<c00ec080>] __kmalloc+0x104/0x190
> 	    [<c01387d4>] load_elf_phdrs+0x60/0x8c
> 	    [<c0138cb4>] load_elf_binary+0x280/0x12d8
> 	    [<c00f8ef0>] search_binary_handler+0x80/0x1f0
> 	    [<c00fa370>] do_execveat_common+0x570/0x658
> 	    [<c00fa480>] do_execve+0x28/0x30
> 	    [<c0038eb4>] ____call_usermodehelper+0x144/0x19c
> 	    [<c000e638>] ret_from_fork+0x14/0x3c
> 	    [<ffffffff>] 0xffffffff
[...]
> I think what happens is that the interp_elf_phdata memory is freed only
> in the error cleanup path, but not when the function actually succeeds.
> 
> The attached patch plugs the leak for me.
> 
> Thierry

> diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
> index f95da60e440e..8a9be83e88c2 100644
> --- a/fs/binfmt_elf.c
> +++ b/fs/binfmt_elf.c
> @@ -1029,6 +1029,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
>  		}
>  	}
>  
> +	kfree(interp_elf_phdata);
>  	kfree(elf_phdata);
>  
>  	set_binfmt(&elf_format);

Folded in and testing now.

  Ralf

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 07/10] MIPS: support for hybrid FPRs
  2014-09-11  7:30   ` Paul Burton
  (?)
@ 2014-12-23 23:21   ` Aaro Koskinen
  2014-12-23 23:31       ` James Hogan
  -1 siblings, 1 reply; 31+ messages in thread
From: Aaro Koskinen @ 2014-12-23 23:21 UTC (permalink / raw)
  To: Paul Burton, Ralf Baechle
  Cc: linux-mips, Alexander Viro, linux-fsdevel, linux-kernel

Hi,

On Thu, Sep 11, 2014 at 08:30:20AM +0100, Paul Burton wrote:
> Hybrid FPRs is a scheme where scalar FP registers are 64b wide, but
> accesses to odd indexed single registers use bits 63:32 of the
> preceeding even indexed 64b register. In this mode all FP code
> except that built for the plain FP64 ABI can execute correctly. Most
> notably a combination of FP64A & FP32 code can execute correctly,
> allowing for existing FP32 binaries to be linked with new FP64A binaries
> that can make use of 64 bit FP & MSA.

This commit (4227a2d4efc9c84f35826dc4d1e6dc183f6c1c05, bisected)
in 3.19-rc1 breaks my Loongson-2F system. I get endless amount
of "Reserved instruction in kernel code" exceptions when booting.
See some examples below. Nothing crashes, and there is some forward
progress, but obviously it's completely unusable.

Any ideas?

[    2.872000] Reserved instruction in kernel code[#1]:
[    2.872000] CPU: 0 PID: 231 Comm: hotplug Not tainted 3.18.0-lemote-los_7f08-09423-g988adfd #1
[    2.872000] task: 980000009f1c7480 ti: 980000009a2e8000 task.ti: 980000009a2e8000
[    2.872000] $ 0   : 0000000000000000 0000000077d32c14 0000000000000000 0000000000000001
[    2.872000] $ 4   : 0000000000000000 000000001000802c 980000009a2ebeb0 0000000000000002
[    2.872000] $ 8   : 0000000000000010 000000007efefeff 0000000024242424 ffffffff81010100
[    2.872000] $12   : 00000000100044e1 000000001000001f 0000000000000000 0000000000000000
[    2.872000] $16   : 0000000000400164 000000007fdb3ee0 0000000077d62cf8 0000000000000000
[    2.872000] $20   : 0000000000000000 0000000077d61ed0 0000000077d61ed0 00000000004022d8
[    2.872000] $24   : 0000000000000005 0000000077d4ba80                                  
[    2.872000] $28   : 980000009a2e8000 980000009a2ebe70 000000007fdb3ee8 ffffffff802077e0
[    2.872000] Hi    : 000000000000002c
[    2.872000] Lo    : 000000000000000b
[    2.872000] epc   : ffffffff8020e8d4 do_cpu+0x304/0x4f0
[    2.872000]     Not tainted
[    2.872000] ra    : ffffffff802077e0 ret_from_exception+0x0/0x1c
[    2.872000] Status: 100044e3	KX SX UX KERNEL EXL IE 
[    2.872000] Cause : 10008028
[    2.872000] PrId  : 00006303 (ICT Loongson-2)
[    2.872000] Modules linked in:
[    2.872000] Process hotplug (pid: 231, threadinfo=980000009a2e8000, task=980000009f1c7480, tls=0000000000000000)
[    2.872000] Stack : 0000000077d50000 0000000077d62c10 ffffffffffffc000 0000000077d61ed0
	  0000000077d622d8 0000000000400164 000000007fdb3ee0 ffffffff802077e0
	  0000000000000000 0000000077d32c14 000000007fdb3da8 0000000077d62bf0
	  000000007fdb3db8 0000000000000000 000000007fdb3d90 000000007fdb3ee8
	  ffffffffbba0ffce 000000007efefeff 0000000024242424 ffffffff81010100
	  0000000000000000 0000000000000fc1 0000000000000000 0000000000000000
	  0000000000400164 000000007fdb3ee0 0000000077d62cf8 0000000000000000
	  0000000000000000 0000000077d61ed0 0000000077d61ed0 00000000004022d8
	  0000000000000005 0000000077d4ba80 000000006474e552 0000000000000000
	  0000000077d6a000 000000007fdb3d90 000000007fdb3ee8 0000000077d41038
	  ...
[    2.872000] Call Trace:
[    2.872000] [<ffffffff8020e8d4>] do_cpu+0x304/0x4f0
[    2.872000] [<ffffffff802077e0>] ret_from_exception+0x0/0x1c
[    2.872000] 
[    2.872000] 
Code: 30420001  2c420001  0040202d <40038005> 2405feff  00651824  40838005  3c032000  3c052400 
[    2.876000] ---[ end trace 71c7b14ce7da936f ]---
[    2.880000] Reserved instruction in kernel code[#2]:
[    2.880000] CPU: 0 PID: 232 Comm: hotplug Tainted: G      D        3.18.0-lemote-los_7f08-09423-g988adfd #1
[    2.880000] task: 980000009f1c5ea8 ti: 980000009a2e4000 task.ti: 980000009a2e4000
[    2.880000] $ 0   : 0000000000000000 0000000077832c14 0000000000000000 0000000000000001
[    2.880000] $ 4   : 0000000000000000 000000001000802c 980000009a2e7eb0 0000000000000002
[    2.880000] $ 8   : 0000000000000010 000000007efefeff 0000000024242424 ffffffff81010100
[    2.880000] $12   : 00000000100044e1 000000001000001f 0000000000000000 0000000000000000
[    2.880000] $16   : 0000000000400164 000000007fd876e0 0000000077862cf8 0000000000000000
[    2.880000] $20   : 0000000000000000 0000000077861ed0 0000000077861ed0 00000000004022d8
[    2.880000] $24   : 0000000000000005 000000007784ba80                                  
[    2.880000] $28   : 980000009a2e4000 980000009a2e7e70 000000007fd876e8 ffffffff802077e0
[    2.880000] Hi    : 000000000000002c
[    2.880000] Lo    : 000000000000000b
[    2.880000] epc   : ffffffff8020e8d4 do_cpu+0x304/0x4f0
[    2.880000]     Tainted: G      D       
[    2.880000] ra    : ffffffff802077e0 ret_from_exception+0x0/0x1c
[    2.880000] Status: 100044e3	KX SX UX KERNEL EXL IE 
[    2.880000] Cause : 10008028
[    2.880000] PrId  : 00006303 (ICT Loongson-2)
[    2.880000] Modules linked in:
[    2.880000] Process hotplug (pid: 232, threadinfo=980000009a2e4000, task=980000009f1c5ea8, tls=0000000000000000)
[    2.880000] Stack : 0000000077850000 0000000077862c10 ffffffffffffc000 0000000077861ed0
	  00000000778622d8 0000000000400164 000000007fd876e0 ffffffff802077e0
	  0000000000000000 0000000077832c14 000000007fd875a8 0000000077862bf0
	  000000007fd875b8 0000000000000000 000000007fd87590 000000007fd876e8
	  ffffffffbba0ffce 000000007efefeff 0000000024242424 ffffffff81010100
	  0000000000000000 0000000000000fc1 0000000000000000 0000000000000000
	  0000000000400164 000000007fd876e0 0000000077862cf8 0000000000000000
	  0000000000000000 0000000077861ed0 0000000077861ed0 00000000004022d8
	  0000000000000005 000000007784ba80 000000006474e552 0000000000000000
	  000000007786a000 000000007fd87590 000000007fd876e8 0000000077841038
	  ...
[    2.880000] Call Trace:
[    2.880000] [<ffffffff8020e8d4>] do_cpu+0x304/0x4f0
[    2.880000] [<ffffffff802077e0>] ret_from_exception+0x0/0x1c
[    2.880000] 
[    2.880000] 
Code: 30420001  2c420001  0040202d <40038005> 2405feff  00651824  40838005  3c032000  3c052400 
[    2.884000] ---[ end trace 71c7b14ce7da9370 ]---
[    2.888000] Reserved instruction in kernel code[#3]:
[    2.888000] CPU: 0 PID: 233 Comm: hotplug Tainted: G      D        3.18.0-lemote-los_7f08-09423-g988adfd #1
[    2.888000] task: 980000009f1c48d0 ti: 980000009a2e0000 task.ti: 980000009a2e0000
[    2.888000] $ 0   : 0000000000000000 0000000077d5ec14 0000000000000000 0000000000000001
[    2.888000] $ 4   : 0000000000000000 000000001000802c 980000009a2e3eb0 0000000000000002
[    2.888000] $ 8   : 0000000000000010 000000007efefeff 0000000024242424 ffffffff81010100
[    2.888000] $12   : 00000000100044e1 000000001000001f 0000000000000000 0000000000000000
[    2.888000] $16   : 0000000000400164 000000007f9b0ad0 0000000077d8ecf8 0000000000000000
[    2.888000] $20   : 0000000000000000 0000000077d8ded0 0000000077d8ded0 00000000004022d8
[    2.888000] $24   : 0000000000000005 0000000077d77a80                                  
[    2.888000] $28   : 980000009a2e0000 980000009a2e3e70 000000007f9b0ad8 ffffffff802077e0
[    2.888000] Hi    : 000000000000002c
[    2.888000] Lo    : 000000000000000b
[    2.888000] epc   : ffffffff8020e8d4 do_cpu+0x304/0x4f0
[    2.888000]     Tainted: G      D       
[    2.888000] ra    : ffffffff802077e0 ret_from_exception+0x0/0x1c
[    2.888000] Status: 100044e3	KX SX UX KERNEL EXL IE 
[    2.888000] Cause : 10008028
[    2.888000] PrId  : 00006303 (ICT Loongson-2)
[    2.888000] Modules linked in:
[    2.888000] Process hotplug (pid: 233, threadinfo=980000009a2e0000, task=980000009f1c48d0, tls=0000000000000000)
[    2.888000] Stack : 0000000077d7c000 0000000077d8ec10 ffffffffffffc000 0000000077d8ded0
	  0000000077d8e2d8 0000000000400164 000000007f9b0ad0 ffffffff802077e0
	  0000000000000000 0000000077d5ec14 000000007f9b0998 0000000077d8ebf0
	  000000007f9b09a8 0000000000000000 000000007f9b0980 000000007f9b0ad8
	  ffffffffbba0ffce 000000007efefeff 0000000024242424 ffffffff81010100
	  0000000000000000 0000000000000fc1 0000000000000000 0000000000000000
	  0000000000400164 000000007f9b0ad0 0000000077d8ecf8 0000000000000000
	  0000000000000000 0000000077d8ded0 0000000077d8ded0 00000000004022d8
	  0000000000000005 0000000077d77a80 000000006474e552 0000000000000000
	  0000000077d96000 000000007f9b0980 000000007f9b0ad8 0000000077d6d038
	  ...
[    2.888000] Call Trace:
[    2.888000] [<ffffffff8020e8d4>] do_cpu+0x304/0x4f0
[    2.888000] [<ffffffff802077e0>] ret_from_exception+0x0/0x1c
[    2.888000] 
[    2.888000] 
Code: 30420001  2c420001  0040202d <40038005> 2405feff  00651824  40838005  3c032000  3c052400 
[    2.892000] ---[ end trace 71c7b14ce7da9371 ]---

A.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 07/10] MIPS: support for hybrid FPRs
@ 2014-12-23 23:31       ` James Hogan
  0 siblings, 0 replies; 31+ messages in thread
From: James Hogan @ 2014-12-23 23:31 UTC (permalink / raw)
  To: Aaro Koskinen
  Cc: Paul Burton, Ralf Baechle, linux-mips, Alexander Viro,
	linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1337 bytes --]

On Wed, Dec 24, 2014 at 01:21:11AM +0200, Aaro Koskinen wrote:
> Hi,
> 
> On Thu, Sep 11, 2014 at 08:30:20AM +0100, Paul Burton wrote:
> > Hybrid FPRs is a scheme where scalar FP registers are 64b wide, but
> > accesses to odd indexed single registers use bits 63:32 of the
> > preceeding even indexed 64b register. In this mode all FP code
> > except that built for the plain FP64 ABI can execute correctly. Most
> > notably a combination of FP64A & FP32 code can execute correctly,
> > allowing for existing FP32 binaries to be linked with new FP64A binaries
> > that can make use of 64 bit FP & MSA.
> 
> This commit (4227a2d4efc9c84f35826dc4d1e6dc183f6c1c05, bisected)
> in 3.19-rc1 breaks my Loongson-2F system. I get endless amount
> of "Reserved instruction in kernel code" exceptions when booting.
> See some examples below. Nothing crashes, and there is some forward
> progress, but obviously it's completely unusable.
> 
> Any ideas?
> 
> [    2.872000] Reserved instruction in kernel code[#1]:
...
> Code: 30420001  2c420001  0040202d <40038005> 2405feff  00651824  40838005  3c032000  3c052400 

0x40038005 = mfc0 v1,$16,5 = mfc0 v1,Config5

Does this help (in linux-next)?:
http://git.linux-mips.org/cgit/ralf/upstream-sfr.git/commit/?id=5bba8dec735f18fe7a2fcd8327f28ef095337ff2

Cheers
James

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 07/10] MIPS: support for hybrid FPRs
@ 2014-12-23 23:31       ` James Hogan
  0 siblings, 0 replies; 31+ messages in thread
From: James Hogan @ 2014-12-23 23:31 UTC (permalink / raw)
  To: Aaro Koskinen
  Cc: Paul Burton, Ralf Baechle, linux-mips, Alexander Viro,
	linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1337 bytes --]

On Wed, Dec 24, 2014 at 01:21:11AM +0200, Aaro Koskinen wrote:
> Hi,
> 
> On Thu, Sep 11, 2014 at 08:30:20AM +0100, Paul Burton wrote:
> > Hybrid FPRs is a scheme where scalar FP registers are 64b wide, but
> > accesses to odd indexed single registers use bits 63:32 of the
> > preceeding even indexed 64b register. In this mode all FP code
> > except that built for the plain FP64 ABI can execute correctly. Most
> > notably a combination of FP64A & FP32 code can execute correctly,
> > allowing for existing FP32 binaries to be linked with new FP64A binaries
> > that can make use of 64 bit FP & MSA.
> 
> This commit (4227a2d4efc9c84f35826dc4d1e6dc183f6c1c05, bisected)
> in 3.19-rc1 breaks my Loongson-2F system. I get endless amount
> of "Reserved instruction in kernel code" exceptions when booting.
> See some examples below. Nothing crashes, and there is some forward
> progress, but obviously it's completely unusable.
> 
> Any ideas?
> 
> [    2.872000] Reserved instruction in kernel code[#1]:
...
> Code: 30420001  2c420001  0040202d <40038005> 2405feff  00651824  40838005  3c032000  3c052400 

0x40038005 = mfc0 v1,$16,5 = mfc0 v1,Config5

Does this help (in linux-next)?:
http://git.linux-mips.org/cgit/ralf/upstream-sfr.git/commit/?id=5bba8dec735f18fe7a2fcd8327f28ef095337ff2

Cheers
James

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 07/10] MIPS: support for hybrid FPRs
  2014-12-23 23:31       ` James Hogan
  (?)
@ 2014-12-23 23:51       ` Aaro Koskinen
  -1 siblings, 0 replies; 31+ messages in thread
From: Aaro Koskinen @ 2014-12-23 23:51 UTC (permalink / raw)
  To: James Hogan
  Cc: Paul Burton, Ralf Baechle, linux-mips, Alexander Viro,
	linux-fsdevel, linux-kernel

Hi,

On Tue, Dec 23, 2014 at 11:31:54PM +0000, James Hogan wrote:
> On Wed, Dec 24, 2014 at 01:21:11AM +0200, Aaro Koskinen wrote:
> > On Thu, Sep 11, 2014 at 08:30:20AM +0100, Paul Burton wrote:
> > > Hybrid FPRs is a scheme where scalar FP registers are 64b wide, but
> > > accesses to odd indexed single registers use bits 63:32 of the
> > > preceeding even indexed 64b register. In this mode all FP code
> > > except that built for the plain FP64 ABI can execute correctly. Most
> > > notably a combination of FP64A & FP32 code can execute correctly,
> > > allowing for existing FP32 binaries to be linked with new FP64A binaries
> > > that can make use of 64 bit FP & MSA.
> > 
> > This commit (4227a2d4efc9c84f35826dc4d1e6dc183f6c1c05, bisected)
> > in 3.19-rc1 breaks my Loongson-2F system. I get endless amount
> > of "Reserved instruction in kernel code" exceptions when booting.
> > See some examples below. Nothing crashes, and there is some forward
> > progress, but obviously it's completely unusable.
> > 
> > Any ideas?
> > 
> > [    2.872000] Reserved instruction in kernel code[#1]:
> ...
> > Code: 30420001  2c420001  0040202d <40038005> 2405feff  00651824  40838005  3c032000  3c052400 
> 
> 0x40038005 = mfc0 v1,$16,5 = mfc0 v1,Config5
> 
> Does this help (in linux-next)?:
> http://git.linux-mips.org/cgit/ralf/upstream-sfr.git/commit/?id=5bba8dec735f18fe7a2fcd8327f28ef095337ff2

Yes, it does. Many thanks!

A.

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2014-12-23 23:51 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-09-11  7:30 [PATCH 00/10] MIPS O32 new FP ABI support Paul Burton
2014-09-11  7:30 ` Paul Burton
2014-09-11  7:30 ` [PATCH 01/10] binfmt_elf: hoist ELF program header loading to a function Paul Burton
2014-09-11  7:30   ` Paul Burton
2014-09-11  7:30 ` [PATCH 02/10] binfmt_elf: load interpreter program headers earlier Paul Burton
2014-09-11  7:30   ` Paul Burton
2014-11-13 12:20   ` Thierry Reding
2014-11-13 17:29     ` Ralf Baechle
2014-09-11  7:30 ` [PATCH 03/10] binfmt_elf: allow arch code to examine PT_LOPROC ... PT_HIPROC headers Paul Burton
2014-09-11  7:30   ` Paul Burton
2014-11-12 13:41   ` Thierry Reding
2014-11-13  0:16     ` Ralf Baechle
2014-11-13 12:25       ` Thierry Reding
2014-09-11  7:30 ` [PATCH 04/10] MIPS: define bits introduced for hybrid FPRs Paul Burton
2014-09-11  7:30   ` Paul Burton
2014-09-11  7:30 ` [PATCH 05/10] MIPS: detect presence of the FRE & UFR bits Paul Burton
2014-09-11  7:30   ` Paul Burton
2014-09-11  7:30 ` [PATCH 06/10] MIPS: ensure Config5.UFE is clear on boot Paul Burton
2014-09-11  7:30   ` Paul Burton
2014-09-11  7:30 ` [PATCH 07/10] MIPS: support for hybrid FPRs Paul Burton
2014-09-11  7:30   ` Paul Burton
2014-12-23 23:21   ` Aaro Koskinen
2014-12-23 23:31     ` James Hogan
2014-12-23 23:31       ` James Hogan
2014-12-23 23:51       ` Aaro Koskinen
2014-09-11  7:30 ` [PATCH 08/10] MIPS: ELF: add definition for the .MIPS.abiflags section Paul Burton
2014-09-11  7:30   ` Paul Burton
2014-09-11  7:30 ` [PATCH 09/10] MIPS: ELF: set FP mode according to .MIPS.abiflags Paul Burton
2014-09-11  7:30   ` Paul Burton
2014-09-11  7:30 ` [PATCH 10/10] MIPS: Kconfig option to better exercise/debug hybrid FPRs Paul Burton
2014-09-11  7:30   ` Paul Burton

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.