2015-11-16 14:34:17

by Maciej W. Rozycki

[permalink] [raw]
Subject: [RFC PATCH 0/4] MIPS: IEEE Std 754 NaN interlinking support

Hi,

This implements the kernel part of IEEE Std 754 NaN interlinking support,
as per "MIPS ABI Extension for IEEE Std 754 Non-Compliant Interlinking"
<https://dmz-portal.mips.com/wiki/MIPS_ABI_-_NaN_Interlinking>.

Four patches are included: a pair of preparatory changes, a generic one
to allow ports to provide their own auxiliary vector's AT_FLAGS entry
initialiser and one factoring out pieces of FP context maintenance code,
respectively, and then a pair of actual changes, implementing the NaN
interlinking feature proper and a prctl(2) interface to switch the
compliance mode dynamically respectively.

These patches rely on 2008-NaN support, recently posted, having been
applied first.

At this point this is a request for comments only rather than an actual
patch submission for inclusion, as consensus about the specification has
to be reached first. All feedback is welcome on the implementation and
I'll be happy to address any questions, comments or concerns.

Maciej


2015-11-16 14:34:33

by Maciej W. Rozycki

[permalink] [raw]
Subject: [RFC PATCH 1/4] ELF: Add platform-specific AT_FLAGS initialisation support

Signed-off-by: Maciej W. Rozycki <[email protected]>
---
linux-elf-at-flags.diff
Index: linux-sfr-test/fs/binfmt_elf.c
===================================================================
--- linux-sfr-test.orig/fs/binfmt_elf.c 2015-09-08 15:24:00.927208000 +0100
+++ linux-sfr-test/fs/binfmt_elf.c 2015-09-08 15:26:10.318310000 +0100
@@ -72,6 +72,10 @@ static int elf_core_dump(struct coredump
#define ELF_MIN_ALIGN PAGE_SIZE
#endif

+#ifndef ELF_FLAGS
+#define ELF_FLAGS 0
+#endif
+
#ifndef ELF_CORE_EFLAGS
#define ELF_CORE_EFLAGS 0
#endif
@@ -238,7 +242,7 @@ create_elf_tables(struct linux_binprm *b
NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
NEW_AUX_ENT(AT_BASE, interp_load_addr);
- NEW_AUX_ENT(AT_FLAGS, 0);
+ NEW_AUX_ENT(AT_FLAGS, ELF_FLAGS);
NEW_AUX_ENT(AT_ENTRY, exec->e_entry);
NEW_AUX_ENT(AT_UID, from_kuid_munged(cred->user_ns, cred->uid));
NEW_AUX_ENT(AT_EUID, from_kuid_munged(cred->user_ns, cred->euid));
Index: linux-sfr-test/fs/binfmt_elf_fdpic.c
===================================================================
--- linux-sfr-test.orig/fs/binfmt_elf_fdpic.c 2015-09-08 15:24:00.950209000 +0100
+++ linux-sfr-test/fs/binfmt_elf_fdpic.c 2015-09-08 15:29:25.860980000 +0100
@@ -80,6 +80,10 @@ static int elf_fdpic_map_file_by_direct_
static int elf_fdpic_core_dump(struct coredump_params *cprm);
#endif

+#ifndef ELF_FLAGS
+#define ELF_FLAGS 0
+#endif
+
static struct linux_binfmt elf_fdpic_format = {
.module = THIS_MODULE,
.load_binary = load_elf_fdpic_binary,
@@ -616,7 +620,7 @@ static int create_elf_fdpic_tables(struc
NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
NEW_AUX_ENT(AT_PHNUM, exec_params->hdr.e_phnum);
NEW_AUX_ENT(AT_BASE, interp_params->elfhdr_addr);
- NEW_AUX_ENT(AT_FLAGS, 0);
+ NEW_AUX_ENT(AT_FLAGS, ELF_FLAGS);
NEW_AUX_ENT(AT_ENTRY, exec_params->entry_addr);
NEW_AUX_ENT(AT_UID, (elf_addr_t) from_kuid_munged(cred->user_ns, cred->uid));
NEW_AUX_ENT(AT_EUID, (elf_addr_t) from_kuid_munged(cred->user_ns, cred->euid));

2015-11-16 14:34:53

by Maciej W. Rozycki

[permalink] [raw]
Subject: [RFC PATCH 2/4] MIPS: Factor out FP context preemption

Signed-off-by: Maciej W. Rozycki <[email protected]>
---
Following the discussion around commit 9791554b [MIPS,prctl: add
PR_[GS]ET_FP_MODE prctl options for MIPS] or
<http://patchwork.linux-mips.org/patch/8899/> and Leonid's observation
<http://www.linux-mips.org/cgi-bin/mesg.cgi?a=linux-mips&i=54B02115.7090609%40imgtec.com>
in particular, I agree this would best be done with an IPI, however such
an improvement is independent of the changes made as a part of this series
so I took the minimal approach and left the solution implemented so far
unchanged. Especially as Leonid says he's got a patch already available.

linux-mips-process-fp-context.diff
Index: linux-sfr-test/arch/mips/kernel/process.c
===================================================================
--- linux-sfr-test.orig/arch/mips/kernel/process.c 2015-11-13 00:36:09.885716000 +0000
+++ linux-sfr-test/arch/mips/kernel/process.c 2015-11-16 13:50:18.962058000 +0000
@@ -570,6 +570,60 @@ void arch_trigger_all_cpu_backtrace(bool
smp_call_function(arch_dump_stack, NULL, 1);
}

+/*
+ * Make the FP context available for mode changes.
+ */
+static void mips_get_fp_context(struct task_struct *task)
+{
+ unsigned long switch_count;
+ struct task_struct *t;
+
+ /* Save FP & vector context, then disable FPU & MSA. */
+ if (task->signal == current->signal)
+ lose_fpu(1);
+
+ /* Prevent any threads from obtaining live FP context. */
+ atomic_set(&task->mm->context.fp_mode_switching, 1);
+ smp_mb__after_atomic();
+
+ /*
+ * If there are multiple online CPUs then wait until all threads
+ * whose FP mode is about to change have been context switched.
+ * This approach allows us to only worry about whether an FP mode
+ * switch is in progress when FP is first used in a tasks time
+ * slice. Pretty much all of the mode switch overhead can thus
+ * be confined to cases where mode switches are actually occurring.
+ * That is, to here. However for the thread performing the mode
+ * switch it may take a while...
+ */
+ if (num_online_cpus() > 1) {
+ spin_lock_irq(&task->sighand->siglock);
+
+ for_each_thread(task, t) {
+ if (t == current)
+ continue;
+
+ switch_count = t->nvcsw + t->nivcsw;
+
+ do {
+ spin_unlock_irq(&task->sighand->siglock);
+ cond_resched();
+ spin_lock_irq(&task->sighand->siglock);
+ } while ((t->nvcsw + t->nivcsw) == switch_count);
+ }
+
+ spin_unlock_irq(&task->sighand->siglock);
+ }
+}
+
+/*
+ * Allow threads to use FP again.
+ */
+static void mips_put_fp_context(struct task_struct *task)
+{
+ atomic_set(&task->mm->context.fp_mode_switching, 0);
+}
+
int mips_get_process_fp_mode(struct task_struct *task)
{
int value = 0;
@@ -585,7 +639,6 @@ int mips_get_process_fp_mode(struct task
int mips_set_process_fp_mode(struct task_struct *task, unsigned int value)
{
const unsigned int known_bits = PR_FP_MODE_FR | PR_FP_MODE_FRE;
- unsigned long switch_count;
struct task_struct *t;

/* Check the value is valid */
@@ -603,41 +656,7 @@ int mips_set_process_fp_mode(struct task
if (!(value & PR_FP_MODE_FR) && cpu_has_fpu && cpu_has_mips_r6)
return -EOPNOTSUPP;

- /* Save FP & vector context, then disable FPU & MSA */
- if (task->signal == current->signal)
- lose_fpu(1);
-
- /* Prevent any threads from obtaining live FP context */
- atomic_set(&task->mm->context.fp_mode_switching, 1);
- smp_mb__after_atomic();
-
- /*
- * If there are multiple online CPUs then wait until all threads whose
- * FP mode is about to change have been context switched. This approach
- * allows us to only worry about whether an FP mode switch is in
- * progress when FP is first used in a tasks time slice. Pretty much all
- * of the mode switch overhead can thus be confined to cases where mode
- * switches are actually occuring. That is, to here. However for the
- * thread performing the mode switch it may take a while...
- */
- if (num_online_cpus() > 1) {
- spin_lock_irq(&task->sighand->siglock);
-
- for_each_thread(task, t) {
- if (t == current)
- continue;
-
- switch_count = t->nvcsw + t->nivcsw;
-
- do {
- spin_unlock_irq(&task->sighand->siglock);
- cond_resched();
- spin_lock_irq(&task->sighand->siglock);
- } while ((t->nvcsw + t->nivcsw) == switch_count);
- }
-
- spin_unlock_irq(&task->sighand->siglock);
- }
+ mips_get_fp_context(task);

/*
* There are now no threads of the process with live FP context, so it
@@ -659,8 +678,7 @@ int mips_set_process_fp_mode(struct task
clear_tsk_thread_flag(t, TIF_HYBRID_FPREGS);
}

- /* Allow threads to use FP again */
- atomic_set(&task->mm->context.fp_mode_switching, 0);
+ mips_put_fp_context(task);

return 0;
}

2015-11-16 14:35:13

by Maciej W. Rozycki

[permalink] [raw]
Subject: [RFC PATCH 3/4] MIPS: Implement IEEE Std 754 NaN interlinking support

Implement the kernel part of IEEE Std 754 NaN interlinking support, as
per "MIPS ABI Extension for IEEE Std 754 Non-Compliant Interlinking"
<https://dmz-portal.mips.com/wiki/MIPS_ABI_-_NaN_Interlinking>:

* interpret the MIPS_AFL_FLAGS1_IEEE and MIPS_AFL_FLAGS2_RELAXED flags
in PT_MIPS_ABIFLAGS,

* accept or reject ELF binaries and interpreters accordingly,

* initialise auxiliary vector's AT_FLAGS appropriately,

* suppress Invalid Operation exceptions with operations on sNaN data.

Add a `strictest' setting to the `ieee754=' kernel parameter to enforce
a full IEEE Std 754 conformance and reject any binaries which request
the relaxed NaN encoding mode. Update documentation accordingly.

Signed-off-by: Maciej W. Rozycki <[email protected]>
---
linux-mips-nan-interlink.diff
Index: linux-sfr-test/Documentation/kernel-parameters.txt
===================================================================
--- linux-sfr-test.orig/Documentation/kernel-parameters.txt 2015-11-11 02:21:08.155578000 +0000
+++ linux-sfr-test/Documentation/kernel-parameters.txt 2015-11-11 12:22:51.732826000 +0000
@@ -1400,7 +1400,8 @@ bytes respectively. Such letter suffixes
idle=nomwait: Disable mwait for CPU C-states

ieee754= [MIPS] Select IEEE Std 754 conformance mode
- Format: { strict | legacy | 2008 | relaxed }
+ Format: { strictest | strict | best | legacy | 2008 |
+ relaxed }
Default: strict

Choose which programs will be accepted for execution
@@ -1412,23 +1413,30 @@ bytes respectively. Such letter suffixes
encoding mode.

Available settings are as follows:
- strict accept binaries that request a NaN encoding
- supported by the FPU
- legacy only accept legacy-NaN binaries, if supported
- by the FPU
- 2008 only accept 2008-NaN binaries, if supported
- by the FPU
- relaxed accept any binaries regardless of whether
- supported by the FPU
+ strictest require binaries to request a NaN
+ encoding supported by the FPU, reject
+ binaries that request the relaxed NaN
+ encoding mode
+ strict accept binaries that request a NaN
+ encoding supported by the FPU
+ best accept binaries that request a NaN
+ encoding supported by the FPU or all
+ on the emulator
+ legacy only accept legacy-NaN binaries, if
+ supported by the FPU
+ 2008 only accept 2008-NaN binaries, if
+ supported by the FPU
+ relaxed accept any binaries regardless of
+ whether supported by the FPU

The FPU emulator is always able to support both NaN
- encodings, so if no FPU hardware is present or it has
- been disabled with 'nofpu', then the settings of
+ encodings, so if no FPU hardware is present or it
+ has been disabled with 'nofpu', then the settings of
'legacy' and '2008' strap the emulator accordingly,
- 'relaxed' straps the emulator for both legacy-NaN and
- 2008-NaN, whereas 'strict' enables legacy-NaN only on
- legacy processors and both NaN encodings on MIPS32 or
- MIPS64 CPUs.
+ 'best' and 'relaxed' strap the emulator for both
+ legacy-NaN and 2008-NaN, whereas 'strictest' and
+ 'strict' enable legacy-NaN only on legacy processors
+ and both NaN encodings on MIPS32 or MIPS64 CPUs.

The setting for ABS.fmt/NEG.fmt instruction execution
mode generally follows that for the NaN encoding,
Index: linux-sfr-test/arch/mips/include/asm/elf.h
===================================================================
--- linux-sfr-test.orig/arch/mips/include/asm/elf.h 2015-11-11 02:21:08.158574000 +0000
+++ linux-sfr-test/arch/mips/include/asm/elf.h 2015-11-11 12:23:55.325343000 +0000
@@ -170,6 +170,13 @@
#define SHF_MIPS_NAMES 0x02000000
#define SHF_MIPS_NODUPES 0x01000000

+/* AT_FLAGS bits 31:24 are reserved for system semantics in the MIPS psABI. */
+#define AV_FLAGS_SYSTEM_SHIFT 24
+
+/* MIPS/Linux system AT_FLAGS bits. */
+#define AV_FLAGS_MIPS_RELAXED (1U << (AV_FLAGS_SYSTEM_SHIFT + 1))
+ /* Relaxed IEEE 754 mode. */
+
#ifndef ELF_ARCH
/* ELF register definitions */
#define ELF_NGREG 45
@@ -205,6 +212,14 @@ struct mips_elf_abiflags_v0 {
#define MIPS_ABI_FP_64 6 /* -mips32r2 -mfp64 */
#define MIPS_ABI_FP_64A 7 /* -mips32r2 -mfp64 -mno-odd-spreg */

+/* Masks for `flags1'. */
+/* IEEE Std 754 compliance mode selection requested. */
+#define MIPS_AFL_FLAGS1_IEEE (1U << 1)
+
+/* Masks for `flags2'. */
+/* IEEE Std 754 relaxed compliance mode selected. */
+#define MIPS_AFL_FLAGS2_RELAXED (1U << 1)
+
#ifdef CONFIG_32BIT

/*
@@ -379,6 +394,18 @@ do { \
#define CORE_DUMP_USE_REGSET
#define ELF_EXEC_PAGESIZE PAGE_SIZE

+/* Platform-specific informational flags. */
+
+#define ELF_FLAGS \
+({ \
+ elf_addr_t at_flags = 0; \
+ \
+ if (test_thread_flag(TIF_IEEE754_RELAXED)) \
+ at_flags |= AV_FLAGS_MIPS_RELAXED; \
+ \
+ at_flags; \
+})
+
/* This yields a mask that user programs can use to figure out what
instruction set this cpu supports. This could be done in userspace,
but it's not easy, and we've already done it here. */
@@ -436,21 +463,31 @@ struct arch_elf_state {
int fp_abi;
int interp_fp_abi;
int overall_fp_mode;
+ bool ieee754_relaxed;
+ bool interp_ieee754_relaxed;
};

#define MIPS_ABI_FP_UNKNOWN (-1) /* Unknown FP ABI (kernel internal) */

-#define INIT_ARCH_ELF_STATE { \
- .nan_2008 = -1, \
- .fp_abi = MIPS_ABI_FP_UNKNOWN, \
- .interp_fp_abi = MIPS_ABI_FP_UNKNOWN, \
- .overall_fp_mode = -1, \
+#define INIT_ARCH_ELF_STATE { \
+ .nan_2008 = -1, \
+ .fp_abi = MIPS_ABI_FP_UNKNOWN, \
+ .interp_fp_abi = MIPS_ABI_FP_UNKNOWN, \
+ .overall_fp_mode = -1, \
+ .ieee754_relaxed = mips_default_ieee754_relaxed, \
+ .interp_ieee754_relaxed = false, \
}

/* Whether to accept legacy-NaN and 2008-NaN user binaries. */
extern bool mips_use_nan_legacy;
extern bool mips_use_nan_2008;

+/* Whether to select the IEEE Std 754 relaxed compliance mode by default. */
+extern bool mips_default_ieee754_relaxed;
+
+/* Whether to accept IEEE Std 754 relaxed compliance mode binaries. */
+extern bool mips_accept_ieee754_relaxed;
+
extern int arch_elf_pt_proc(void *ehdr, void *phdr, struct file *elf,
bool is_interp, struct arch_elf_state *state);

Index: linux-sfr-test/arch/mips/include/asm/thread_info.h
===================================================================
--- linux-sfr-test.orig/arch/mips/include/asm/thread_info.h 2015-11-11 02:21:08.160573000 +0000
+++ linux-sfr-test/arch/mips/include/asm/thread_info.h 2015-11-11 12:22:51.855825000 +0000
@@ -102,6 +102,7 @@ static inline struct thread_info *curren
#define TIF_UPROBE 6 /* breakpointed or singlestepping */
#define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */
#define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */
+#define TIF_IEEE754_RELAXED 17 /* Relaxed IEEE Std 754 rules */
#define TIF_MEMDIE 18 /* is terminating due to OOM killer */
#define TIF_NOHZ 19 /* in adaptive nohz mode */
#define TIF_FIXADE 20 /* Fix address errors in software */
Index: linux-sfr-test/arch/mips/kernel/cpu-probe.c
===================================================================
--- linux-sfr-test.orig/arch/mips/kernel/cpu-probe.c 2015-11-11 02:21:08.161578000 +0000
+++ linux-sfr-test/arch/mips/kernel/cpu-probe.c 2015-11-11 12:22:51.859824000 +0000
@@ -154,13 +154,15 @@ static void cpu_set_fpu_2008(struct cpui
* IEEE 754 conformance mode to use. Affects the NaN encoding and the
* ABS.fmt/NEG.fmt execution mode.
*/
-static enum { STRICT, LEGACY, STD2008, RELAXED } ieee754 = STRICT;
+static enum {
+ STRICTEST, STRICT, BEST, LEGACY, STD2008, RELAXED
+} ieee754 = STRICT;

/*
* Set the IEEE 754 NaN encodings and the ABS.fmt/NEG.fmt execution modes
* to support by the FPU emulator according to the IEEE 754 conformance
- * mode selected. Note that "relaxed" straps the emulator so that it
- * allows 2008-NaN binaries even for legacy processors.
+ * mode selected. Note that "best" and "relaxed" strap the emulator so
+ * that it allows 2008-NaN binaries even for legacy processors.
*/
static void cpu_set_nofpu_2008(struct cpuinfo_mips *c)
{
@@ -169,6 +171,7 @@ static void cpu_set_nofpu_2008(struct cp
c->fpu_msk31 &= ~(FPU_CSR_ABS2008 | FPU_CSR_NAN2008);

switch (ieee754) {
+ case STRICTEST:
case STRICT:
if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 |
MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 |
@@ -179,6 +182,9 @@ static void cpu_set_nofpu_2008(struct cp
c->fpu_msk31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008;
}
break;
+ case BEST:
+ c->options |= MIPS_CPU_NAN_2008 | MIPS_CPU_NAN_LEGACY;
+ break;
case LEGACY:
c->options |= MIPS_CPU_NAN_LEGACY;
c->fpu_msk31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008;
@@ -200,8 +206,13 @@ static void cpu_set_nofpu_2008(struct cp
*/
static void cpu_set_nan_2008(struct cpuinfo_mips *c)
{
+ mips_default_ieee754_relaxed = ieee754 == RELAXED;
+ mips_accept_ieee754_relaxed = ieee754 != STRICTEST;
+
switch (ieee754) {
+ case STRICTEST:
case STRICT:
+ case BEST:
mips_use_nan_legacy = !!cpu_has_nan_legacy;
mips_use_nan_2008 = !!cpu_has_nan_2008;
break;
@@ -224,17 +235,23 @@ static void cpu_set_nan_2008(struct cpui
* IEEE 754 NaN encoding and ABS.fmt/NEG.fmt execution mode override
* settings:
*
- * strict: accept binaries that request a NaN encoding supported by the FPU
- * legacy: only accept legacy-NaN binaries
- * 2008: only accept 2008-NaN binaries
- * relaxed: accept any binaries regardless of whether supported by the FPU
+ * strictest: require binaries to request a NaN encoding supported by the FPU
+ * strict: accept binaries that request a NaN encoding supported by the FPU
+ * best: accept all binaries in full FP emulation, otherwise like "strict"
+ * legacy: only accept legacy-NaN binaries
+ * 2008: only accept 2008-NaN binaries
+ * relaxed: accept any binaries regardless of whether supported by the FPU
*/
static int __init ieee754_setup(char *s)
{
if (!s)
return -1;
+ else if (!strcmp(s, "strictest"))
+ ieee754 = STRICTEST;
else if (!strcmp(s, "strict"))
ieee754 = STRICT;
+ else if (!strcmp(s, "best"))
+ ieee754 = BEST;
else if (!strcmp(s, "legacy"))
ieee754 = LEGACY;
else if (!strcmp(s, "2008"))
Index: linux-sfr-test/arch/mips/kernel/elf.c
===================================================================
--- linux-sfr-test.orig/arch/mips/kernel/elf.c 2015-11-11 02:21:08.163575000 +0000
+++ linux-sfr-test/arch/mips/kernel/elf.c 2015-11-11 12:22:51.861832000 +0000
@@ -17,6 +17,12 @@
bool mips_use_nan_legacy;
bool mips_use_nan_2008;

+/* Whether to select the IEEE Std 754 relaxed compliance mode by default. */
+bool mips_default_ieee754_relaxed;
+
+/* Whether to accept IEEE Std 754 relaxed compliance mode binaries. */
+bool mips_accept_ieee754_relaxed;
+
/* FPU modes */
enum {
FP_FRE,
@@ -81,6 +87,7 @@ int arch_elf_pt_proc(void *_ehdr, void *
struct elf32_phdr *phdr32 = _phdr;
struct elf64_phdr *phdr64 = _phdr;
struct mips_elf_abiflags_v0 abiflags;
+ bool relaxed;
bool elf32;
u32 flags;
int ret;
@@ -131,6 +138,17 @@ int arch_elf_pt_proc(void *_ehdr, void *
else
state->fp_abi = abiflags.fp_abi;

+ /* Record any requested IEEE Std 754 compliance mode. */
+ relaxed = !!(abiflags.flags2 & MIPS_AFL_FLAGS2_RELAXED);
+ if (abiflags.flags1 & MIPS_AFL_FLAGS1_IEEE) {
+ if (is_interp)
+ state->interp_ieee754_relaxed = relaxed;
+ else
+ state->ieee754_relaxed = relaxed;
+ } else if (relaxed) {
+ return -EINVAL;
+ }
+
return 0;
}

@@ -147,6 +165,7 @@ int arch_check_elf(void *_ehdr, bool has
} *iehdr = _interp_ehdr;
struct mode_req prog_req, interp_req;
int fp_abi, interp_fp_abi, abi0, abi1, max_abi;
+ bool relaxed;
bool elf32;
u32 flags;

@@ -155,23 +174,31 @@ int arch_check_elf(void *_ehdr, bool has

/*
* Determine the NaN personality, reject the binary if not allowed.
- * Also ensure that any interpreter matches the executable.
+ * Check hardware support if strict conformance requested by the
+ * binary, otherwise respect any kernel override. Also ensure that
+ * any interpreter matches the executable.
*/
+ relaxed = state->ieee754_relaxed;
+ if (relaxed && !mips_accept_ieee754_relaxed)
+ return -ENOEXEC;
if (flags & EF_MIPS_NAN2008) {
- if (mips_use_nan_2008)
- state->nan_2008 = 1;
+ if (relaxed || mips_use_nan_2008)
+ state->nan_2008 = mips_use_nan_2008;
else
return -ENOEXEC;
} else {
- if (mips_use_nan_legacy)
- state->nan_2008 = 0;
+ if (relaxed || mips_use_nan_legacy)
+ state->nan_2008 = !mips_use_nan_legacy;
else
return -ENOEXEC;
}
- if (has_interpreter) {
+ if (has_interpreter && !relaxed) {
bool ielf32;
u32 iflags;

+ if (state->interp_ieee754_relaxed)
+ return -ELIBBAD;
+
ielf32 = iehdr->e32.e_ident[EI_CLASS] == ELFCLASS32;
iflags = ielf32 ? iehdr->e32.e_flags : iehdr->e64.e_flags;

@@ -312,6 +339,11 @@ void mips_set_personality_nan(struct arc
struct cpuinfo_mips *c = &boot_cpu_data;
struct task_struct *t = current;

+ if (state->ieee754_relaxed)
+ set_thread_flag(TIF_IEEE754_RELAXED);
+ else
+ clear_thread_flag(TIF_IEEE754_RELAXED);
+
t->thread.fpu.fcr31 = c->fpu_csr31;
switch (state->nan_2008) {
case 0:
Index: linux-sfr-test/arch/mips/math-emu/ieee754dp.c
===================================================================
--- linux-sfr-test.orig/arch/mips/math-emu/ieee754dp.c 2015-11-11 02:21:08.165573000 +0000
+++ linux-sfr-test/arch/mips/math-emu/ieee754dp.c 2015-11-11 12:22:51.884826000 +0000
@@ -53,7 +53,8 @@ union ieee754dp __cold ieee754dp_nanxcpt
{
assert(ieee754dp_issnan(r));

- ieee754_setcx(IEEE754_INVALID_OPERATION);
+ if (!test_thread_flag(TIF_IEEE754_RELAXED))
+ ieee754_setcx(IEEE754_INVALID_OPERATION);
if (ieee754_csr.nan2008) {
DPMANT(r) |= DP_MBIT(DP_FBITS - 1);
} else {
Index: linux-sfr-test/arch/mips/math-emu/ieee754sp.c
===================================================================
--- linux-sfr-test.orig/arch/mips/math-emu/ieee754sp.c 2015-11-11 02:21:08.167574000 +0000
+++ linux-sfr-test/arch/mips/math-emu/ieee754sp.c 2015-11-11 12:22:51.887824000 +0000
@@ -53,7 +53,8 @@ union ieee754sp __cold ieee754sp_nanxcpt
{
assert(ieee754sp_issnan(r));

- ieee754_setcx(IEEE754_INVALID_OPERATION);
+ if (!test_thread_flag(TIF_IEEE754_RELAXED))
+ ieee754_setcx(IEEE754_INVALID_OPERATION);
if (ieee754_csr.nan2008) {
SPMANT(r) |= SP_MBIT(SP_FBITS - 1);
} else {

2015-11-16 14:35:26

by Maciej W. Rozycki

[permalink] [raw]
Subject: [RFC PATCH 4/4] prctl: Add MIPS IEEE Std 754 compliance mode switching

Implement the prctl(2) interface for IEEE Std 754 NaN interlinking, as
per "MIPS ABI Extension for IEEE Std 754 Non-Compliant Interlinking"
<https://dmz-portal.mips.com/wiki/MIPS_ABI_-_NaN_Interlinking>:

* interpret the PR_SET_IEEE754_MODE request,

* accept or reject the new mode requested according to FP hardware or
emulator capabilities and any `ieee754=' kernel parameter in effect,

* set the values of the FCSR ABS2008 and NAN2008 bits according to the
NaN encoding requested, either PR_IEEE754_MODE_NAN_LEGACY or
PR_IEEE754_MODE_NAN_2008, if writable,

* on success return bits 31:24 of the auxiliary vector's AT_FLAGS value
corresponding to the new mode in effect, in bits 7:0 of the result.

Signed-off-by: Maciej W. Rozycki <[email protected]>
---
linux-mips-nan-interlink-prctl.diff
Index: linux-sfr-test/arch/mips/include/asm/processor.h
===================================================================
--- linux-sfr-test.orig/arch/mips/include/asm/processor.h 2015-11-11 12:34:46.131650000 +0000
+++ linux-sfr-test/arch/mips/include/asm/processor.h 2015-11-11 13:14:09.208745000 +0000
@@ -402,4 +402,13 @@ extern int mips_set_process_fp_mode(stru
#define GET_FP_MODE(task) mips_get_process_fp_mode(task)
#define SET_FP_MODE(task,value) mips_set_process_fp_mode(task, value)

+/*
+ * Likewise the PR_SET_IEEE754_MODE option.
+ */
+extern int mips_set_process_ieee754_mode(struct task_struct *task,
+ unsigned int mode, unsigned int what);
+
+#define SET_IEEE754_MODE(task, mode, what) \
+ mips_set_process_ieee754_mode((task), (mode), (what))
+
#endif /* _ASM_PROCESSOR_H */
Index: linux-sfr-test/arch/mips/kernel/process.c
===================================================================
--- linux-sfr-test.orig/arch/mips/kernel/process.c 2015-11-11 12:34:46.154646000 +0000
+++ linux-sfr-test/arch/mips/kernel/process.c 2015-11-11 13:17:14.564231000 +0000
@@ -9,6 +9,7 @@
* Copyright (C) 2004 Thiemo Seufer
* Copyright (C) 2013 Imagination Technologies Ltd.
*/
+#include <linux/elf.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/tick.h>
@@ -39,7 +40,6 @@
#include <asm/reg.h>
#include <asm/uaccess.h>
#include <asm/io.h>
-#include <asm/elf.h>
#include <asm/isadep.h>
#include <asm/inst.h>
#include <asm/stacktrace.h>
@@ -682,3 +682,76 @@ int mips_set_process_fp_mode(struct task

return 0;
}
+
+/*
+ * Set the process's IEEE 754 compliance mode according to MODE, either
+ * strict or relaxed, affecting WHAT, either legacy or 2008 NaN. On
+ * success return an updated bit pattern as in bits 31:24 of the value
+ * of of of the AT_FLAGS auxiliary vector entry upon program startup,
+ * shifted into bits 7:0 of the result.
+ */
+int mips_set_process_ieee754_mode(struct task_struct *task,
+ unsigned int mode, unsigned int what)
+{
+ struct cpuinfo_mips *c = &boot_cpu_data;
+ struct task_struct *t;
+ bool nan_2008;
+ bool relaxed;
+
+ switch (mode) {
+ case PR_IEEE754_MODE_LEGACY:
+ relaxed = mips_default_ieee754_relaxed;
+ break;
+ case PR_IEEE754_MODE_STRICT:
+ relaxed = false;
+ break;
+ case PR_IEEE754_MODE_RELAXED:
+ if (mips_accept_ieee754_relaxed)
+ relaxed = true;
+ else
+ return -EOPNOTSUPP;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (what) {
+ case PR_IEEE754_MODE_NAN_LEGACY:
+ if (relaxed || mips_use_nan_legacy)
+ nan_2008 = false;
+ else
+ return -ENXIO;
+ break;
+ case PR_IEEE754_MODE_NAN_2008:
+ if (relaxed || mips_use_nan_2008)
+ nan_2008 = true;
+ else
+ return -ENXIO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mips_get_fp_context(task);
+
+ for_each_thread(task, t) {
+ if (relaxed)
+ set_thread_flag(TIF_IEEE754_RELAXED);
+ else
+ clear_thread_flag(TIF_IEEE754_RELAXED);
+ if (nan_2008) {
+ if (!(c->fpu_msk31 & FPU_CSR_NAN2008))
+ t->thread.fpu.fcr31 |= FPU_CSR_NAN2008;
+ if (!(c->fpu_msk31 & FPU_CSR_ABS2008))
+ t->thread.fpu.fcr31 |= FPU_CSR_ABS2008;
+ } else {
+ if (!(c->fpu_msk31 & FPU_CSR_NAN2008))
+ t->thread.fpu.fcr31 &= ~FPU_CSR_NAN2008;
+ if (!(c->fpu_msk31 & FPU_CSR_ABS2008))
+ t->thread.fpu.fcr31 &= ~FPU_CSR_ABS2008;
+ }
+ }
+
+ mips_put_fp_context(task);
+
+ return ELF_FLAGS >> AV_FLAGS_SYSTEM_SHIFT;
+}
Index: linux-sfr-test/include/uapi/linux/prctl.h
===================================================================
--- linux-sfr-test.orig/include/uapi/linux/prctl.h 2015-11-11 12:34:46.157647000 +0000
+++ linux-sfr-test/include/uapi/linux/prctl.h 2015-11-11 13:14:09.286746000 +0000
@@ -197,4 +197,16 @@ struct prctl_mm_map {
# define PR_CAP_AMBIENT_LOWER 3
# define PR_CAP_AMBIENT_CLEAR_ALL 4

+/*
+ * Control MIPS IEEE 754 compliance modes.
+ */
+#define PR_SET_IEEE754_MODE 48
+
+# define PR_IEEE754_MODE_LEGACY 0 /* Legacy mode. */
+# define PR_IEEE754_MODE_STRICT 1 /* Strict mode. */
+# define PR_IEEE754_MODE_RELAXED 2 /* Relaxed mode. */
+
+# define PR_IEEE754_MODE_NAN_LEGACY 0 /* Set legacy NaN encoding. */
+# define PR_IEEE754_MODE_NAN_2008 1 /* Set 2008 NaN encoding. */
+
#endif /* _LINUX_PRCTL_H */
Index: linux-sfr-test/kernel/sys.c
===================================================================
--- linux-sfr-test.orig/kernel/sys.c 2015-11-11 12:34:46.159650000 +0000
+++ linux-sfr-test/kernel/sys.c 2015-11-11 13:14:09.342744000 +0000
@@ -103,6 +103,9 @@
#ifndef SET_FP_MODE
# define SET_FP_MODE(a,b) (-EINVAL)
#endif
+#ifndef SET_IEEE754_MODE
+# define SET_IEEE754_MODE(a, b, c) (-EINVAL)
+#endif

/*
* this is where the system-wide overflow UID and GID are defined, for
@@ -2266,6 +2269,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsi
case PR_GET_FP_MODE:
error = GET_FP_MODE(me);
break;
+ case PR_SET_IEEE754_MODE:
+ error = SET_IEEE754_MODE(me, arg2, arg3);
+ break;
default:
error = -EINVAL;
break;