mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 04:46:30 -04:00
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:
@@ -6,7 +6,7 @@
|
||||
-----------------------
|
||||
| arch |status|
|
||||
-----------------------
|
||||
| alpha: | TODO |
|
||||
| alpha: | ok |
|
||||
| arc: | ok |
|
||||
| arm: | ok |
|
||||
| arm64: | ok |
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
-----------------------
|
||||
| arch |status|
|
||||
-----------------------
|
||||
| alpha: | TODO |
|
||||
| alpha: | ok |
|
||||
| arc: | ok |
|
||||
| arm: | ok |
|
||||
| arm64: | ok |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Arch specific extensions to struct device
|
||||
*/
|
||||
#include <asm-generic/device.h>
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
|
||||
#define EF_ALPHA_32BIT 1 /* All addresses are below 2GB */
|
||||
|
||||
#define CORE_DUMP_USE_REGSET 1
|
||||
/*
|
||||
* ELF register definitions..
|
||||
*/
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
61
arch/alpha/kernel/stacktrace.c
Normal file
61
arch/alpha/kernel/stacktrace.c
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -28,6 +28,8 @@ SECTIONS
|
||||
TEXT_TEXT
|
||||
SCHED_TEXT
|
||||
LOCK_TEXT
|
||||
IRQENTRY_TEXT
|
||||
SOFTIRQENTRY_TEXT
|
||||
*(.fixup)
|
||||
*(.gnu.warning)
|
||||
} :text
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user