summaryrefslogtreecommitdiff
path: root/arch/alpha/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/alpha/kernel')
-rw-r--r--arch/alpha/kernel/Makefile3
-rw-r--r--arch/alpha/kernel/asm-offsets.c1
-rw-r--r--arch/alpha/kernel/entry.S32
-rw-r--r--arch/alpha/kernel/irq_alpha.c87
-rw-r--r--arch/alpha/kernel/irq_i8259.c19
-rw-r--r--arch/alpha/kernel/pci-sysfs.c32
-rw-r--r--arch/alpha/kernel/proto.h4
-rw-r--r--arch/alpha/kernel/ptrace.c324
-rw-r--r--arch/alpha/kernel/rtc.c37
-rw-r--r--arch/alpha/kernel/signal.c9
-rw-r--r--arch/alpha/kernel/stacktrace.c61
-rw-r--r--arch/alpha/kernel/sys_dp264.c38
-rw-r--r--arch/alpha/kernel/sys_marvel.c25
-rw-r--r--arch/alpha/kernel/sys_rawhide.c17
-rw-r--r--arch/alpha/kernel/syscalls/syscall.tbl1
-rw-r--r--arch/alpha/kernel/traps.c18
-rw-r--r--arch/alpha/kernel/vmlinux.lds.S2
17 files changed, 547 insertions, 163 deletions
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile
index 187cd8df2faf..4ea5c189e60e 100644
--- a/arch/alpha/kernel/Makefile
+++ b/arch/alpha/kernel/Makefile
@@ -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
diff --git a/arch/alpha/kernel/asm-offsets.c b/arch/alpha/kernel/asm-offsets.c
index 1ebb05890499..1d3bfca319ae 100644
--- a/arch/alpha/kernel/asm-offsets.c
+++ b/arch/alpha/kernel/asm-offsets.c
@@ -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));
}
diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S
index fcfd06529b12..9f2608de2544 100644
--- a/arch/alpha/kernel/entry.S
+++ b/arch/alpha/kernel/entry.S
@@ -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
- and $3, $6, $3
- bne $3, strace
-#else
- lda $6, _TIF_SYSCALL_TRACE | _TIF_SECCOMP
+ lda $6, _TIF_SYSCALL_WORK
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
diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c
index d17e44c99df9..1b799aafb7dc 100644
--- a/arch/alpha/kernel/irq_alpha.c
+++ b/arch/alpha/kernel/irq_alpha.c
@@ -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
diff --git a/arch/alpha/kernel/irq_i8259.c b/arch/alpha/kernel/irq_i8259.c
index 29c6c477ac35..28f7b0680564 100644
--- a/arch/alpha/kernel/irq_i8259.c
+++ b/arch/alpha/kernel/irq_i8259.c
@@ -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 = {
diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c
index 94dbc470cd6c..67f9822f7626 100644
--- a/arch/alpha/kernel/pci-sysfs.c
+++ b/arch/alpha/kernel/pci-sysfs.c
@@ -102,25 +102,25 @@ static int pci_mmap_resource_dense(struct file *filp, struct kobject *kobj,
return pci_mmap_resource(kobj, attr, vma, 0);
}
-#define __pci_dev_resource_attr(_bar, _name, _suffix, _mmap) \
-static const struct bin_attribute \
-pci_dev_resource##_bar##_suffix##_attr = { \
- .attr = { .name = __stringify(_name), .mode = 0600 }, \
- .private = (void *)(unsigned long)(_bar), \
- .mmap = (_mmap), \
+#define __pci_dev_resource_attr(_suffix, _bar, _name, _mmap) \
+static const struct bin_attribute \
+pci_dev_resource##_bar##_suffix##_attr = { \
+ .attr = { .name = __stringify(_name), .mode = 0600 }, \
+ .private = (void *)(unsigned long)(_bar), \
+ .mmap = (_mmap), \
}
-#define pci_dev_resource_attr(_bar) \
- __pci_dev_resource_attr(_bar, resource##_bar,, \
- pci_mmap_resource_dense)
+#define pci_dev_resource_attr(_bar) \
+ __pci_dev_resource_attr(, _bar, resource##_bar, \
+ pci_mmap_resource_dense)
#define pci_dev_resource_sparse_attr(_bar) \
- __pci_dev_resource_attr(_bar, resource##_bar##_sparse, _sparse, \
- pci_mmap_resource_sparse)
+ __pci_dev_resource_attr(_sparse, _bar, resource##_bar##_sparse, \
+ pci_mmap_resource_sparse)
#define pci_dev_resource_dense_attr(_bar) \
- __pci_dev_resource_attr(_bar, resource##_bar##_dense, _dense, \
- pci_mmap_resource_dense)
+ __pci_dev_resource_attr(_dense, _bar, resource##_bar##_dense, \
+ pci_mmap_resource_dense)
static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num)
{
@@ -224,17 +224,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/proto.h b/arch/alpha/kernel/proto.h
index a8bc3ead776b..9b262ef09a3a 100644
--- a/arch/alpha/kernel/proto.h
+++ b/arch/alpha/kernel/proto.h
@@ -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);
diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c
index 0687760ea466..fc8f6cedbb28 100644
--- a/arch/alpha/kernel/ptrace.c
+++ b/arch/alpha/kernel/ptrace.c
@@ -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;
@@ -375,7 +476,7 @@ asmlinkage unsigned long syscall_trace_enter(void)
struct pt_regs *regs = current_pt_regs();
if (test_thread_flag(TIF_SYSCALL_TRACE) &&
- ptrace_report_syscall_entry(regs)) {
+ !ptrace_report_syscall_permit_entry(regs)) {
syscall_set_nr(current, regs, -1);
if (regs->r19 == 0 && regs->r0 == (unsigned long)-1)
syscall_set_return_value(current, regs, -ENOSYS, 0);
@@ -387,7 +488,7 @@ asmlinkage unsigned long syscall_trace_enter(void)
* If this fails, seccomp may already have set up the return value
* (e.g. SECCOMP_RET_ERRNO / TRACE).
*/
- if (secure_computing() == -1) {
+ if (!seccomp_permit_syscall()) {
if (regs->r19 == 0 && regs->r0 == (unsigned long)-1)
syscall_set_return_value(current, regs, -ENOSYS, 0);
syscall_set_nr(current, regs, -1);
@@ -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;
+}
diff --git a/arch/alpha/kernel/rtc.c b/arch/alpha/kernel/rtc.c
index cfdf90bc8b3f..9e7d714ef6f8 100644
--- a/arch/alpha/kernel/rtc.c
+++ b/arch/alpha/kernel/rtc.c
@@ -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);
}
diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c
index e62d1d461b1f..ce40a49b8496 100644
--- a/arch/alpha/kernel/signal.c
+++ b/arch/alpha/kernel/signal.c
@@ -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();
diff --git a/arch/alpha/kernel/stacktrace.c b/arch/alpha/kernel/stacktrace.c
new file mode 100644
index 000000000000..74d95f591039
--- /dev/null
+++ b/arch/alpha/kernel/stacktrace.c
@@ -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);
+}
diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c
index 9fb445d7dca5..0a2d319bb1c8 100644
--- a/arch/alpha/kernel/sys_dp264.c
+++ b/arch/alpha/kernel/sys_dp264.c
@@ -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;
}
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/sys_rawhide.c b/arch/alpha/kernel/sys_rawhide.c
index b5846ffdadce..b4a08890dce9 100644
--- a/arch/alpha/kernel/sys_rawhide.c
+++ b/arch/alpha/kernel/sys_rawhide.c
@@ -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 = {
diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index f31b7afffc34..52e3538cc7df 100644
--- a/arch/alpha/kernel/syscalls/syscall.tbl
+++ b/arch/alpha/kernel/syscalls/syscall.tbl
@@ -511,3 +511,4 @@
579 common file_setattr sys_file_setattr
580 common listns sys_listns
581 common rseq_slice_yield sys_rseq_slice_yield
+582 common fchroot sys_fchroot
diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c
index 7004397937cf..cfaef96c4ce8 100644
--- a/arch/alpha/kernel/traps.c
+++ b/arch/alpha/kernel/traps.c
@@ -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);
diff --git a/arch/alpha/kernel/vmlinux.lds.S b/arch/alpha/kernel/vmlinux.lds.S
index 2d136c63db16..95704e64b6a6 100644
--- a/arch/alpha/kernel/vmlinux.lds.S
+++ b/arch/alpha/kernel/vmlinux.lds.S
@@ -28,6 +28,8 @@ SECTIONS
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
+ IRQENTRY_TEXT
+ SOFTIRQENTRY_TEXT
*(.fixup)
*(.gnu.warning)
} :text