Merge tag 'alpha-for-v7.3-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/lindholm/alpha

Pull alpha updates from Magnus Lindholm:
 "This contains two fixes for Alpha floating-point exception handling,
  two clang-related fixes, the preparatory changes from the
  generic-entry series, an interrupt-entry lockdep fix, two Marvel/EV7
  IRQ fixes, an RTC fix, and one header cleanup.

  The generic-entry preparation adds regset-based ptrace and core dumps,
  ARCH_STACKWALK and lockdep hardirq-state tracking. These changes are
  useful independently and enable previously missing debugging
  facilities on Alpha.

  The final patch switching Alpha to GENERIC_ENTRY is intentionally not
  included in this pull request. I am deferring that change to allow
  further testing and to reduce the risk of conflicts with ongoing
  entry-path work elsewhere in the kernel"

* tag 'alpha-for-v7.3-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/lindholm/alpha:
  alpha: read $gp and $sp explicitly for clang
  alpha: pass -Wa,-mev6 only when using GNU as
  alpha: annotate hardirqs-off on IPL 7 interrupt entry
  alpha: run the remote RTC access in a worker, not an IPI callback
  alpha: don't leak hardware-fabricated FP exception bits to user space
  alpha: fix ieee_swcr_to_fpcr setting FPCR_DNOD unconditionally
  alpha: enable lockdep hardirq state tracking
  alpha: use raw spinlocks for low-level platform locks
  alpha: provide ftrace return address support for lockdep
  alpha: make irqflags helpers operate on IPL state
  alpha: add ARCH_STACKWALK-based stacktrace support
  alpha: enable regset-based ptrace and core dumps
  alpha: marvel: Fix lock ordering in init_io7_irqs()
  alpha: marvel: Fix irq_set_status_flags to use correct IRQ number
  alpha: remove unnecessary architecture-specific <asm/device.h>
This commit is contained in:
Linus Torvalds
2026-08-18 19:33:32 -07:00
30 changed files with 684 additions and 176 deletions

View File

@@ -6,7 +6,7 @@
-----------------------
| arch |status|
-----------------------
| alpha: | TODO |
| alpha: | ok |
| arc: | ok |
| arm: | ok |
| arm64: | ok |

View File

@@ -6,7 +6,7 @@
-----------------------
| arch |status|
-----------------------
| alpha: | TODO |
| alpha: | ok |
| arc: | ok |
| arm: | ok |
| arm64: | ok |

View File

@@ -33,16 +33,20 @@ config ALPHA
select HAVE_ARCH_AUDITSYSCALL
select HAVE_ARCH_SECCOMP
select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ARCH_TRACEHOOK
select HAVE_MOD_ARCH_SPECIFIC
select LOCK_MM_AND_FIND_VMA
select MODULES_USE_ELF_RELA
select ODD_RT_SIGACTION
select OLD_SIGSUSPEND
select ARCH_STACKWALK
select CPU_NO_EFFICIENT_FFS if !ALPHA_EV67
select MMU_GATHER_NO_RANGE
select MMU_GATHER_RCU_TABLE_FREE
select SPARSEMEM_EXTREME if SPARSEMEM
select ZONE_DMA
select TRACE_IRQFLAGS_SUPPORT
select ARCH_WANT_FRAME_POINTERS
help
The Alpha is a 64-bit general-purpose processor designed and
marketed by the Digital Equipment Corporation of blessed memory,
@@ -79,6 +83,12 @@ config PGTABLE_LEVELS
config AUDIT_ARCH
bool
config STACKTRACE_SUPPORT
def_bool y
config LOCKDEP_SUPPORT
def_bool y
menu "System setup"
choice

View File

@@ -27,10 +27,16 @@ cpuflags-$(CONFIG_ALPHA_GENERIC) := -mcpu=ev56 -mtune=ev6
cflags-y += $(cpuflags-y)
KBUILD_CFLAGS += $(cflags-y)
# For TSUNAMI, we must have the assembler not emulate our instructions.
# The same is true for IRONGATE, POLARIS, PYXIS.
# BWX is most important, but we don't really want any emulation ever.
KBUILD_CFLAGS += $(cflags-y) -Wa,-mev6
# Only gas emulates instructions the target does not implement, and only gas
# understands -mev6. LLVM's integrated assembler never emulates.
ifdef CONFIG_AS_IS_GNU
KBUILD_CFLAGS += -Wa,-mev6
endif
libs-y += arch/alpha/lib/

View File

@@ -1,6 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Arch specific extensions to struct device
*/
#include <asm-generic/device.h>

View File

@@ -53,6 +53,7 @@
#define EF_ALPHA_32BIT 1 /* All addresses are below 2GB */
#define CORE_DUMP_USE_REGSET 1
/*
* ELF register definitions..
*/

View File

@@ -1 +1,29 @@
/* empty */
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_ALPHA_FTRACE_H
#define _ASM_ALPHA_FTRACE_H
#ifdef CONFIG_FRAME_POINTER
static void *alpha_ftrace_return_address0(void)
noinline notrace;
static void *alpha_ftrace_return_address0(void)
{
return __builtin_return_address(0);
}
#define ftrace_return_address0 alpha_ftrace_return_address0()
/*
* __builtin_return_address() requires a constant integer argument.
* Keep this as a macro so the value is seen at the callsite.
*/
#define ftrace_return_address(n) __builtin_return_address(n)
#else /* !CONFIG_FRAME_POINTER */
#define ftrace_return_address0 0UL
#define ftrace_return_address(n) ((void)(n), 0UL)
#endif /* CONFIG_FRAME_POINTER */
#endif /* _ASM_ALPHA_FTRACE_H */

View File

@@ -26,7 +26,7 @@ extern int __min_ipl;
static inline unsigned long arch_local_save_flags(void)
{
return rdps();
return getipl();
}
static inline void arch_local_irq_disable(void)
@@ -51,13 +51,13 @@ static inline void arch_local_irq_enable(void)
static inline void arch_local_irq_restore(unsigned long flags)
{
barrier();
setipl(flags);
setipl(flags & 7);
barrier();
}
static inline bool arch_irqs_disabled_flags(unsigned long flags)
{
return flags == IPL_MAX;
return (flags & 7) == IPL_MAX;
}
static inline bool arch_irqs_disabled(void)

View File

@@ -24,4 +24,11 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
return regs->r0;
}
/* Helpers for working with the user stack pointer */
static inline unsigned long user_stack_pointer(struct pt_regs *regs)
{
/* Valid for user-mode regs */
return regs->usp;
}
#endif

View File

@@ -19,6 +19,13 @@ static inline long syscall_get_return_value(struct task_struct *task,
return regs->r19 ? -(long)regs->r0 : (long)regs->r0;
}
static inline long syscall_get_error(struct task_struct *task,
struct pt_regs *regs)
{
return regs->r19 ? -(long)regs->r0 : 0;
}
/*
* Alpha syscall ABI / kernel conventions:
* - PAL provides syscall number in r0 on entry.

View File

@@ -66,6 +66,7 @@ register unsigned long *current_stack_pointer __asm__ ("$30");
#define TIF_SYSCALL_AUDIT 4 /* syscall audit active */
#define TIF_NOTIFY_SIGNAL 5 /* signal notifications exist */
#define TIF_SECCOMP 6 /* seccomp syscall filtering active */
#define TIF_SYSCALL_TRACEPOINT 7 /* syscall tracepoint instrumentation */
#define TIF_DIE_IF_KERNEL 9 /* dik recursion lock */
#define TIF_MEMDIE 13 /* is terminating due to OOM killer */
#define TIF_POLLING_NRFLAG 14 /* idle is polling for TIF_NEED_RESCHED */
@@ -78,6 +79,7 @@ register unsigned long *current_stack_pointer __asm__ ("$30");
#define _TIF_NOTIFY_SIGNAL (1<<TIF_NOTIFY_SIGNAL)
#define _TIF_SECCOMP (1<<TIF_SECCOMP)
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT)
/*
* Work to do on syscall entry (in entry.S).
@@ -85,9 +87,10 @@ register unsigned long *current_stack_pointer __asm__ ("$30");
* with the mask used before branching to syscall_trace_enter().
*/
#ifdef CONFIG_AUDITSYSCALL
# define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP)
# define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP \
| _TIF_SYSCALL_TRACEPOINT)
#else
# define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SECCOMP)
# define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT)
#endif
/* Work to do on interrupt/exception return. */

View File

@@ -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;
}

View File

@@ -43,7 +43,7 @@ struct pt_regs {
unsigned long trap_a1;
unsigned long trap_a2;
/* This makes the stack 16-byte aligned as GCC expects */
unsigned long __pad0;
unsigned long usp;
/* These are saved by PAL-code: */
unsigned long ps;
unsigned long pc;

View File

@@ -9,7 +9,8 @@ ccflags-y := -Wno-sign-compare
obj-y := head.o entry.o traps.o process.o osf_sys.o irq.o \
irq_alpha.o signal.o setup.o ptrace.o time.o \
systbls.o err_common.o io.o bugs.o termios.o
systbls.o err_common.o io.o bugs.o termios.o \
stacktrace.o
obj-$(CONFIG_VGA_HOSE) += console.o
obj-$(CONFIG_SMP) += smp.o

View File

@@ -29,4 +29,5 @@ static void __used foo(void)
DEFINE(HAE_CACHE, offsetof(struct alpha_machine_vector, hae_cache));
DEFINE(HAE_REG, offsetof(struct alpha_machine_vector, hae_register));
DEFINE(PT_REGS_USP, offsetof(struct pt_regs, usp));
}

View File

@@ -93,6 +93,19 @@
4:
.endm
.macro LOCKDEP_HARDIRQS_ON_RESTORE
#ifdef CONFIG_PROVE_LOCKING
/* a0 = saved PS */
ldq $16, SP_OFF($sp)
/* a1 = callsite IP for lockdep */
lda $17, 1f
jsr $26, lockdep_on_restore
ldgp $gp, 0($26)
1:
#endif
.endm
/*
* This defines the normal kernel pt-regs layout.
@@ -427,6 +440,7 @@ CFI_START_OSF_FRAME entUna
.cfi_restore $28
.cfi_restore $29
.cfi_adjust_cfa_offset -256
LOCKDEP_HARDIRQS_ON_RESTORE
call_pal PAL_rti
.align 4
@@ -520,6 +534,12 @@ entSys:
ldq $1, 0($sp) /* syscall nr from saved r0 */
stq $1, 8($sp) /* regs->r1 = shadow syscall nr */
stq $1, 16($sp) /* regs->r2 = restart syscall nr */
/* Syscalls always enter from user mode: snapshot USP into pt_regs->usp */
mov $0, $8
call_pal PAL_rdusp
stq $0, PT_REGS_USP($sp)
mov $8, $0
lda $8, 0x3fff
bic $sp, $8, $8
@@ -535,15 +555,10 @@ entSys:
.cfi_rel_offset $16, SP_OFF+24
.cfi_rel_offset $17, SP_OFF+32
.cfi_rel_offset $18, SP_OFF+40
#ifdef CONFIG_AUDITSYSCALL
lda $6, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP
lda $6, _TIF_SYSCALL_WORK
and $3, $6, $3
bne $3, strace
#else
lda $6, _TIF_SYSCALL_TRACE | _TIF_SECCOMP
and $3, $6, $3
bne $3, strace
#endif
beq $4, 1f
ldq $27, 0($5)
1: ldq $0, 8($sp) /* syscall nr shadow (regs->r1) */
@@ -576,6 +591,7 @@ restore_all:
bne $3, restore_fpu
restore_other:
.cfi_remember_state
LOCKDEP_HARDIRQS_ON_RESTORE
RESTORE_ALL
call_pal PAL_rti
@@ -621,7 +637,7 @@ $work_resched:
* or got through work_notifysig already. Either case means no syscall
* restarts for us, so let $18 and $19 burn.
*/
jsr $26, schedule
jsr $26, alpha_schedule_user_work
mov 0, $18
br ret_to_user

View File

@@ -41,7 +41,7 @@ EXPORT_SYMBOL(perf_irq);
* The main interrupt entry point.
*/
asmlinkage void
asmlinkage void
do_entInt(unsigned long type, unsigned long vector,
unsigned long la_ptr, struct pt_regs *regs)
{
@@ -52,42 +52,91 @@ do_entInt(unsigned long type, unsigned long vector,
* Note that there is no matching local_irq_enable() due to
* severe problems with RTI at IPL0 and some MILO PALcode
* (namely LX164).
*
* PALcode has already raised PS.IPL to the level of the interrupt
* being delivered. For an IPL 7 entry - a machine check or a system
* event - that is IPL_MAX, which is what arch_irqs_disabled() tests
* for, so local_irq_disable() would decide interrupts were already
* off and skip trace_hardirqs_off(). lockdep would then spend the
* whole handler believing interrupts are enabled. Drive the
* annotation from lockdep's own state rather than the hardware IPL.
*/
local_irq_disable();
raw_local_irq_disable();
if (lockdep_hardirqs_enabled())
trace_hardirqs_off();
old_regs = set_irq_regs(regs);
switch (type) {
case 0:
#ifdef CONFIG_SMP
irq_enter();
handle_ipi(regs);
return;
irq_exit();
break;
#else
irq_err_count++;
printk(KERN_CRIT "Interprocessor interrupt? "
"You must be kidding!\n");
#endif
pr_crit("Interprocessor interrupt? You must be kidding!\n");
break;
#endif
case 1:
old_regs = set_irq_regs(regs);
/* handle_irq() already does irq_enter()/irq_exit() */
handle_irq(RTC_IRQ);
set_irq_regs(old_regs);
return;
break;
case 2:
old_regs = set_irq_regs(regs);
irq_enter();
alpha_mv.machine_check(vector, la_ptr);
set_irq_regs(old_regs);
return;
irq_exit();
break;
case 3:
old_regs = set_irq_regs(regs);
irq_enter();
alpha_mv.device_interrupt(vector);
set_irq_regs(old_regs);
return;
irq_exit();
break;
case 4:
irq_enter();
perf_irq(la_ptr, regs);
return;
irq_exit();
break;
default:
printk(KERN_CRIT "Hardware intr %ld %lx? Huh?\n",
type, vector);
pr_crit("Hardware intr %lu %lx? Huh?\n", type, vector);
pr_crit("PC = %016lx PS=%04lx\n", regs->pc, regs->ps);
break;
}
printk(KERN_CRIT "PC = %016lx PS=%04lx\n", regs->pc, regs->ps);
set_irq_regs(old_regs);
/*
* Intentionally no local_irq_enable(): Alpha historically avoids
* enabling at IPL0 here due to PAL/RTI issues (LX164/MILO note).
*/
}
void notrace lockdep_on_restore(unsigned long ps,
unsigned long ip)
{
#ifdef CONFIG_PROVE_LOCKING
/* Restoring IPL==7 means interrupts remain disabled. */
if ((ps & 7) == 7)
return;
/*
* If hardware IRQs are already enabled here, then emitting a
* hardirqs-on transition is redundant.
*/
if (!irqs_disabled())
return;
/*
* Only emit the transition if lockdep currently believes
* hardirqs are off.
*/
if (lockdep_hardirqs_enabled())
return;
lockdep_hardirqs_on_prepare();
lockdep_hardirqs_on(ip);
#endif
}
void __init

View File

@@ -22,7 +22,7 @@
/* Note mask bit is true for DISABLED irqs. */
static unsigned int cached_irq_mask = 0xffff;
static DEFINE_SPINLOCK(i8259_irq_lock);
static DEFINE_RAW_SPINLOCK(i8259_irq_lock);
static inline void
i8259_update_irq_hw(unsigned int irq, unsigned long mask)
@@ -36,9 +36,11 @@ i8259_update_irq_hw(unsigned int irq, unsigned long mask)
inline void
i8259a_enable_irq(struct irq_data *d)
{
spin_lock(&i8259_irq_lock);
unsigned long flags;
raw_spin_lock_irqsave(&i8259_irq_lock, flags);
i8259_update_irq_hw(d->irq, cached_irq_mask &= ~(1 << d->irq));
spin_unlock(&i8259_irq_lock);
raw_spin_unlock_irqrestore(&i8259_irq_lock, flags);
}
static inline void
@@ -50,17 +52,20 @@ __i8259a_disable_irq(unsigned int irq)
void
i8259a_disable_irq(struct irq_data *d)
{
spin_lock(&i8259_irq_lock);
unsigned long flags;
raw_spin_lock_irqsave(&i8259_irq_lock, flags);
__i8259a_disable_irq(d->irq);
spin_unlock(&i8259_irq_lock);
raw_spin_unlock_irqrestore(&i8259_irq_lock, flags);
}
void
i8259a_mask_and_ack_irq(struct irq_data *d)
{
unsigned int irq = d->irq;
unsigned long flags;
spin_lock(&i8259_irq_lock);
raw_spin_lock_irqsave(&i8259_irq_lock, flags);
__i8259a_disable_irq(irq);
/* Ack the interrupt making it the lowest priority. */
@@ -69,7 +74,7 @@ i8259a_mask_and_ack_irq(struct irq_data *d)
irq = 2;
}
outb(0xE0 | irq, 0x20); /* ack the master */
spin_unlock(&i8259_irq_lock);
raw_spin_unlock_irqrestore(&i8259_irq_lock, flags);
}
struct irq_chip i8259a_irq_type = {

View File

@@ -173,6 +173,7 @@ extern void do_sigreturn(struct sigcontext __user *);
struct rt_sigframe;
extern void do_rt_sigreturn(struct rt_sigframe __user *);
extern void do_work_pending(struct pt_regs *, unsigned long, unsigned long, unsigned long);
extern void alpha_schedule_user_work(void);
/* traps.c */
extern void dik_show_regs(struct pt_regs *regs, unsigned long *r9_15);
@@ -185,6 +186,9 @@ struct allregs;
extern void do_entUna(void *, unsigned long, unsigned long, struct allregs *);
extern void do_entUnaUser(void __user *, unsigned long, unsigned long, struct pt_regs *);
/* irq_alpha.c */
extern void notrace lockdep_on_restore(unsigned long ps, unsigned long ip);
/* sys_titan.c */
extern void titan_dispatch_irqs(u64);

View File

@@ -24,10 +24,15 @@
#include "proto.h"
#include <linux/uio.h>
#include <linux/regset.h>
#define DEBUG DBG_MEM
#undef DEBUG
#ifndef NT_FPREGSET
#define NT_FPREGSET NT_PRFPREG
#endif
#ifdef DEBUG
enum {
DBG_MEM = (1<<0),
@@ -143,19 +148,163 @@ get_reg(struct task_struct * task, unsigned long regno)
return *get_reg_addr(task, regno);
}
static void alpha_elf_fpregs_get(struct task_struct *target,
elf_fpreg_t *fpregs) /* points to ELF_NFPREG entries */
{
memcpy(fpregs, task_thread_info(target)->fp, sizeof(elf_fpregset_t));
}
static void alpha_elf_fpregs_set(struct task_struct *target,
const elf_fpreg_t *fpregs,
size_t nwords)
{
size_t n = min_t(size_t, nwords, ELF_NFPREG);
memcpy(task_thread_info(target)->fp, fpregs, n * sizeof(elf_fpreg_t));
}
static void alpha_elf_gregs_set(struct task_struct *child,
const elf_greg_t *src,
size_t nwords)
{
struct pt_regs *pt = task_pt_regs(child);
struct thread_info *ti = task_thread_info(child);
struct switch_stack *sw = ((struct switch_stack *)pt) - 1;
/* GPRs r0..r8 live in pt_regs */
if (nwords > 0)
pt->r0 = src[0];
if (nwords > 1)
pt->r1 = src[1];
if (nwords > 2)
pt->r2 = src[2];
if (nwords > 3)
pt->r3 = src[3];
if (nwords > 4)
pt->r4 = src[4];
if (nwords > 5)
pt->r5 = src[5];
if (nwords > 6)
pt->r6 = src[6];
if (nwords > 7)
pt->r7 = src[7];
if (nwords > 8)
pt->r8 = src[8];
/* r9..r15 live in switch_stack */
if (nwords > 9)
sw->r9 = src[9];
if (nwords > 10)
sw->r10 = src[10];
if (nwords > 11)
sw->r11 = src[11];
if (nwords > 12)
sw->r12 = src[12];
if (nwords > 13)
sw->r13 = src[13];
if (nwords > 14)
sw->r14 = src[14];
if (nwords > 15)
sw->r15 = src[15];
/* r16..r28 live in pt_regs */
if (nwords > 16)
pt->r16 = src[16];
if (nwords > 17)
pt->r17 = src[17];
if (nwords > 18)
pt->r18 = src[18];
if (nwords > 19)
pt->r19 = src[19];
if (nwords > 20)
pt->r20 = src[20];
if (nwords > 21)
pt->r21 = src[21];
if (nwords > 22)
pt->r22 = src[22];
if (nwords > 23)
pt->r23 = src[23];
if (nwords > 24)
pt->r24 = src[24];
if (nwords > 25)
pt->r25 = src[25];
if (nwords > 26)
pt->r26 = src[26];
if (nwords > 27)
pt->r27 = src[27];
if (nwords > 28)
pt->r28 = src[28];
/* gp, usp, pc, unique */
if (nwords > 29)
pt->gp = src[29];
if (nwords > 30) {
ti->pcb.usp = src[30];
/*
* If someone ever does this to current (rare), keep the
* hardware usp consistent.
*/
if (child == current)
wrusp(src[30]);
}
if (nwords > 31)
pt->pc = src[31];
if (nwords > 32)
ti->pcb.unique = src[32];
/*
* PTRACE_SETREGSET can be used at a syscall-entry stop to skip the
* syscall by setting the syscall number to -1. The seccomp/ptrace
* selftests use this to synthesize errno returns.
*
* Alpha uses r19/a3 as the error flag, so a skipped syscall with a
* small positive r0 and a clear r19 must be normalized to an error
* return.
*/
if (pt->r1 == (unsigned long)-1 &&
pt->r19 == 0 &&
pt->r0 > 0 &&
pt->r0 < MAX_ERRNO)
pt->r19 = 1;
}
/*
* Write contents of register REGNO in task TASK.
*/
static int
put_reg(struct task_struct *task, unsigned long regno, unsigned long data)
{
struct pt_regs *regs = task_pt_regs(task);
if (regno == 63) {
task_thread_info(task)->ieee_state
= ((task_thread_info(task)->ieee_state & ~IEEE_SW_MASK)
| (data & IEEE_SW_MASK));
data = (data & FPCR_DYN_MASK) | ieee_swcr_to_fpcr(data);
}
*get_reg_addr(task, regno) = data;
/*
* Alpha historically exposes r0/v0 as the syscall number at a
* syscall-entry stop. The generic-entry conversion keeps the
* mutable syscall number in regs->r1, so old ptrace users such
* as strace that skip a syscall by poking r0 to -1 must also
* update the internal shadow syscall number.
*
* Do not mirror other r0 writes. strace later pokes r0 to the
* injected return value, e.g. 42, while r1 must remain -1.
*/
if (regno == 0 && data == (unsigned long)-1) {
regs->r1 = data;
regs->r19 = 0;
}
return 0;
}
@@ -315,54 +464,6 @@ long arch_ptrace(struct task_struct *child, long request,
DBG(DBG_MEM, ("poke $%lu<-%#lx\n", addr, data));
ret = put_reg(child, addr, data);
break;
case PTRACE_GETREGSET:
case PTRACE_SETREGSET: {
struct iovec __user *uiov = (struct iovec __user *)data;
struct iovec iov;
struct pt_regs *regs;
size_t len;
/* Only support NT_PRSTATUS (general registers) for now. */
if (addr != NT_PRSTATUS) {
ret = -EIO;
break;
}
if (copy_from_user(&iov, uiov, sizeof(iov))) {
ret = -EFAULT;
break;
}
regs = task_pt_regs(child);
len = min_t(size_t, iov.iov_len, sizeof(*regs));
if (request == PTRACE_GETREGSET) {
if (copy_to_user(iov.iov_base, regs, len)) {
ret = -EFAULT;
break;
}
} else {
/*
* Allow writing back regs. This is needed by the TRACE_syscall
* tests (they change PC/syscall nr/retval).
*/
if (copy_from_user(regs, iov.iov_base, len)) {
ret = -EFAULT;
break;
}
}
/* Per API, update iov_len with amount transferred. */
iov.iov_len = len;
if (copy_to_user(uiov, &iov, sizeof(iov))) {
ret = -EFAULT;
break;
}
ret = 0;
break;
}
default:
ret = ptrace_request(child, request, addr, data);
break;
@@ -410,3 +511,126 @@ syscall_trace_leave(void)
if (test_thread_flag(TIF_SYSCALL_TRACE))
ptrace_report_syscall_exit(current_pt_regs(), 0);
}
/*
* Minimal regset support for Alpha.
*
* Alpha-specific notes:
* - Do NOT use ELF_CORE_COPY_REGS(): it uses current_thread_info(),
* which is wrong for non-current tasks.
* - dump_elf_task() returns 1 unconditionally in this tree, while
* regset_get should return 0 on success. So call dump_elf_thread()
* directly and return membuf_write()'s result.
*/
static int alpha_regset_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf,
const void __user *ubuf)
{
elf_gregset_t gregs;
unsigned int nwords;
if (pos + count > sizeof(gregs))
return -EIO;
/*
* Preserve registers outside the written range.
*/
dump_elf_thread(gregs, task_pt_regs(target),
task_thread_info(target));
if (user_regset_copyin(&pos, &count, &kbuf, &ubuf,
gregs, 0, sizeof(gregs)))
return -EFAULT;
nwords = sizeof(gregs) / sizeof(elf_greg_t);
alpha_elf_gregs_set(target, gregs, nwords);
return 0;
}
static int alpha_fpregset_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf,
const void __user *ubuf)
{
elf_fpregset_t fpregs;
unsigned int nwords;
if (pos + count > sizeof(fpregs))
return -EIO;
alpha_elf_fpregs_get(target, fpregs);
if (user_regset_copyin(&pos, &count, &kbuf, &ubuf,
fpregs, 0, sizeof(fpregs)))
return -EFAULT;
nwords = sizeof(fpregs) / sizeof(elf_fpreg_t);
alpha_elf_fpregs_set(target, fpregs, nwords);
return 0;
}
static int alpha_regset_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
struct pt_regs *pt = task_pt_regs(target);
struct thread_info *ti = task_thread_info(target);
elf_gregset_t gregs;
dump_elf_thread(gregs, pt, ti);
return membuf_write(&to, gregs, sizeof(gregs));
}
static int alpha_fpregset_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
elf_fpregset_t fpregs;
alpha_elf_fpregs_get(target, fpregs);
return membuf_write(&to, fpregs, sizeof(fpregs));
}
enum alpha_regset {
REGSET_GPR,
REGSET_FPR,
};
static const struct user_regset alpha_user_regsets[] = {
[REGSET_GPR] = {
.core_note_type = NT_PRSTATUS,
.n = ELF_NGREG,
.size = sizeof(elf_greg_t),
.align = sizeof(elf_greg_t),
.regset_get = alpha_regset_get,
.set = alpha_regset_set,
},
[REGSET_FPR] = {
.core_note_type = NT_PRFPREG,
.core_note_name = "CORE",
.n = ELF_NFPREG,
.size = sizeof(elf_fpreg_t),
.align = sizeof(elf_fpreg_t),
.regset_get = alpha_fpregset_get,
.set = alpha_fpregset_set,
},
};
static const struct user_regset_view user_alpha_view = {
.name = "alpha",
.e_machine = EM_ALPHA,
.ei_osabi = ELF_OSABI,
.regsets = alpha_user_regsets,
.n = ARRAY_SIZE(alpha_user_regsets),
};
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
{
return &user_alpha_view;
}

View File

@@ -15,6 +15,7 @@
#include <linux/bcd.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include "proto.h"
@@ -142,54 +143,40 @@ static const struct rtc_class_ops alpha_rtc_ops = {
};
/*
* Similarly, except do the actual CMOS access on the boot cpu only.
* This requires marshalling the data across an interprocessor call.
* Similarly, except do the actual CMOS access on the boot cpu only. The
* access polls for the RTC update cycle and takes rtc_lock, so run it in a
* worker on that cpu rather than from an interprocessor interrupt.
*/
#if defined(CONFIG_SMP) && \
(defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_MARVEL))
# define HAVE_REMOTE_RTC 1
union remote_data {
struct rtc_time *tm;
long retval;
};
static void
static long
do_remote_read(void *data)
{
union remote_data *x = data;
x->retval = alpha_rtc_read_time(NULL, x->tm);
return alpha_rtc_read_time(NULL, data);
}
static int
remote_read_time(struct device *dev, struct rtc_time *tm)
{
union remote_data x;
if (smp_processor_id() != boot_cpuid) {
x.tm = tm;
smp_call_function_single(boot_cpuid, do_remote_read, &x, 1);
return x.retval;
}
if (smp_processor_id() != boot_cpuid)
return work_on_cpu(boot_cpuid, do_remote_read, tm);
return alpha_rtc_read_time(NULL, tm);
}
static void
static long
do_remote_set(void *data)
{
union remote_data *x = data;
x->retval = alpha_rtc_set_time(NULL, x->tm);
return alpha_rtc_set_time(NULL, data);
}
static int
remote_set_time(struct device *dev, struct rtc_time *tm)
{
union remote_data x;
if (smp_processor_id() != boot_cpuid) {
x.tm = tm;
smp_call_function_single(boot_cpuid, do_remote_set, &x, 1);
return x.retval;
}
if (smp_processor_id() != boot_cpuid)
return work_on_cpu(boot_cpuid, do_remote_set, tm);
return alpha_rtc_set_time(NULL, tm);
}

View File

@@ -41,6 +41,14 @@ asmlinkage void ret_from_sys_call(void);
* The OSF/1 sigprocmask calling sequence is different from the
* C sigprocmask() sequence..
*/
asmlinkage void alpha_schedule_user_work(void)
{
local_irq_enable();
schedule();
local_irq_disable();
}
SYSCALL_DEFINE2(osf_sigprocmask, int, how, unsigned long, newmask)
{
sigset_t oldmask;
@@ -525,6 +533,7 @@ do_work_pending(struct pt_regs *regs, unsigned long thread_flags,
{
do {
if (thread_flags & _TIF_NEED_RESCHED) {
local_irq_enable();
schedule();
} else {
local_irq_enable();

View File

@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
#include <linux/stacktrace.h>
#include <linux/kallsyms.h>
#include <asm/thread_info.h>
#include <asm/ptrace.h>
static __always_inline unsigned long alpha_get_current_ksp(void)
{
unsigned long sp;
asm volatile("mov $30, %0" : "=r"(sp));
return sp;
}
static void alpha_scan_kernel_stack(unsigned long ksp,
stack_trace_consume_fn consume_entry,
void *cookie)
{
unsigned long *p = (unsigned long *)ksp;
if (unlikely(ksp & (sizeof(unsigned long) - 1)))
return;
while (!kstack_end(p)) {
unsigned long addr = READ_ONCE_NOCHECK(*p++);
if (!__kernel_text_address(addr))
continue;
if (!consume_entry(cookie, addr))
break;
}
}
noinline void arch_stack_walk(stack_trace_consume_fn consume_entry,
void *cookie,
struct task_struct *task,
struct pt_regs *regs)
{
unsigned long ksp;
if (!task)
task = current;
if (regs && task == current) {
/*
* pt_regs is stored on the kernel stack; regs+1 matches
* what arch/alpha/kernel/traps.c uses as the trace start.
*/
ksp = (unsigned long)(regs + 1);
} else if (task == current) {
ksp = alpha_get_current_ksp();
} else {
ksp = task_thread_info(task)->pcb.ksp;
}
alpha_scan_kernel_stack(ksp, consume_entry, cookie);
}

View File

@@ -41,7 +41,7 @@ static unsigned long cached_irq_mask;
/* dp264 boards handle at max four CPUs */
static unsigned long cpu_irq_affinity[4] = { 0UL, 0UL, 0UL, 0UL };
DEFINE_SPINLOCK(dp264_irq_lock);
static DEFINE_RAW_SPINLOCK(dp264_irq_lock);
static void
tsunami_update_irq_hw(unsigned long mask)
@@ -99,37 +99,45 @@ tsunami_update_irq_hw(unsigned long mask)
static void
dp264_enable_irq(struct irq_data *d)
{
spin_lock(&dp264_irq_lock);
unsigned long flags;
raw_spin_lock_irqsave(&dp264_irq_lock, flags);
cached_irq_mask |= 1UL << d->irq;
tsunami_update_irq_hw(cached_irq_mask);
spin_unlock(&dp264_irq_lock);
raw_spin_unlock_irqrestore(&dp264_irq_lock, flags);
}
static void
dp264_disable_irq(struct irq_data *d)
{
spin_lock(&dp264_irq_lock);
unsigned long flags;
raw_spin_lock_irqsave(&dp264_irq_lock, flags);
cached_irq_mask &= ~(1UL << d->irq);
tsunami_update_irq_hw(cached_irq_mask);
spin_unlock(&dp264_irq_lock);
raw_spin_unlock_irqrestore(&dp264_irq_lock, flags);
}
static void
clipper_enable_irq(struct irq_data *d)
{
spin_lock(&dp264_irq_lock);
unsigned long flags;
raw_spin_lock_irqsave(&dp264_irq_lock, flags);
cached_irq_mask |= 1UL << (d->irq - 16);
tsunami_update_irq_hw(cached_irq_mask);
spin_unlock(&dp264_irq_lock);
raw_spin_unlock_irqrestore(&dp264_irq_lock, flags);
}
static void
clipper_disable_irq(struct irq_data *d)
{
spin_lock(&dp264_irq_lock);
unsigned long flags;
raw_spin_lock_irqsave(&dp264_irq_lock, flags);
cached_irq_mask &= ~(1UL << (d->irq - 16));
tsunami_update_irq_hw(cached_irq_mask);
spin_unlock(&dp264_irq_lock);
raw_spin_unlock_irqrestore(&dp264_irq_lock, flags);
}
static void
@@ -151,10 +159,12 @@ static int
dp264_set_affinity(struct irq_data *d, const struct cpumask *affinity,
bool force)
{
spin_lock(&dp264_irq_lock);
unsigned long flags;
raw_spin_lock_irqsave(&dp264_irq_lock, flags);
cpu_set_irq_affinity(d->irq, *affinity);
tsunami_update_irq_hw(cached_irq_mask);
spin_unlock(&dp264_irq_lock);
raw_spin_unlock_irqrestore(&dp264_irq_lock, flags);
return 0;
}
@@ -163,10 +173,12 @@ static int
clipper_set_affinity(struct irq_data *d, const struct cpumask *affinity,
bool force)
{
spin_lock(&dp264_irq_lock);
unsigned long flags;
raw_spin_lock_irqsave(&dp264_irq_lock, flags);
cpu_set_irq_affinity(d->irq - 16, *affinity);
tsunami_update_irq_hw(cached_irq_mask);
spin_unlock(&dp264_irq_lock);
raw_spin_unlock_irqrestore(&dp264_irq_lock, flags);
return 0;
}

View File

@@ -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);

View File

@@ -41,7 +41,7 @@ static unsigned int hose_irq_masks[4] = {
0xff0000, 0xfe0000, 0xff0000, 0xff0000
};
static unsigned int cached_irq_masks[4];
DEFINE_SPINLOCK(rawhide_irq_lock);
DEFINE_RAW_SPINLOCK(rawhide_irq_lock);
static inline void
rawhide_update_irq_hw(int hose, int mask)
@@ -59,6 +59,7 @@ rawhide_enable_irq(struct irq_data *d)
{
unsigned int mask, hose;
unsigned int irq = d->irq;
unsigned long flags;
irq -= 16;
hose = irq / 24;
@@ -68,11 +69,11 @@ rawhide_enable_irq(struct irq_data *d)
irq -= hose * 24;
mask = 1 << irq;
spin_lock(&rawhide_irq_lock);
raw_spin_lock_irqsave(&rawhide_irq_lock, flags);
mask |= cached_irq_masks[hose];
cached_irq_masks[hose] = mask;
rawhide_update_irq_hw(hose, mask);
spin_unlock(&rawhide_irq_lock);
raw_spin_unlock_irqrestore(&rawhide_irq_lock, flags);
}
static void
@@ -80,6 +81,7 @@ rawhide_disable_irq(struct irq_data *d)
{
unsigned int mask, hose;
unsigned int irq = d->irq;
unsigned long flags;
irq -= 16;
hose = irq / 24;
@@ -89,11 +91,11 @@ rawhide_disable_irq(struct irq_data *d)
irq -= hose * 24;
mask = ~(1 << irq) | hose_irq_masks[hose];
spin_lock(&rawhide_irq_lock);
raw_spin_lock_irqsave(&rawhide_irq_lock, flags);
mask &= cached_irq_masks[hose];
cached_irq_masks[hose] = mask;
rawhide_update_irq_hw(hose, mask);
spin_unlock(&rawhide_irq_lock);
raw_spin_unlock_irqrestore(&rawhide_irq_lock, flags);
}
static void
@@ -101,6 +103,7 @@ rawhide_mask_and_ack_irq(struct irq_data *d)
{
unsigned int mask, mask1, hose;
unsigned int irq = d->irq;
unsigned long flags;
irq -= 16;
hose = irq / 24;
@@ -111,7 +114,7 @@ rawhide_mask_and_ack_irq(struct irq_data *d)
mask1 = 1 << irq;
mask = ~mask1 | hose_irq_masks[hose];
spin_lock(&rawhide_irq_lock);
raw_spin_lock_irqsave(&rawhide_irq_lock, flags);
mask &= cached_irq_masks[hose];
cached_irq_masks[hose] = mask;
@@ -120,7 +123,7 @@ rawhide_mask_and_ack_irq(struct irq_data *d)
/* Clear the interrupt. */
*(vuip)MCPCIA_INT_REQ(MCPCIA_HOSE2MID(hose)) = mask1;
spin_unlock(&rawhide_irq_lock);
raw_spin_unlock_irqrestore(&rawhide_irq_lock, flags);
}
static struct irq_chip rawhide_irq_type = {

View File

@@ -30,6 +30,12 @@
#include "proto.h"
static __always_inline void alpha_snapshot_usp(struct pt_regs *regs)
{
if (user_mode(regs))
regs->usp = rdusp();
}
void
dik_show_regs(struct pt_regs *regs, unsigned long *r9_15)
{
@@ -166,12 +172,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
@@ -180,12 +186,13 @@ do_entArith(unsigned long summary, unsigned long write_mask,
{
long si_code = FPE_FLTINV;
alpha_snapshot_usp(regs);
if (summary & 1) {
/* Software-completion summary bit is set, so try to
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)
@@ -201,6 +208,7 @@ do_entIF(unsigned long type, struct pt_regs *regs)
{
int signo, code;
alpha_snapshot_usp(regs);
if (type == 3) { /* FEN fault */
/* Irritating users can call PAL_clrfen to disable the
FPU for the process. The kernel will then trap in
@@ -917,7 +925,9 @@ void
trap_init(void)
{
/* Tell PAL-code what global pointer we want in the kernel. */
register unsigned long gptr __asm__("$29");
unsigned long gptr;
__asm__ __volatile__("mov $29, %0" : "=r" (gptr));
wrkgp(gptr);
wrent(entArith, 1);

View File

@@ -28,6 +28,8 @@ SECTIONS
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
IRQENTRY_TEXT
SOFTIRQENTRY_TEXT
*(.fixup)
*(.gnu.warning)
} :text

View File

@@ -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 @@ alpha_fp_emul (unsigned long pc)
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;

View File

@@ -63,8 +63,7 @@ pgd_alloc(struct mm_struct *mm)
static inline unsigned long
load_PCB(struct pcb_struct *pcb)
{
register unsigned long sp __asm__("$30");
pcb->ksp = sp;
pcb->ksp = (unsigned long)current_stack_pointer;
return __reload_thread(pcb);
}