summaryrefslogtreecommitdiff
path: root/arch/alpha
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-07 17:23:00 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-07 17:23:00 +0200
commitdcf5b8a7ae4e3875878529597c05f8cac4121515 (patch)
tree0d86567a8feacb35b2a62c1ba6cf6c2fe3be65de /arch/alpha
parent864c971e923f55d3ff5ac3ebc87aab8108d30c8a (diff)
parent7cfc41f8e80f11ffa8382ed1a505154ceffb79c7 (diff)
Merge v6.18.50linux-rolling-lts
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'arch/alpha')
-rw-r--r--arch/alpha/include/uapi/asm/fpu.h8
-rw-r--r--arch/alpha/kernel/pci-sysfs.c6
-rw-r--r--arch/alpha/kernel/sys_marvel.c25
-rw-r--r--arch/alpha/kernel/traps.c6
-rw-r--r--arch/alpha/math-emu/math.c88
5 files changed, 101 insertions, 32 deletions
diff --git a/arch/alpha/include/uapi/asm/fpu.h b/arch/alpha/include/uapi/asm/fpu.h
index cea9eafa056f..d28dc36786e2 100644
--- a/arch/alpha/include/uapi/asm/fpu.h
+++ b/arch/alpha/include/uapi/asm/fpu.h
@@ -101,7 +101,12 @@ ieee_swcr_to_fpcr(unsigned long sw)
| IEEE_TRAP_ENABLE_OVF)) << 48;
fp |= (~sw & (IEEE_TRAP_ENABLE_UNF | IEEE_TRAP_ENABLE_INE)) << 57;
fp |= (sw & IEEE_MAP_UMZ ? FPCR_UNDZ | FPCR_UNFD : 0);
- fp |= (~sw & IEEE_TRAP_ENABLE_DNO) << 41;
+ /*
+ * Disable denormal operand traps only when denormal inputs are to be
+ * flushed to zero. Otherwise they must keep trapping, so that /S
+ * instructions reach the kernel emulation handler.
+ */
+ fp |= (sw & IEEE_MAP_DMZ ? FPCR_DNOD : 0);
return fp;
}
@@ -116,7 +121,6 @@ ieee_fpcr_to_swcr(unsigned long fp)
| IEEE_TRAP_ENABLE_OVF);
sw |= (~fp >> 57) & (IEEE_TRAP_ENABLE_UNF | IEEE_TRAP_ENABLE_INE);
sw |= (fp >> 47) & IEEE_MAP_UMZ;
- sw |= (~fp >> 41) & IEEE_TRAP_ENABLE_DNO;
return sw;
}
diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c
index 21833d1c867d..2e9870ee21c6 100644
--- a/arch/alpha/kernel/pci-sysfs.c
+++ b/arch/alpha/kernel/pci-sysfs.c
@@ -364,17 +364,17 @@ int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
switch(size) {
case 1:
- outb(port, val);
+ outb(val, port);
return 1;
case 2:
if (port & 1)
return -EINVAL;
- outw(port, val);
+ outw(val, port);
return 2;
case 4:
if (port & 3)
return -EINVAL;
- outl(port, val);
+ outl(val, port);
return 4;
}
return -EINVAL;
diff --git a/arch/alpha/kernel/sys_marvel.c b/arch/alpha/kernel/sys_marvel.c
index 1f99b03effc2..a37707e05e34 100644
--- a/arch/alpha/kernel/sys_marvel.c
+++ b/arch/alpha/kernel/sys_marvel.c
@@ -263,6 +263,18 @@ init_io7_irqs(struct io7 *io7,
*/
printk(" Interrupts reported to CPU at PE %u\n", boot_cpuid);
+ /* Set up the lsi irqs. */
+ for (i = 0; i < 128; ++i) {
+ irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
+ irq_set_status_flags(base + i, IRQ_LEVEL);
+ }
+
+ /* Set up the msi irqs. */
+ for (i = 128; i < (128 + 512); ++i) {
+ irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
+ irq_set_status_flags(base + i, IRQ_LEVEL);
+ }
+
raw_spin_lock(&io7->irq_lock);
/* set up the error irqs */
@@ -272,12 +284,6 @@ init_io7_irqs(struct io7 *io7,
io7_redirect_irq(io7, &io7->csrs->STV_CTL.csr, boot_cpuid);
io7_redirect_irq(io7, &io7->csrs->HEI_CTL.csr, boot_cpuid);
- /* Set up the lsi irqs. */
- for (i = 0; i < 128; ++i) {
- irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
- irq_set_status_flags(i, IRQ_LEVEL);
- }
-
/* Disable the implemented irqs in hardware. */
for (i = 0; i < 0x60; ++i)
init_one_io7_lsi(io7, i, boot_cpuid);
@@ -285,13 +291,6 @@ init_io7_irqs(struct io7 *io7,
init_one_io7_lsi(io7, 0x74, boot_cpuid);
init_one_io7_lsi(io7, 0x75, boot_cpuid);
-
- /* Set up the msi irqs. */
- for (i = 128; i < (128 + 512); ++i) {
- irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
- irq_set_status_flags(i, IRQ_LEVEL);
- }
-
for (i = 0; i < 16; ++i)
init_one_io7_msi(io7, i, boot_cpuid);
diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c
index 7004397937cf..7cd20e5f9ee0 100644
--- a/arch/alpha/kernel/traps.c
+++ b/arch/alpha/kernel/traps.c
@@ -166,12 +166,12 @@ static long dummy_emul(void) { return 0; }
long (*alpha_fp_emul_imprecise)(struct pt_regs *regs, unsigned long writemask)
= (void *)dummy_emul;
EXPORT_SYMBOL_GPL(alpha_fp_emul_imprecise);
-long (*alpha_fp_emul) (unsigned long pc)
+long (*alpha_fp_emul) (unsigned long pc, unsigned long summary)
= (void *)dummy_emul;
EXPORT_SYMBOL_GPL(alpha_fp_emul);
#else
long alpha_fp_emul_imprecise(struct pt_regs *regs, unsigned long writemask);
-long alpha_fp_emul (unsigned long pc);
+long alpha_fp_emul (unsigned long pc, unsigned long summary);
#endif
asmlinkage void
@@ -185,7 +185,7 @@ do_entArith(unsigned long summary, unsigned long write_mask,
emulate the instruction. If the processor supports
precise exceptions, we don't have to search. */
if (!amask(AMASK_PRECISE_TRAP))
- si_code = alpha_fp_emul(regs->pc - 4);
+ si_code = alpha_fp_emul(regs->pc - 4, summary);
else
si_code = alpha_fp_emul_imprecise(regs, write_mask);
if (si_code == 0)
diff --git a/arch/alpha/math-emu/math.c b/arch/alpha/math-emu/math.c
index 68d420bfd3c0..e3f2df3729e3 100644
--- a/arch/alpha/math-emu/math.c
+++ b/arch/alpha/math-emu/math.c
@@ -52,13 +52,13 @@ MODULE_DESCRIPTION("FP Software completion module");
MODULE_LICENSE("GPL v2");
extern long (*alpha_fp_emul_imprecise)(struct pt_regs *, unsigned long);
-extern long (*alpha_fp_emul) (unsigned long pc);
+extern long (*alpha_fp_emul) (unsigned long pc, unsigned long summary);
static long (*save_emul_imprecise)(struct pt_regs *, unsigned long);
-static long (*save_emul) (unsigned long pc);
+static long (*save_emul) (unsigned long pc, unsigned long summary);
long do_alpha_fp_emul_imprecise(struct pt_regs *, unsigned long);
-long do_alpha_fp_emul(unsigned long);
+long do_alpha_fp_emul(unsigned long, unsigned long);
static int alpha_fp_emul_init_module(void)
{
@@ -86,7 +86,22 @@ module_exit(alpha_fp_emul_cleanup_module);
/*
- * Emulate the floating point instruction at address PC. Returns -1 if the
+ * Exception bits of the exception summary register (EXC_SUM). Bit 0 is the
+ * software completion bit; bits 1 through 5 report the exceptions the
+ * hardware attributed to the trapping instruction, and lie at the same
+ * positions as the corresponding IEEE_TRAP_ENABLE_* bits.
+ */
+#define EXC_SUM_INV (1UL << 1)
+#define EXC_SUM_DZE (1UL << 2)
+#define EXC_SUM_OVF (1UL << 3)
+#define EXC_SUM_UNF (1UL << 4)
+#define EXC_SUM_INE (1UL << 5)
+#define EXC_SUM_MASK (EXC_SUM_INV | EXC_SUM_DZE | EXC_SUM_OVF \
+ | EXC_SUM_UNF | EXC_SUM_INE)
+
+/*
+ * Emulate the floating point instruction at address PC. SUMMARY is the
+ * exception summary register the trap was delivered with. Returns -1 if the
* instruction to be emulated is illegal (such as with the opDEC trap), else
* the SI_CODE for a SIGFPE signal, else 0 if everything's ok.
*
@@ -95,7 +110,7 @@ module_exit(alpha_fp_emul_cleanup_module);
* stick the result of the operation into the appropriate register.
*/
long
-alpha_fp_emul (unsigned long pc)
+alpha_fp_emul (unsigned long pc, unsigned long summary)
{
FP_DECL_EX;
FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
@@ -300,12 +315,56 @@ done:
swcr |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT);
current_thread_info()->ieee_state
|= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT);
+ }
- /* Update hardware control register. */
- fpcr &= (~FPCR_MASK | FPCR_DYN_MASK);
- fpcr |= ieee_swcr_to_fpcr(swcr);
- wrfpcr(fpcr);
+ /*
+ * EV6 records exception status bits in the FPCR before delivering the
+ * software completion trap, and swcr_update_status() above merged them
+ * into SWCR. Some can be wrong for the instruction we just emulated:
+ * a CVTTS of a value exactly representable as a subnormal sets FPCR_UNF
+ * even though the result is exact. Clear the exceptions the trap
+ * reported but that soft-fp did not raise.
+ */
+ if (implver() == IMPLVER_EV6) {
+ unsigned long spurious = summary & EXC_SUM_MASK;
+
+ if (spurious & (EXC_SUM_UNF | EXC_SUM_OVF)) {
+ /*
+ * EXC_SUM reports only the underflow or overflow,
+ * but the hardware sets INE alongside it in the FPCR.
+ */
+ spurious |= EXC_SUM_INE;
+ } else if (!spurious) {
+ /*
+ * No exception reported, so this was a denormal
+ * operand trap, for which INE and UNF can be
+ * fabricated as well.
+ */
+ spurious = EXC_SUM_INE | EXC_SUM_UNF;
+ }
+ /*
+ * Never clear an exception software has confirmed. Every
+ * instruction that genuinely raises one traps for software
+ * completion and is recorded in ieee_state above, so a bit
+ * found there -- including one just set from _fex -- belongs
+ * to this or an earlier instruction and must survive.
+ */
+ spurious &= ~(current_thread_info()->ieee_state
+ >> IEEE_STATUS_TO_EXCSUM_SHIFT);
+
+ swcr &= ~(spurious << IEEE_STATUS_TO_EXCSUM_SHIFT);
+ }
+
+ /*
+ * Update hardware control register. This has to happen even when
+ * soft-fp raised nothing, to clear any fabricated bits.
+ */
+ fpcr &= (~FPCR_MASK | FPCR_DYN_MASK);
+ fpcr |= ieee_swcr_to_fpcr(swcr);
+ wrfpcr(fpcr);
+
+ if (_fex) {
/* Do we generate a signal? */
_fex = _fex & swcr & IEEE_TRAP_ENABLE_MASK;
si_code = 0;
@@ -387,9 +446,16 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)
break;
}
if (!write_mask) {
- /* Re-execute insns in the trap-shadow. */
+ /*
+ * Re-execute insns in the trap-shadow. Pass no
+ * exception summary: it describes the trap, which
+ * was taken anywhere in the shadow, and so is not
+ * attribution for this instruction. Nothing is
+ * lost, since only EV6 -- which traps precisely and
+ * never comes this way -- needs it.
+ */
regs->pc = trigger_pc + 4;
- si_code = alpha_fp_emul(trigger_pc);
+ si_code = alpha_fp_emul(trigger_pc, 0);
goto egress;
}
trigger_pc -= 4;