summaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2011-12-30 13:59:37 -0200
committerMauro Carvalho Chehab <mchehab@redhat.com>2011-12-30 13:59:37 -0200
commitb4d48c942c17ce3d3a330ad91e109e522bc97378 (patch)
tree3365292f3a5a502edb51492d011fd326c930ca40 /arch/sparc/kernel
parent1a5cd29631a6b75e49e6ad8a770ab9d69cda0fa2 (diff)
parent5f0a6e2d503896062f641639dacfe5055c2f593b (diff)
Merge tag 'v3.2-rc7' into staging/for_v3.3
Linux 3.2-rc7 * tag 'v3.2-rc7': (1304 commits) Linux 3.2-rc7 netfilter: xt_connbytes: handle negation correctly Btrfs: call d_instantiate after all ops are setup Btrfs: fix worker lock misuse in find_worker net: relax rcvbuf limits rps: fix insufficient bounds checking in store_rps_dev_flow_table_cnt() net: introduce DST_NOPEER dst flag mqprio: Avoid panic if no options are provided bridge: provide a mtu() method for fake_dst_ops md/bitmap: It is OK to clear bits during recovery. md: don't give up looking for spares on first failure-to-add md/raid5: ensure correct assessment of drives during degraded reshape. md/linear: fix hot-add of devices to linear arrays. sparc64: Fix MSIQ HV call ordering in pci_sun4v_msiq_build_irq(). pata_of_platform: Add missing CONFIG_OF_IRQ dependency. ipv4: using prefetch requires including prefetch.h VFS: Fix race between CPU hotplug and lglocks vfs: __read_cache_page should use gfp argument rather than GFP_KERNEL USB: Fix usb/isp1760 build on sparc net: Add a flow_cache_flush_deferred function ... Conflicts: drivers/media/common/tuners/tda18218.c drivers/media/video/omap3isp/ispccdc.c drivers/staging/media/as102/as102_drv.h
Diffstat (limited to 'arch/sparc/kernel')
-rw-r--r--arch/sparc/kernel/ds.c6
-rw-r--r--arch/sparc/kernel/entry.h7
-rw-r--r--arch/sparc/kernel/module.c27
-rw-r--r--arch/sparc/kernel/pci_sun4v.c4
-rw-r--r--arch/sparc/kernel/prom_common.c4
-rw-r--r--arch/sparc/kernel/setup_64.c48
-rw-r--r--arch/sparc/kernel/signal32.c18
-rw-r--r--arch/sparc/kernel/signal_32.c30
-rw-r--r--arch/sparc/kernel/signal_64.c42
-rw-r--r--arch/sparc/kernel/sigutil_64.c1
10 files changed, 132 insertions, 55 deletions
diff --git a/arch/sparc/kernel/ds.c b/arch/sparc/kernel/ds.c
index 7429b47c3ac..381edcd5bc2 100644
--- a/arch/sparc/kernel/ds.c
+++ b/arch/sparc/kernel/ds.c
@@ -1181,13 +1181,11 @@ static int __devinit ds_probe(struct vio_dev *vdev,
dp->rcv_buf_len = 4096;
- dp->ds_states = kzalloc(sizeof(ds_states_template),
- GFP_KERNEL);
+ dp->ds_states = kmemdup(ds_states_template,
+ sizeof(ds_states_template), GFP_KERNEL);
if (!dp->ds_states)
goto out_free_rcv_buf;
- memcpy(dp->ds_states, ds_states_template,
- sizeof(ds_states_template));
dp->num_ds_states = ARRAY_SIZE(ds_states_template);
for (i = 0; i < dp->num_ds_states; i++)
diff --git a/arch/sparc/kernel/entry.h b/arch/sparc/kernel/entry.h
index e27f8ea8656..0c218e4c088 100644
--- a/arch/sparc/kernel/entry.h
+++ b/arch/sparc/kernel/entry.h
@@ -42,6 +42,9 @@ extern void fpsave(unsigned long *fpregs, unsigned long *fsr,
extern void fpload(unsigned long *fpregs, unsigned long *fsr);
#else /* CONFIG_SPARC32 */
+
+#include <asm/trap_block.h>
+
struct popc_3insn_patch_entry {
unsigned int addr;
unsigned int insns[3];
@@ -57,6 +60,10 @@ extern struct popc_6insn_patch_entry __popc_6insn_patch,
__popc_6insn_patch_end;
extern void __init per_cpu_patch(void);
+extern void sun4v_patch_1insn_range(struct sun4v_1insn_patch_entry *,
+ struct sun4v_1insn_patch_entry *);
+extern void sun4v_patch_2insn_range(struct sun4v_2insn_patch_entry *,
+ struct sun4v_2insn_patch_entry *);
extern void __init sun4v_patch(void);
extern void __init boot_cpu_id_too_large(int cpu);
extern unsigned int dcache_parity_tl1_occurred;
diff --git a/arch/sparc/kernel/module.c b/arch/sparc/kernel/module.c
index da0c6c70ccb..e5519870c3d 100644
--- a/arch/sparc/kernel/module.c
+++ b/arch/sparc/kernel/module.c
@@ -17,6 +17,8 @@
#include <asm/processor.h>
#include <asm/spitfire.h>
+#include "entry.h"
+
#ifdef CONFIG_SPARC64
#include <linux/jump_label.h>
@@ -203,6 +205,29 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
}
#ifdef CONFIG_SPARC64
+static void do_patch_sections(const Elf_Ehdr *hdr,
+ const Elf_Shdr *sechdrs)
+{
+ const Elf_Shdr *s, *sun4v_1insn = NULL, *sun4v_2insn = NULL;
+ char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+
+ for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
+ if (!strcmp(".sun4v_1insn_patch", secstrings + s->sh_name))
+ sun4v_1insn = s;
+ if (!strcmp(".sun4v_2insn_patch", secstrings + s->sh_name))
+ sun4v_2insn = s;
+ }
+
+ if (sun4v_1insn && tlb_type == hypervisor) {
+ void *p = (void *) sun4v_1insn->sh_addr;
+ sun4v_patch_1insn_range(p, p + sun4v_1insn->sh_size);
+ }
+ if (sun4v_2insn && tlb_type == hypervisor) {
+ void *p = (void *) sun4v_2insn->sh_addr;
+ sun4v_patch_2insn_range(p, p + sun4v_2insn->sh_size);
+ }
+}
+
int module_finalize(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs,
struct module *me)
@@ -210,6 +235,8 @@ int module_finalize(const Elf_Ehdr *hdr,
/* make jump label nops */
jump_label_apply_nops(me);
+ do_patch_sections(hdr, sechdrs);
+
/* Cheetah's I-cache is fully coherent. */
if (tlb_type == spitfire) {
unsigned long va;
diff --git a/arch/sparc/kernel/pci_sun4v.c b/arch/sparc/kernel/pci_sun4v.c
index b272cda35a0..af5755d20fb 100644
--- a/arch/sparc/kernel/pci_sun4v.c
+++ b/arch/sparc/kernel/pci_sun4v.c
@@ -849,10 +849,10 @@ static int pci_sun4v_msiq_build_irq(struct pci_pbm_info *pbm,
if (!irq)
return -ENOMEM;
- if (pci_sun4v_msiq_setstate(pbm->devhandle, msiqid, HV_MSIQSTATE_IDLE))
- return -EINVAL;
if (pci_sun4v_msiq_setvalid(pbm->devhandle, msiqid, HV_MSIQ_VALID))
return -EINVAL;
+ if (pci_sun4v_msiq_setstate(pbm->devhandle, msiqid, HV_MSIQSTATE_IDLE))
+ return -EINVAL;
return irq;
}
diff --git a/arch/sparc/kernel/prom_common.c b/arch/sparc/kernel/prom_common.c
index 46614807a57..741df916c12 100644
--- a/arch/sparc/kernel/prom_common.c
+++ b/arch/sparc/kernel/prom_common.c
@@ -58,12 +58,10 @@ int of_set_property(struct device_node *dp, const char *name, void *val, int len
void *new_val;
int err;
- new_val = kmalloc(len, GFP_KERNEL);
+ new_val = kmemdup(val, len, GFP_KERNEL);
if (!new_val)
return -ENOMEM;
- memcpy(new_val, val, len);
-
err = -ENODEV;
mutex_lock(&of_set_property_mutex);
diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c
index c965595aa7e..a854a1c240f 100644
--- a/arch/sparc/kernel/setup_64.c
+++ b/arch/sparc/kernel/setup_64.c
@@ -234,40 +234,50 @@ void __init per_cpu_patch(void)
}
}
-void __init sun4v_patch(void)
+void sun4v_patch_1insn_range(struct sun4v_1insn_patch_entry *start,
+ struct sun4v_1insn_patch_entry *end)
{
- extern void sun4v_hvapi_init(void);
- struct sun4v_1insn_patch_entry *p1;
- struct sun4v_2insn_patch_entry *p2;
-
- if (tlb_type != hypervisor)
- return;
+ while (start < end) {
+ unsigned long addr = start->addr;
- p1 = &__sun4v_1insn_patch;
- while (p1 < &__sun4v_1insn_patch_end) {
- unsigned long addr = p1->addr;
-
- *(unsigned int *) (addr + 0) = p1->insn;
+ *(unsigned int *) (addr + 0) = start->insn;
wmb();
__asm__ __volatile__("flush %0" : : "r" (addr + 0));
- p1++;
+ start++;
}
+}
- p2 = &__sun4v_2insn_patch;
- while (p2 < &__sun4v_2insn_patch_end) {
- unsigned long addr = p2->addr;
+void sun4v_patch_2insn_range(struct sun4v_2insn_patch_entry *start,
+ struct sun4v_2insn_patch_entry *end)
+{
+ while (start < end) {
+ unsigned long addr = start->addr;
- *(unsigned int *) (addr + 0) = p2->insns[0];
+ *(unsigned int *) (addr + 0) = start->insns[0];
wmb();
__asm__ __volatile__("flush %0" : : "r" (addr + 0));
- *(unsigned int *) (addr + 4) = p2->insns[1];
+ *(unsigned int *) (addr + 4) = start->insns[1];
wmb();
__asm__ __volatile__("flush %0" : : "r" (addr + 4));
- p2++;
+ start++;
}
+}
+
+void __init sun4v_patch(void)
+{
+ extern void sun4v_hvapi_init(void);
+
+ if (tlb_type != hypervisor)
+ return;
+
+ sun4v_patch_1insn_range(&__sun4v_1insn_patch,
+ &__sun4v_1insn_patch_end);
+
+ sun4v_patch_2insn_range(&__sun4v_2insn_patch,
+ &__sun4v_2insn_patch_end);
sun4v_hvapi_init();
}
diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c
index 2caa556db86..023b8860dc9 100644
--- a/arch/sparc/kernel/signal32.c
+++ b/arch/sparc/kernel/signal32.c
@@ -822,21 +822,23 @@ static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*/
-void do_signal32(sigset_t *oldset, struct pt_regs * regs,
- int restart_syscall, unsigned long orig_i0)
+void do_signal32(sigset_t *oldset, struct pt_regs * regs)
{
struct k_sigaction ka;
+ unsigned long orig_i0;
+ int restart_syscall;
siginfo_t info;
int signr;
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
- /* If the debugger messes with the program counter, it clears
- * the "in syscall" bit, directing us to not perform a syscall
- * restart.
- */
- if (restart_syscall && !pt_regs_is_syscall(regs))
- restart_syscall = 0;
+ restart_syscall = 0;
+ orig_i0 = 0;
+ if (pt_regs_is_syscall(regs) &&
+ (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) {
+ restart_syscall = 1;
+ orig_i0 = regs->u_regs[UREG_G6];
+ }
if (signr > 0) {
if (restart_syscall)
diff --git a/arch/sparc/kernel/signal_32.c b/arch/sparc/kernel/signal_32.c
index 8ce247ac04c..d54c6e53aba 100644
--- a/arch/sparc/kernel/signal_32.c
+++ b/arch/sparc/kernel/signal_32.c
@@ -519,10 +519,26 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
siginfo_t info;
int signr;
+ /* It's a lot of work and synchronization to add a new ptrace
+ * register for GDB to save and restore in order to get
+ * orig_i0 correct for syscall restarts when debugging.
+ *
+ * Although it should be the case that most of the global
+ * registers are volatile across a system call, glibc already
+ * depends upon that fact that we preserve them. So we can't
+ * just use any global register to save away the orig_i0 value.
+ *
+ * In particular %g2, %g3, %g4, and %g5 are all assumed to be
+ * preserved across a system call trap by various pieces of
+ * code in glibc.
+ *
+ * %g7 is used as the "thread register". %g6 is not used in
+ * any fixed manner. %g6 is used as a scratch register and
+ * a compiler temporary, but it's value is never used across
+ * a system call. Therefore %g6 is usable for orig_i0 storage.
+ */
if (pt_regs_is_syscall(regs) && (regs->psr & PSR_C))
- restart_syscall = 1;
- else
- restart_syscall = 0;
+ regs->u_regs[UREG_G6] = orig_i0;
if (test_thread_flag(TIF_RESTORE_SIGMASK))
oldset = &current->saved_sigmask;
@@ -535,8 +551,12 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
* the software "in syscall" bit, directing us to not perform
* a syscall restart.
*/
- if (restart_syscall && !pt_regs_is_syscall(regs))
- restart_syscall = 0;
+ restart_syscall = 0;
+ if (pt_regs_is_syscall(regs) && (regs->psr & PSR_C)) {
+ restart_syscall = 1;
+ orig_i0 = regs->u_regs[UREG_G6];
+ }
+
if (signr > 0) {
if (restart_syscall)
diff --git a/arch/sparc/kernel/signal_64.c b/arch/sparc/kernel/signal_64.c
index a2b81598d90..f0836cd0e2f 100644
--- a/arch/sparc/kernel/signal_64.c
+++ b/arch/sparc/kernel/signal_64.c
@@ -529,11 +529,27 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
siginfo_t info;
int signr;
+ /* It's a lot of work and synchronization to add a new ptrace
+ * register for GDB to save and restore in order to get
+ * orig_i0 correct for syscall restarts when debugging.
+ *
+ * Although it should be the case that most of the global
+ * registers are volatile across a system call, glibc already
+ * depends upon that fact that we preserve them. So we can't
+ * just use any global register to save away the orig_i0 value.
+ *
+ * In particular %g2, %g3, %g4, and %g5 are all assumed to be
+ * preserved across a system call trap by various pieces of
+ * code in glibc.
+ *
+ * %g7 is used as the "thread register". %g6 is not used in
+ * any fixed manner. %g6 is used as a scratch register and
+ * a compiler temporary, but it's value is never used across
+ * a system call. Therefore %g6 is usable for orig_i0 storage.
+ */
if (pt_regs_is_syscall(regs) &&
- (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) {
- restart_syscall = 1;
- } else
- restart_syscall = 0;
+ (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY)))
+ regs->u_regs[UREG_G6] = orig_i0;
if (current_thread_info()->status & TS_RESTORE_SIGMASK)
oldset = &current->saved_sigmask;
@@ -542,22 +558,20 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
#ifdef CONFIG_COMPAT
if (test_thread_flag(TIF_32BIT)) {
- extern void do_signal32(sigset_t *, struct pt_regs *,
- int restart_syscall,
- unsigned long orig_i0);
- do_signal32(oldset, regs, restart_syscall, orig_i0);
+ extern void do_signal32(sigset_t *, struct pt_regs *);
+ do_signal32(oldset, regs);
return;
}
#endif
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
- /* If the debugger messes with the program counter, it clears
- * the software "in syscall" bit, directing us to not perform
- * a syscall restart.
- */
- if (restart_syscall && !pt_regs_is_syscall(regs))
- restart_syscall = 0;
+ restart_syscall = 0;
+ if (pt_regs_is_syscall(regs) &&
+ (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) {
+ restart_syscall = 1;
+ orig_i0 = regs->u_regs[UREG_G6];
+ }
if (signr > 0) {
if (restart_syscall)
diff --git a/arch/sparc/kernel/sigutil_64.c b/arch/sparc/kernel/sigutil_64.c
index e7dc508c38e..b19570d41a3 100644
--- a/arch/sparc/kernel/sigutil_64.c
+++ b/arch/sparc/kernel/sigutil_64.c
@@ -2,6 +2,7 @@
#include <linux/types.h>
#include <linux/thread_info.h>
#include <linux/uaccess.h>
+#include <linux/errno.h>
#include <asm/sigcontext.h>
#include <asm/fpumacro.h>