summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid S. Miller <davem@kernel.bkbits.net>2004-09-01 11:19:10 -0700
committerDavid S. Miller <davem@kernel.bkbits.net>2004-09-01 11:19:10 -0700
commita0adf3b176081c8af8176cc08dce984b0f0894de (patch)
tree2562e5d468e42dff44e2c9cae8ad56827cfd3408
parent6ce8ea8a4590fa59e8987392960983254fdfc16e (diff)
parent39e8d677aa14b60da1f4271b4882ad5f4a196bcc (diff)
Merge davem@nuts.davemloft.net:/disk1/BK/sparc-2.6
into kernel.bkbits.net:/home/davem/sparc-2.6
-rw-r--r--arch/sparc/kernel/systbls.S2
-rw-r--r--arch/sparc64/Kconfig.debug10
-rw-r--r--arch/sparc64/defconfig13
-rw-r--r--arch/sparc64/kernel/Makefile1
-rw-r--r--arch/sparc64/kernel/irq.c19
-rw-r--r--arch/sparc64/kernel/kprobes.c276
-rw-r--r--arch/sparc64/kernel/pci.c4
-rw-r--r--arch/sparc64/kernel/pci_common.c122
-rw-r--r--arch/sparc64/kernel/pci_impl.h1
-rw-r--r--arch/sparc64/kernel/pci_psycho.c6
-rw-r--r--arch/sparc64/kernel/pci_sabre.c15
-rw-r--r--arch/sparc64/kernel/pci_schizo.c6
-rw-r--r--arch/sparc64/kernel/signal32.c77
-rw-r--r--arch/sparc64/kernel/sys_sparc32.c28
-rw-r--r--arch/sparc64/kernel/systbls.S4
-rw-r--r--arch/sparc64/kernel/traps.c97
-rw-r--r--arch/sparc64/kernel/ttable.S2
-rw-r--r--arch/sparc64/mm/fault.c11
-rw-r--r--arch/sparc64/solaris/misc.c69
-rw-r--r--include/asm-sparc/unistd.h11
-rw-r--r--include/asm-sparc64/kdebug.h49
-rw-r--r--include/asm-sparc64/kprobes.h24
-rw-r--r--include/asm-sparc64/siginfo.h54
-rw-r--r--include/asm-sparc64/ttable.h6
-rw-r--r--include/asm-sparc64/unistd.h12
-rw-r--r--kernel/kprobes.c6
26 files changed, 704 insertions, 221 deletions
diff --git a/arch/sparc/kernel/systbls.S b/arch/sparc/kernel/systbls.S
index fd452a6d3b71..288a0b44b848 100644
--- a/arch/sparc/kernel/systbls.S
+++ b/arch/sparc/kernel/systbls.S
@@ -74,7 +74,7 @@ sys_call_table:
/*260*/ .long sys_sched_getaffinity, sys_sched_setaffinity, sys_timer_settime, sys_timer_gettime, sys_timer_getoverrun
/*265*/ .long sys_timer_delete, sys_timer_create, sys_nis_syscall, sys_io_setup, sys_io_destroy
/*270*/ .long sys_io_submit, sys_io_cancel, sys_io_getevents, sys_mq_open, sys_mq_unlink
-/*275*/ .long sys_mq_timedsend, sys_mq_timedreceive, sys_mq_notify, sys_mq_getsetattr, sys_ni_syscall
+/*275*/ .long sys_mq_timedsend, sys_mq_timedreceive, sys_mq_notify, sys_mq_getsetattr, sys_waitid
/*280*/ .long sys_ni_syscall, sys_ni_syscall, sys_ni_syscall
#ifdef CONFIG_SUNOS_EMUL
diff --git a/arch/sparc64/Kconfig.debug b/arch/sparc64/Kconfig.debug
index c3f79c0afc34..cd8d39fb954d 100644
--- a/arch/sparc64/Kconfig.debug
+++ b/arch/sparc64/Kconfig.debug
@@ -11,6 +11,16 @@ config DEBUG_STACK_USAGE
This option will slow down process creation somewhat.
+config KPROBES
+ bool "Kprobes"
+ depends on DEBUG_KERNEL
+ help
+ Kprobes allows you to trap at almost any kernel address and
+ execute a callback function. register_kprobe() establishes
+ a probepoint and specifies the callback. Kprobes is useful
+ for kernel debugging, non-intrusive instrumentation and testing.
+ If in doubt, say "N".
+
config DEBUG_DCFLUSH
bool "D-cache flush debugging"
depends on DEBUG_KERNEL
diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig
index 90ece9813fc6..871f09bae1e4 100644
--- a/arch/sparc64/defconfig
+++ b/arch/sparc64/defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.9-rc1
-# Fri Aug 27 17:37:00 2004
+# Wed Sep 1 17:54:49 2004
#
CONFIG_64BIT=y
CONFIG_MMU=y
@@ -35,6 +35,8 @@ CONFIG_IOSCHED_AS=y
CONFIG_IOSCHED_DEADLINE=y
CONFIG_IOSCHED_CFQ=y
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+# CONFIG_TINY_SHMEM is not set
#
# Loadable module support
@@ -332,7 +334,8 @@ CONFIG_AIC79XX_RESET_DELAY_MS=15000
# CONFIG_AIC79XX_DEBUG_ENABLE is not set
CONFIG_AIC79XX_DEBUG_MASK=0
# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set
-# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
CONFIG_SCSI_SATA=y
CONFIG_SCSI_SATA_SVW=m
CONFIG_SCSI_ATA_PIIX=m
@@ -357,9 +360,7 @@ CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1
CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
-CONFIG_SCSI_IPR=m
-# CONFIG_SCSI_IPR_TRACE is not set
-# CONFIG_SCSI_IPR_DUMP is not set
+# CONFIG_SCSI_IPR is not set
CONFIG_SCSI_QLOGIC_ISP=m
CONFIG_SCSI_QLOGIC_FC=y
CONFIG_SCSI_QLOGIC_FC_FIRMWARE=y
@@ -417,7 +418,6 @@ CONFIG_DM_ZERO=m
#
CONFIG_FUSION=m
CONFIG_FUSION_MAX_SGE=40
-CONFIG_FUSION_ISENSE=m
CONFIG_FUSION_CTL=m
CONFIG_FUSION_LAN=m
@@ -1757,6 +1757,7 @@ CONFIG_MAGIC_SYSRQ=y
# CONFIG_DEBUG_BUGVERBOSE is not set
# CONFIG_DEBUG_INFO is not set
# CONFIG_DEBUG_STACK_USAGE is not set
+CONFIG_KPROBES=y
# CONFIG_DEBUG_DCFLUSH is not set
# CONFIG_STACK_DEBUG is not set
# CONFIG_DEBUG_BOOTMEM is not set
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile
index 5c53e0026932..093281bdf85f 100644
--- a/arch/sparc64/kernel/Makefile
+++ b/arch/sparc64/kernel/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_BINFMT_AOUT32) += binfmt_aout32.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o
obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o
+obj-$(CONFIG_KPROBES) += kprobes.o
ifdef CONFIG_SUNOS_EMUL
obj-y += sys_sunos32.o sunos_ioctl32.o
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index d52b842915d4..fdb848178a86 100644
--- a/arch/sparc64/kernel/irq.c
+++ b/arch/sparc64/kernel/irq.c
@@ -791,16 +791,24 @@ void handler_irq(int irq, struct pt_regs *regs)
#endif
if ((flags & IBF_MULTI) == 0) {
struct irqaction *ap = bp->irq_info;
- ap->handler(__irq(bp), ap->dev_id, regs);
- random |= ap->flags & SA_SAMPLE_RANDOM;
+ int ret;
+
+ ret = ap->handler(__irq(bp), ap->dev_id, regs);
+ if (ret == IRQ_HANDLED)
+ random |= ap->flags;
} else {
void **vector = (void **)bp->irq_info;
int ent;
for (ent = 0; ent < 4; ent++) {
struct irqaction *ap = vector[ent];
if (ap != NULL) {
- ap->handler(__irq(bp), ap->dev_id, regs);
- random |= ap->flags & SA_SAMPLE_RANDOM;
+ int ret;
+
+ ret = ap->handler(__irq(bp),
+ ap->dev_id,
+ regs);
+ if (ret == IRQ_HANDLED)
+ random |= ap->flags;
}
}
}
@@ -813,8 +821,9 @@ void handler_irq(int irq, struct pt_regs *regs)
}
#endif
upa_writel(ICLR_IDLE, bp->iclr);
+
/* Test and add entropy */
- if (random)
+ if (random & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
}
} else
diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c
new file mode 100644
index 000000000000..ea447298a2e3
--- /dev/null
+++ b/arch/sparc64/kernel/kprobes.c
@@ -0,0 +1,276 @@
+/* arch/sparc64/kernel/kprobes.c
+ *
+ * Copyright (C) 2004 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+
+#include <asm/kdebug.h>
+#include <asm/signal.h>
+
+/* We do not have hardware single-stepping, so in order
+ * to implement post handlers correctly we use two breakpoint
+ * instructions.
+ *
+ * 1) ta 0x70 --> 0x91d02070
+ * 2) ta 0x71 --> 0x91d02071
+ *
+ * When these are hit, control is transferred to kprobe_trap()
+ * below. The arg 'level' tells us which of the two traps occurred.
+ *
+ * Initially, the instruction at p->addr gets set to "ta 0x70"
+ * by code in register_kprobe() by setting that memory address
+ * to BREAKPOINT_INSTRUCTION. When this breakpoint is hit
+ * the following happens:
+ *
+ * 1) We run the pre-handler
+ * 2) We replace p->addr with the original opcode
+ * 3) We set the instruction at "regs->npc" to "ta 0x71"
+ * 4) We mark that we are waiting for the second breakpoint
+ * to hit and return from the trap.
+ *
+ * At this point we wait for the second breakpoint to hit.
+ * When it does:
+ *
+ * 1) We run the post-handler
+ * 2) We re-install "ta 0x70" at p->addr
+ * 3) We restore the opcode at the "ta 0x71" breakpoint
+ * 4) We reset our "waiting for "ta 0x71" state
+ * 5) We return from the trap
+ *
+ * We could use the trick used by the i386 kprobe code but I
+ * think that scheme has problems with exception tables. On i386
+ * they single-step over the original instruction stored at
+ * kprobe->insn. So they set the processor to single step, and
+ * set the program counter to kprobe->insn.
+ *
+ * But that explodes if the original opcode is a user space
+ * access instruction and that faults. It will go wrong because
+ * since the location of the instruction being executed is
+ * different from that recorded in the exception tables, the
+ * kernel will not find it and this will cause an erroneous
+ * kernel OOPS.
+ */
+
+void arch_prepare_kprobe(struct kprobe *p)
+{
+ p->insn[0] = *p->addr;
+ p->insn[1] = 0xdeadbeef;
+}
+
+static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
+{
+ u32 *insn2 = (u32 *) regs->tpc;
+
+ p->insn[1] = *insn2;
+
+ *insn2 = BREAKPOINT_INSTRUCTION_2;
+ flushi(insn2);
+}
+
+static void undo_singlestep(struct kprobe *p, struct pt_regs *regs)
+{
+ u32 *insn2 = (u32 *) regs->tpc;
+
+ BUG_ON(p->insn[1] == 0xdeadbeef);
+
+ *insn2 = p->insn[1];
+ flushi(insn2);
+
+ p->insn[1] = 0xdeadbeef;
+}
+
+/* kprobe_status settings */
+#define KPROBE_HIT_ACTIVE 0x00000001
+#define KPROBE_HIT_SS 0x00000002
+
+static struct kprobe *current_kprobe;
+static unsigned int kprobe_status;
+
+static int kprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe *p;
+ void *addr = (void *) regs->tpc;
+ int ret = 0;
+
+ preempt_disable();
+
+ if (kprobe_running()) {
+ p = get_kprobe(addr);
+ if (p) {
+ *p->addr = p->opcode;
+ flushi(p->addr);
+ ret = 1;
+ } else {
+ p = current_kprobe;
+ if (p->break_handler && p->break_handler(p, regs))
+ goto ss_probe;
+ }
+ goto no_kprobe;
+ }
+
+ lock_kprobes();
+ p = get_kprobe(addr);
+ if (!p) {
+ unlock_kprobes();
+ if (*(u32 *)addr != BREAKPOINT_INSTRUCTION)
+ ret = 1;
+ goto no_kprobe;
+ }
+
+ kprobe_status = KPROBE_HIT_ACTIVE;
+ current_kprobe = p;
+ if (p->pre_handler(p, regs))
+ return 1;
+
+ss_probe:
+ prepare_singlestep(p, regs);
+ kprobe_status = KPROBE_HIT_SS;
+ return 1;
+
+no_kprobe:
+ preempt_enable_no_resched();
+ return ret;
+}
+
+static int post_kprobe_handler(struct pt_regs *regs)
+{
+ u32 *insn_p = (u32 *) regs->tpc;
+
+ if (!kprobe_running() || (*insn_p != BREAKPOINT_INSTRUCTION_2))
+ return 0;
+
+ if (current_kprobe->post_handler)
+ current_kprobe->post_handler(current_kprobe, regs, 0);
+
+ undo_singlestep(current_kprobe, regs);
+
+ unlock_kprobes();
+ preempt_enable_no_resched();
+
+ return 1;
+}
+
+/* Interrupts disabled, kprobe_lock held. */
+static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+ if (current_kprobe->fault_handler
+ && current_kprobe->fault_handler(current_kprobe, regs, trapnr))
+ return 1;
+
+ if (kprobe_status & KPROBE_HIT_SS) {
+ undo_singlestep(current_kprobe, regs);
+
+ unlock_kprobes();
+ preempt_enable_no_resched();
+ }
+ return 0;
+}
+
+/*
+ * Wrapper routine to for handling exceptions.
+ */
+int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val,
+ void *data)
+{
+ struct die_args *args = (struct die_args *)data;
+ switch (val) {
+ case DIE_DEBUG:
+ if (kprobe_handler(args->regs))
+ return NOTIFY_OK;
+ break;
+ case DIE_DEBUG_2:
+ if (post_kprobe_handler(args->regs))
+ return NOTIFY_OK;
+ break;
+ case DIE_GPF:
+ if (kprobe_running() &&
+ kprobe_fault_handler(args->regs, args->trapnr))
+ return NOTIFY_OK;
+ break;
+ case DIE_PAGE_FAULT:
+ if (kprobe_running() &&
+ kprobe_fault_handler(args->regs, args->trapnr))
+ return NOTIFY_OK;
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_BAD;
+}
+
+asmlinkage void kprobe_trap(unsigned long trap_level, struct pt_regs *regs)
+{
+ BUG_ON(trap_level != 0x170 && trap_level != 0x171);
+
+ if (user_mode(regs)) {
+ local_irq_enable();
+ bad_trap(regs, trap_level);
+ return;
+ }
+
+ /* trap_level == 0x170 --> ta 0x70
+ * trap_level == 0x171 --> ta 0x71
+ */
+ if (notify_die((trap_level == 0x170) ? DIE_DEBUG : DIE_DEBUG_2,
+ (trap_level == 0x170) ? "debug" : "debug_2",
+ regs, 0, trap_level, SIGTRAP) != NOTIFY_OK)
+ bad_trap(regs, trap_level);
+}
+
+/* Jprobes support. */
+static struct pt_regs jprobe_saved_regs;
+static struct sparc_stackf jprobe_saved_stack;
+
+int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+
+ memcpy(&jprobe_saved_regs, regs, sizeof(*regs));
+
+ /* Save a whole stack frame, this gets arguments
+ * pushed onto the stack after using up all the
+ * arg registers.
+ */
+ memcpy(&jprobe_saved_stack,
+ (char *) (regs->u_regs[UREG_FP] + STACK_BIAS),
+ sizeof(jprobe_saved_stack));
+
+ regs->tpc = (unsigned long) jp->entry;
+ regs->tnpc = ((unsigned long) jp->entry) + 0x4UL;
+
+ return 1;
+}
+
+void jprobe_return(void)
+{
+ preempt_enable_no_resched();
+ __asm__ __volatile__(
+ ".globl jprobe_return_trap_instruction\n"
+"jprobe_return_trap_instruction:\n\t"
+ "ta 0x70");
+}
+
+extern void jprobe_return_trap_instruction(void);
+
+int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ u32 *addr = (u32 *) regs->tpc;
+
+ if (addr == (u32 *) jprobe_return_trap_instruction) {
+ /* Restore old register state. Do pt_regs
+ * first so that UREG_FP is the original one for
+ * the stack frame restore.
+ */
+ memcpy(regs, &jprobe_saved_regs, sizeof(*regs));
+
+ memcpy((char *) (regs->u_regs[UREG_FP] + STACK_BIAS),
+ &jprobe_saved_stack,
+ sizeof(jprobe_saved_stack));
+
+ return 1;
+ }
+ return 0;
+}
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
index f7b7fafb2479..75032d0e2a2a 100644
--- a/arch/sparc64/kernel/pci.c
+++ b/arch/sparc64/kernel/pci.c
@@ -41,7 +41,6 @@ asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn,
#else
/* List of all PCI controllers found in the system. */
-spinlock_t pci_controller_lock = SPIN_LOCK_UNLOCKED;
struct pci_controller_info *pci_controller_root = NULL;
/* Each PCI controller found gets a unique index. */
@@ -298,12 +297,9 @@ static void __init pci_controller_probe(void)
static void __init pci_scan_each_controller_bus(void)
{
struct pci_controller_info *p;
- unsigned long flags;
- spin_lock_irqsave(&pci_controller_lock, flags);
for (p = pci_controller_root; p; p = p->next)
p->scan_bus(p);
- spin_unlock_irqrestore(&pci_controller_lock, flags);
}
/* Reorder the pci_dev chain, so that onboard devices come first
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c
index fda65241451c..58310aacea28 100644
--- a/arch/sparc64/kernel/pci_common.c
+++ b/arch/sparc64/kernel/pci_common.c
@@ -15,18 +15,13 @@
*/
void __init pci_fixup_host_bridge_self(struct pci_bus *pbus)
{
- struct list_head *walk = &pbus->devices;
-
- walk = walk->next;
- while (walk != &pbus->devices) {
- struct pci_dev *pdev = pci_dev_b(walk);
+ struct pci_dev *pdev;
+ list_for_each_entry(pdev, &pbus->devices, bus_list) {
if (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) {
pbus->self = pdev;
return;
}
-
- walk = walk->next;
}
prom_printf("PCI: Critical error, cannot find host bridge PDEV.\n");
@@ -217,31 +212,18 @@ void __init pci_fill_in_pbm_cookies(struct pci_bus *pbus,
struct pci_pbm_info *pbm,
int prom_node)
{
- struct list_head *walk = &pbus->devices;
-
- /* This loop is coded like this because the cookie
- * fillin routine can delete devices from the tree.
- */
- walk = walk->next;
- while (walk != &pbus->devices) {
- struct pci_dev *pdev = pci_dev_b(walk);
- struct list_head *walk_next = walk->next;
+ struct pci_dev *pdev, *pdev_next;
+ struct pci_bus *this_pbus, *pbus_next;
+ /* This must be _safe because the cookie fillin
+ routine can delete devices from the tree. */
+ list_for_each_entry_safe(pdev, pdev_next, &pbus->devices, bus_list)
pdev_cookie_fillin(pbm, pdev, prom_node);
- walk = walk_next;
- }
-
- walk = &pbus->children;
- walk = walk->next;
- while (walk != &pbus->children) {
- struct pci_bus *this_pbus = pci_bus_b(walk);
+ list_for_each_entry_safe(this_pbus, pbus_next, &pbus->children, node) {
struct pcidev_cookie *pcp = this_pbus->self->sysdata;
- struct list_head *walk_next = walk->next;
pci_fill_in_pbm_cookies(this_pbus, pbm, pcp->prom_node);
-
- walk = walk_next;
}
}
@@ -431,14 +413,14 @@ static void __init pdev_record_assignments(struct pci_pbm_info *pbm,
void __init pci_record_assignments(struct pci_pbm_info *pbm,
struct pci_bus *pbus)
{
- struct list_head *walk = &pbus->devices;
+ struct pci_dev *dev;
+ struct pci_bus *bus;
- for (walk = walk->next; walk != &pbus->devices; walk = walk->next)
- pdev_record_assignments(pbm, pci_dev_b(walk));
+ list_for_each_entry(dev, &pbus->devices, bus_list)
+ pdev_record_assignments(pbm, dev);
- walk = &pbus->children;
- for (walk = walk->next; walk != &pbus->children; walk = walk->next)
- pci_record_assignments(pbm, pci_bus_b(walk));
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_record_assignments(pbm, bus);
}
/* Return non-zero if PDEV has implicit I/O resources even
@@ -549,14 +531,14 @@ static void __init pdev_assign_unassigned(struct pci_pbm_info *pbm,
void __init pci_assign_unassigned(struct pci_pbm_info *pbm,
struct pci_bus *pbus)
{
- struct list_head *walk = &pbus->devices;
+ struct pci_dev *dev;
+ struct pci_bus *bus;
- for (walk = walk->next; walk != &pbus->devices; walk = walk->next)
- pdev_assign_unassigned(pbm, pci_dev_b(walk));
+ list_for_each_entry(dev, &pbus->devices, bus_list)
+ pdev_assign_unassigned(pbm, dev);
- walk = &pbus->children;
- for (walk = walk->next; walk != &pbus->children; walk = walk->next)
- pci_assign_unassigned(pbm, pci_bus_b(walk));
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_assign_unassigned(pbm, bus);
}
static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt)
@@ -797,14 +779,14 @@ have_irq:
void __init pci_fixup_irq(struct pci_pbm_info *pbm,
struct pci_bus *pbus)
{
- struct list_head *walk = &pbus->devices;
+ struct pci_dev *dev;
+ struct pci_bus *bus;
- for (walk = walk->next; walk != &pbus->devices; walk = walk->next)
- pdev_fixup_irq(pci_dev_b(walk));
+ list_for_each_entry(dev, &pbus->devices, bus_list)
+ pdev_fixup_irq(dev);
- walk = &pbus->children;
- for (walk = walk->next; walk != &pbus->children; walk = walk->next)
- pci_fixup_irq(pbm, pci_bus_b(walk));
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_fixup_irq(pbm, bus);
}
static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz)
@@ -897,7 +879,7 @@ static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz)
void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm,
struct pci_bus *pbus)
{
- struct list_head *walk;
+ struct pci_dev *pdev;
int all_are_66mhz;
u16 status;
@@ -906,11 +888,8 @@ void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm,
goto out;
}
- walk = &pbus->devices;
all_are_66mhz = 1;
- for (walk = walk->next; walk != &pbus->devices; walk = walk->next) {
- struct pci_dev *pdev = pci_dev_b(walk);
-
+ list_for_each_entry(pdev, &pbus->devices, bus_list) {
pci_read_config_word(pdev, PCI_STATUS, &status);
if (!(status & PCI_STATUS_66MHZ)) {
all_are_66mhz = 0;
@@ -929,17 +908,17 @@ out:
void pci_setup_busmastering(struct pci_pbm_info *pbm,
struct pci_bus *pbus)
{
- struct list_head *walk = &pbus->devices;
+ struct pci_dev *dev;
+ struct pci_bus *bus;
int is_66mhz;
is_66mhz = pbm->is_66mhz_capable && pbm->all_devs_66mhz;
- for (walk = walk->next; walk != &pbus->devices; walk = walk->next)
- pdev_setup_busmastering(pci_dev_b(walk), is_66mhz);
+ list_for_each_entry(dev, &pbus->devices, bus_list)
+ pdev_setup_busmastering(dev, is_66mhz);
- walk = &pbus->children;
- for (walk = walk->next; walk != &pbus->children; walk = walk->next)
- pci_setup_busmastering(pbm, pci_bus_b(walk));
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_setup_busmastering(pbm, bus);
}
void pci_register_legacy_regions(struct resource *io_res,
@@ -987,10 +966,10 @@ void pci_scan_for_target_abort(struct pci_controller_info *p,
struct pci_pbm_info *pbm,
struct pci_bus *pbus)
{
- struct list_head *walk = &pbus->devices;
+ struct pci_dev *pdev;
+ struct pci_bus *bus;
- for (walk = walk->next; walk != &pbus->devices; walk = walk->next) {
- struct pci_dev *pdev = pci_dev_b(walk);
+ list_for_each_entry(pdev, &pbus->devices, bus_list) {
u16 status, error_bits;
pci_read_config_word(pdev, PCI_STATUS, &status);
@@ -1005,19 +984,18 @@ void pci_scan_for_target_abort(struct pci_controller_info *p,
}
}
- walk = &pbus->children;
- for (walk = walk->next; walk != &pbus->children; walk = walk->next)
- pci_scan_for_target_abort(p, pbm, pci_bus_b(walk));
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_scan_for_target_abort(p, pbm, bus);
}
void pci_scan_for_master_abort(struct pci_controller_info *p,
struct pci_pbm_info *pbm,
struct pci_bus *pbus)
{
- struct list_head *walk = &pbus->devices;
+ struct pci_dev *pdev;
+ struct pci_bus *bus;
- for (walk = walk->next; walk != &pbus->devices; walk = walk->next) {
- struct pci_dev *pdev = pci_dev_b(walk);
+ list_for_each_entry(pdev, &pbus->devices, bus_list) {
u16 status, error_bits;
pci_read_config_word(pdev, PCI_STATUS, &status);
@@ -1031,19 +1009,18 @@ void pci_scan_for_master_abort(struct pci_controller_info *p,
}
}
- walk = &pbus->children;
- for (walk = walk->next; walk != &pbus->children; walk = walk->next)
- pci_scan_for_master_abort(p, pbm, pci_bus_b(walk));
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_scan_for_master_abort(p, pbm, bus);
}
void pci_scan_for_parity_error(struct pci_controller_info *p,
struct pci_pbm_info *pbm,
struct pci_bus *pbus)
{
- struct list_head *walk = &pbus->devices;
+ struct pci_dev *pdev;
+ struct pci_bus *bus;
- for (walk = walk->next; walk != &pbus->devices; walk = walk->next) {
- struct pci_dev *pdev = pci_dev_b(walk);
+ list_for_each_entry(pdev, &pbus->devices, bus_list) {
u16 status, error_bits;
pci_read_config_word(pdev, PCI_STATUS, &status);
@@ -1058,7 +1035,6 @@ void pci_scan_for_parity_error(struct pci_controller_info *p,
}
}
- walk = &pbus->children;
- for (walk = walk->next; walk != &pbus->children; walk = walk->next)
- pci_scan_for_parity_error(p, pbm, pci_bus_b(walk));
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_scan_for_parity_error(p, pbm, bus);
}
diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc64/kernel/pci_impl.h
index 302a0a2ce379..6c3205962544 100644
--- a/arch/sparc64/kernel/pci_impl.h
+++ b/arch/sparc64/kernel/pci_impl.h
@@ -11,7 +11,6 @@
#include <linux/spinlock.h>
#include <asm/io.h>
-extern spinlock_t pci_controller_lock;
extern struct pci_controller_info *pci_controller_root;
extern int pci_num_controllers;
diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c
index 3e1e1dae3516..bdf4d4fca00c 100644
--- a/arch/sparc64/kernel/pci_psycho.c
+++ b/arch/sparc64/kernel/pci_psycho.c
@@ -1487,22 +1487,18 @@ void __init psycho_init(int node, char *model_name)
struct linux_prom64_registers pr_regs[3];
struct pci_controller_info *p;
struct pci_iommu *iommu;
- unsigned long flags;
u32 upa_portid;
int is_pbm_a, err;
upa_portid = prom_getintdefault(node, "upa-portid", 0xff);
- spin_lock_irqsave(&pci_controller_lock, flags);
for(p = pci_controller_root; p; p = p->next) {
if (p->pbm_A.portid == upa_portid) {
- spin_unlock_irqrestore(&pci_controller_lock, flags);
is_pbm_a = (p->pbm_A.prom_node == 0);
psycho_pbm_init(p, node, is_pbm_a);
return;
}
}
- spin_unlock_irqrestore(&pci_controller_lock, flags);
p = kmalloc(sizeof(struct pci_controller_info), GFP_ATOMIC);
if (!p) {
@@ -1518,10 +1514,8 @@ void __init psycho_init(int node, char *model_name)
memset(iommu, 0, sizeof(*iommu));
p->pbm_A.iommu = p->pbm_B.iommu = iommu;
- spin_lock_irqsave(&pci_controller_lock, flags);
p->next = pci_controller_root;
pci_controller_root = p;
- spin_unlock_irqrestore(&pci_controller_lock, flags);
p->pbm_A.portid = upa_portid;
p->pbm_B.portid = upa_portid;
diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c
index 7d6e5149a4e9..2bf247792fe5 100644
--- a/arch/sparc64/kernel/pci_sabre.c
+++ b/arch/sparc64/kernel/pci_sabre.c
@@ -1113,10 +1113,9 @@ static void __init sabre_base_address_update(struct pci_dev *pdev, int resource)
static void __init apb_init(struct pci_controller_info *p, struct pci_bus *sabre_bus)
{
- struct list_head *walk = &sabre_bus->devices;
+ struct pci_dev *pdev;
- for (walk = walk->next; walk != &sabre_bus->devices; walk = walk->next) {
- struct pci_dev *pdev = pci_dev_b(walk);
+ list_for_each_entry(pdev, &sabre_bus->devices, bus_list) {
if (pdev->vendor == PCI_VENDOR_ID_SUN &&
pdev->device == PCI_DEVICE_ID_SUN_SIMBA) {
@@ -1178,10 +1177,9 @@ static struct pcidev_cookie *alloc_bridge_cookie(struct pci_pbm_info *pbm)
static void __init sabre_scan_bus(struct pci_controller_info *p)
{
static int once;
- struct pci_bus *sabre_bus;
+ struct pci_bus *sabre_bus, *pbus;
struct pci_pbm_info *pbm;
struct pcidev_cookie *cookie;
- struct list_head *walk;
int sabres_scanned;
/* The APB bridge speaks to the Sabre host PCI bridge
@@ -1217,9 +1215,7 @@ static void __init sabre_scan_bus(struct pci_controller_info *p)
sabres_scanned = 0;
- walk = &sabre_bus->children;
- for (walk = walk->next; walk != &sabre_bus->children; walk = walk->next) {
- struct pci_bus *pbus = pci_bus_b(walk);
+ list_for_each_entry(pbus, &sabre_bus->children, node) {
if (pbus->number == p->pbm_A.pci_first_busno) {
pbm = &p->pbm_A;
@@ -1554,7 +1550,6 @@ void __init sabre_init(int pnode, char *model_name)
struct linux_prom64_registers pr_regs[2];
struct pci_controller_info *p;
struct pci_iommu *iommu;
- unsigned long flags;
int tsbsize, err;
u32 busrange[2];
u32 vdma[2];
@@ -1602,10 +1597,8 @@ void __init sabre_init(int pnode, char *model_name)
upa_portid = prom_getintdefault(pnode, "upa-portid", 0xff);
- spin_lock_irqsave(&pci_controller_lock, flags);
p->next = pci_controller_root;
pci_controller_root = p;
- spin_unlock_irqrestore(&pci_controller_lock, flags);
p->pbm_A.portid = upa_portid;
p->pbm_B.portid = upa_portid;
diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c
index 3f90de646f7f..5f7dea84cd73 100644
--- a/arch/sparc64/kernel/pci_schizo.c
+++ b/arch/sparc64/kernel/pci_schizo.c
@@ -2081,13 +2081,11 @@ static void __init __schizo_init(int node, char *model_name, int chip_type)
{
struct pci_controller_info *p;
struct pci_iommu *iommu;
- unsigned long flags;
int is_pbm_a;
u32 portid;
portid = prom_getintdefault(node, "portid", 0xff);
- spin_lock_irqsave(&pci_controller_lock, flags);
for(p = pci_controller_root; p; p = p->next) {
struct pci_pbm_info *pbm;
@@ -2099,13 +2097,11 @@ static void __init __schizo_init(int node, char *model_name, int chip_type)
&p->pbm_B);
if (portid_compare(pbm->portid, portid, chip_type)) {
- spin_unlock_irqrestore(&pci_controller_lock, flags);
is_pbm_a = (p->pbm_A.prom_node == 0);
schizo_pbm_init(p, node, portid, chip_type);
return;
}
}
- spin_unlock_irqrestore(&pci_controller_lock, flags);
p = kmalloc(sizeof(struct pci_controller_info), GFP_ATOMIC);
if (!p) {
@@ -2130,10 +2126,8 @@ static void __init __schizo_init(int node, char *model_name, int chip_type)
memset(iommu, 0, sizeof(*iommu));
p->pbm_B.iommu = iommu;
- spin_lock_irqsave(&pci_controller_lock, flags);
p->next = pci_controller_root;
pci_controller_root = p;
- spin_unlock_irqrestore(&pci_controller_lock, flags);
p->index = pci_num_controllers++;
p->pbms_same_domain = 0;
diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c
index 3333571e736a..f8bf70233fe7 100644
--- a/arch/sparc64/kernel/signal32.c
+++ b/arch/sparc64/kernel/signal32.c
@@ -86,9 +86,62 @@ struct new_signal_frame32 {
__siginfo_fpu_t fpu_state;
};
+struct siginfo32 {
+ int si_signo;
+ int si_errno;
+ int si_code;
+
+ union {
+ int _pad[SI_PAD_SIZE32];
+
+ /* kill() */
+ struct {
+ compat_pid_t _pid; /* sender's pid */
+ unsigned int _uid; /* sender's uid */
+ } _kill;
+
+ /* POSIX.1b timers */
+ struct {
+ timer_t _tid; /* timer id */
+ int _overrun; /* overrun count */
+ sigval_t32 _sigval; /* same as below */
+ int _sys_private; /* not to be passed to user */
+ } _timer;
+
+ /* POSIX.1b signals */
+ struct {
+ compat_pid_t _pid; /* sender's pid */
+ unsigned int _uid; /* sender's uid */
+ sigval_t32 _sigval;
+ } _rt;
+
+ /* SIGCHLD */
+ struct {
+ compat_pid_t _pid; /* which child */
+ unsigned int _uid; /* sender's uid */
+ int _status; /* exit code */
+ compat_clock_t _utime;
+ compat_clock_t _stime;
+ struct compat_rusage _rusage;
+ } _sigchld;
+
+ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGEMT */
+ struct {
+ u32 _addr; /* faulting insn/memory ref. */
+ int _trapno;
+ } _sigfault;
+
+ /* SIGPOLL */
+ struct {
+ int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
+ int _fd;
+ } _sigpoll;
+ } _sifields;
+};
+
struct rt_signal_frame32 {
struct sparc_stackf32 ss;
- siginfo_t32 info;
+ struct siginfo32 info;
struct pt_regs32 regs;
compat_sigset_t mask;
/* __siginfo_fpu32_t * */ u32 fpu_save;
@@ -105,11 +158,11 @@ struct rt_signal_frame32 {
#define NF_ALIGNEDSZ (((sizeof(struct new_signal_frame32) + 7) & (~7)))
#define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame32) + 7) & (~7)))
-int copy_siginfo_to_user32(siginfo_t32 __user *to, siginfo_t *from)
+int copy_siginfo_to_user32(struct siginfo32 __user *to, siginfo_t *from)
{
int err;
- if (!access_ok(VERIFY_WRITE, to, sizeof(siginfo_t32)))
+ if (!access_ok(VERIFY_WRITE, to, sizeof(struct siginfo32)))
return -EFAULT;
/* If you change siginfo_t structure, please be sure
@@ -135,6 +188,8 @@ int copy_siginfo_to_user32(siginfo_t32 __user *to, siginfo_t *from)
err |= __put_user(from->si_utime, &to->si_utime);
err |= __put_user(from->si_stime, &to->si_stime);
err |= __put_user(from->si_status, &to->si_status);
+ err |= put_compat_rusage(&from->si_rusage,
+ &to->si_rusage);
default:
err |= __put_user(from->si_pid, &to->si_pid);
err |= __put_user(from->si_uid, &to->si_uid);
@@ -155,6 +210,22 @@ int copy_siginfo_to_user32(siginfo_t32 __user *to, siginfo_t *from)
return err;
}
+/* CAUTION: This is just a very minimalist implementation for the
+ * sake of compat_sys_rt_sigqueueinfo()
+ */
+int copy_siginfo_to_kernel32(siginfo_t *to, struct siginfo32 __user *from)
+{
+ if (!access_ok(VERIFY_WRITE, from, sizeof(struct siginfo32)))
+ return -EFAULT;
+
+ if (copy_from_user(to, from, 3*sizeof(int)) ||
+ copy_from_user(to->_sifields._pad, from->_sifields._pad,
+ SI_PAD_SIZE))
+ return -EFAULT;
+
+ return 0;
+}
+
/*
* atomically swap in the new signal mask, and wait for a signal.
* This is really tricky on the Sparc, watch out...
diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c
index 56e277383c36..80d65ffc0066 100644
--- a/arch/sparc64/kernel/sys_sparc32.c
+++ b/arch/sparc64/kernel/sys_sparc32.c
@@ -1045,7 +1045,7 @@ asmlinkage long sys32_rt_sigpending(compat_sigset_t __user *set,
}
asmlinkage long sys32_rt_sigtimedwait(compat_sigset_t __user *uthese,
- siginfo_t32 __user *uinfo,
+ struct siginfo32 __user *uinfo,
struct compat_timespec __user *uts,
compat_size_t sigsetsize)
{
@@ -1130,15 +1130,15 @@ asmlinkage long sys32_rt_sigtimedwait(compat_sigset_t __user *uthese,
}
asmlinkage long compat_sys_rt_sigqueueinfo(int pid, int sig,
- siginfo_t32 __user *uinfo)
+ struct siginfo32 __user *uinfo)
{
siginfo_t info;
int ret;
mm_segment_t old_fs = get_fs();
- if (copy_from_user (&info, uinfo, 3*sizeof(int)) ||
- copy_from_user (info._sifields._pad, uinfo->_sifields._pad, SI_PAD_SIZE))
+ if (copy_siginfo_to_kernel32(&info, uinfo))
return -EFAULT;
+
set_fs (KERNEL_DS);
ret = sys_rt_sigqueueinfo(pid, sig, (siginfo_t __user *) &info);
set_fs (old_fs);
@@ -1736,3 +1736,23 @@ sys32_timer_create(u32 clock, struct sigevent32 __user *se32,
return err;
}
+asmlinkage long compat_sys_waitid(u32 which, u32 pid,
+ struct siginfo32 __user *uinfo, u32 options)
+{
+ siginfo_t info;
+ long ret;
+ mm_segment_t old_fs = get_fs();
+
+ memset(&info, 0, sizeof(info));
+
+ set_fs (KERNEL_DS);
+ ret = sys_waitid((int)which, (compat_pid_t) pid,
+ (siginfo_t __user *) &info, (int) options);
+ set_fs (old_fs);
+
+ if (ret < 0 || info.si_signo == 0)
+ return ret;
+ BUG_ON(info.si_code & __SI_MASK);
+ info.si_code |= __SI_CHLD;
+ return copy_siginfo_to_user32(uinfo, &info);
+}
diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S
index a937ab707501..8fe732b3447f 100644
--- a/arch/sparc64/kernel/systbls.S
+++ b/arch/sparc64/kernel/systbls.S
@@ -75,7 +75,7 @@ sys_call_table32:
/*260*/ .word compat_sys_sched_getaffinity, compat_sys_sched_setaffinity, sys32_timer_settime, compat_timer_gettime, sys_timer_getoverrun
.word sys_timer_delete, sys32_timer_create, sys_ni_syscall, compat_sys_io_setup, sys_io_destroy
/*270*/ .word sys32_io_submit, sys_io_cancel, compat_sys_io_getevents, sys32_mq_open, sys_mq_unlink
- .word sys_mq_timedsend, sys_mq_timedreceive, compat_sys_mq_notify, compat_sys_mq_getsetattr, sys_ni_syscall
+ .word sys_mq_timedsend, sys_mq_timedreceive, compat_sys_mq_notify, compat_sys_mq_getsetattr, compat_sys_waitid
/*280*/ .word sys_ni_syscall, sys_ni_syscall, sys_ni_syscall
#endif /* CONFIG_COMPAT */
@@ -141,7 +141,7 @@ sys_call_table:
/*260*/ .word sys_sched_getaffinity, sys_sched_setaffinity, sys_timer_settime, sys_timer_gettime, sys_timer_getoverrun
.word sys_timer_delete, sys_timer_create, sys_ni_syscall, sys_io_setup, sys_io_destroy
/*270*/ .word sys_io_submit, sys_io_cancel, sys_io_getevents, sys_mq_open, sys_mq_unlink
- .word sys_mq_timedsend, sys_mq_timedreceive, sys_mq_notify, sys_mq_getsetattr, sys_ni_syscall
+ .word sys_mq_timedsend, sys_mq_timedreceive, sys_mq_notify, sys_mq_getsetattr, sys_waitid
/*280*/ .word sys_ni_syscall, sys_ni_syscall, sys_ni_syscall
#if defined(CONFIG_SUNOS_EMUL) || defined(CONFIG_SOLARIS_EMUL) || \
diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c
index 473901974d99..6f5c7fdb5a5c 100644
--- a/arch/sparc64/kernel/traps.c
+++ b/arch/sparc64/kernel/traps.c
@@ -36,10 +36,24 @@
#include <asm/psrcompat.h>
#include <asm/processor.h>
#include <asm/timer.h>
+#include <asm/kdebug.h>
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif
+struct notifier_block *sparc64die_chain;
+static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED;
+
+int register_die_notifier(struct notifier_block *nb)
+{
+ int err = 0;
+ unsigned long flags;
+ spin_lock_irqsave(&die_notifier_lock, flags);
+ err = notifier_chain_register(&sparc64die_chain, nb);
+ spin_unlock_irqrestore(&die_notifier_lock, flags);
+ return err;
+}
+
/* When an irrecoverable trap occurs at tl > 0, the trap entry
* code logs the trap state registers at every level in the trap
* stack. It is found at (pt_regs + sizeof(pt_regs)) and the layout
@@ -71,11 +85,20 @@ static void dump_tl1_traplog(struct tl1_traplog *p)
}
}
-void bad_trap (struct pt_regs *regs, long lvl)
+void do_call_debug(struct pt_regs *regs)
+{
+ notify_die(DIE_CALL, "debug call", regs, 0, 255, SIGINT);
+}
+
+void bad_trap(struct pt_regs *regs, long lvl)
{
char buffer[32];
siginfo_t info;
+ if (notify_die(DIE_TRAP, "bad trap", regs,
+ 0, lvl, SIGTRAP) == NOTIFY_OK)
+ return;
+
if (lvl < 0x100) {
sprintf(buffer, "Bad hw trap %lx at tl0\n", lvl);
die_if_kernel(buffer, regs);
@@ -84,7 +107,7 @@ void bad_trap (struct pt_regs *regs, long lvl)
lvl -= 0x100;
if (regs->tstate & TSTATE_PRIV) {
sprintf(buffer, "Kernel bad sw trap %lx", lvl);
- die_if_kernel (buffer, regs);
+ die_if_kernel(buffer, regs);
}
if (test_thread_flag(TIF_32BIT)) {
regs->tpc &= 0xffffffff;
@@ -98,10 +121,14 @@ void bad_trap (struct pt_regs *regs, long lvl)
force_sig_info(SIGILL, &info, current);
}
-void bad_trap_tl1 (struct pt_regs *regs, long lvl)
+void bad_trap_tl1(struct pt_regs *regs, long lvl)
{
char buffer[32];
+ if (notify_die(DIE_TRAP_TL1, "bad trap tl1", regs,
+ 0, lvl, SIGTRAP) == NOTIFY_OK)
+ return;
+
dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
sprintf (buffer, "Bad trap %lx at tl>0", lvl);
@@ -121,6 +148,10 @@ void instruction_access_exception(struct pt_regs *regs,
{
siginfo_t info;
+ if (notify_die(DIE_TRAP, "instruction access exception", regs,
+ 0, 0x8, SIGTRAP) == NOTIFY_OK)
+ return;
+
if (regs->tstate & TSTATE_PRIV) {
printk("instruction_access_exception: SFSR[%016lx] SFAR[%016lx], going.\n",
sfsr, sfar);
@@ -141,15 +172,23 @@ void instruction_access_exception(struct pt_regs *regs,
void instruction_access_exception_tl1(struct pt_regs *regs,
unsigned long sfsr, unsigned long sfar)
{
+ if (notify_die(DIE_TRAP_TL1, "instruction access exception tl1", regs,
+ 0, 0x8, SIGTRAP) == NOTIFY_OK)
+ return;
+
dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
instruction_access_exception(regs, sfsr, sfar);
}
-void data_access_exception (struct pt_regs *regs,
- unsigned long sfsr, unsigned long sfar)
+void data_access_exception(struct pt_regs *regs,
+ unsigned long sfsr, unsigned long sfar)
{
siginfo_t info;
+ if (notify_die(DIE_TRAP, "data access exception", regs,
+ 0, 0x30, SIGTRAP) == NOTIFY_OK)
+ return;
+
if (regs->tstate & TSTATE_PRIV) {
/* Test if this comes from uaccess places. */
unsigned long fixup;
@@ -220,6 +259,10 @@ void do_iae(struct pt_regs *regs)
spitfire_clean_and_reenable_l1_caches();
+ if (notify_die(DIE_TRAP, "instruction access exception", regs,
+ 0, 0x8, SIGTRAP) == NOTIFY_OK)
+ return;
+
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_OBJERR;
@@ -230,6 +273,8 @@ void do_iae(struct pt_regs *regs)
void do_dae(struct pt_regs *regs)
{
+ siginfo_t info;
+
#ifdef CONFIG_PCI
if (pci_poke_in_progress && pci_poke_cpu == smp_processor_id()) {
spitfire_clean_and_reenable_l1_caches();
@@ -244,7 +289,18 @@ void do_dae(struct pt_regs *regs)
return;
}
#endif
- do_iae(regs);
+ spitfire_clean_and_reenable_l1_caches();
+
+ if (notify_die(DIE_TRAP, "data access exception", regs,
+ 0, 0x30, SIGTRAP) == NOTIFY_OK)
+ return;
+
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_OBJERR;
+ info.si_addr = (void *)0;
+ info.si_trapno = 0;
+ force_sig_info(SIGBUS, &info, current);
}
static char ecc_syndrome_table[] = {
@@ -1638,6 +1694,10 @@ void do_fpe_common(struct pt_regs *regs)
void do_fpieee(struct pt_regs *regs)
{
+ if (notify_die(DIE_TRAP, "fpu exception ieee", regs,
+ 0, 0x24, SIGFPE) == NOTIFY_OK)
+ return;
+
do_fpe_common(regs);
}
@@ -1648,6 +1708,10 @@ void do_fpother(struct pt_regs *regs)
struct fpustate *f = FPUSTATE;
int ret = 0;
+ if (notify_die(DIE_TRAP, "fpu exception other", regs,
+ 0, 0x25, SIGFPE) == NOTIFY_OK)
+ return;
+
switch ((current_thread_info()->xfsr[0] & 0x1c000)) {
case (2 << 14): /* unfinished_FPop */
case (3 << 14): /* unimplemented_FPop */
@@ -1663,6 +1727,10 @@ void do_tof(struct pt_regs *regs)
{
siginfo_t info;
+ if (notify_die(DIE_TRAP, "tagged arithmetic overflow", regs,
+ 0, 0x26, SIGEMT) == NOTIFY_OK)
+ return;
+
if (regs->tstate & TSTATE_PRIV)
die_if_kernel("Penguin overflow trap from kernel mode", regs);
if (test_thread_flag(TIF_32BIT)) {
@@ -1681,6 +1749,10 @@ void do_div0(struct pt_regs *regs)
{
siginfo_t info;
+ if (notify_die(DIE_TRAP, "integer division by zero", regs,
+ 0, 0x28, SIGFPE) == NOTIFY_OK)
+ return;
+
if (regs->tstate & TSTATE_PRIV)
die_if_kernel("TL0: Kernel divide by zero.", regs);
if (test_thread_flag(TIF_32BIT)) {
@@ -1786,6 +1858,7 @@ void die_if_kernel(char *str, struct pt_regs *regs)
" \\__U_/\n");
printk("%s(%d): %s [#%d]\n", current->comm, current->pid, str, ++die_counter);
+ notify_die(DIE_OOPS, str, regs, 0, 255, SIGSEGV);
__asm__ __volatile__("flushw");
__show_regs(regs);
if (regs->tstate & TSTATE_PRIV) {
@@ -1834,6 +1907,10 @@ void do_illegal_instruction(struct pt_regs *regs)
u32 insn;
siginfo_t info;
+ if (notify_die(DIE_TRAP, "illegal instruction", regs,
+ 0, 0x10, SIGILL) == NOTIFY_OK)
+ return;
+
if (tstate & TSTATE_PRIV)
die_if_kernel("Kernel illegal instruction", regs);
if (test_thread_flag(TIF_32BIT))
@@ -1859,6 +1936,10 @@ void mem_address_unaligned(struct pt_regs *regs, unsigned long sfar, unsigned lo
{
siginfo_t info;
+ if (notify_die(DIE_TRAP, "memory address unaligned", regs,
+ 0, 0x34, SIGSEGV) == NOTIFY_OK)
+ return;
+
if (regs->tstate & TSTATE_PRIV) {
extern void kernel_unaligned_trap(struct pt_regs *regs,
unsigned int insn,
@@ -1881,6 +1962,10 @@ void do_privop(struct pt_regs *regs)
{
siginfo_t info;
+ if (notify_die(DIE_TRAP, "privileged operation", regs,
+ 0, 0x11, SIGILL) == NOTIFY_OK)
+ return;
+
if (test_thread_flag(TIF_32BIT)) {
regs->tpc &= 0xffffffff;
regs->tnpc &= 0xffffffff;
diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S
index 40f7919aa171..491bb3681f9d 100644
--- a/arch/sparc64/kernel/ttable.S
+++ b/arch/sparc64/kernel/ttable.S
@@ -158,7 +158,7 @@ tl0_resv164: BTRAP(0x164) BTRAP(0x165) BTRAP(0x166) BTRAP(0x167) BTRAP(0x168)
tl0_resv169: BTRAP(0x169) BTRAP(0x16a) BTRAP(0x16b) BTRAP(0x16c)
tl0_linux64: LINUX_64BIT_SYSCALL_TRAP
tl0_gsctx: TRAP(sparc64_get_context) TRAP(sparc64_set_context)
-tl0_resv170: BTRAP(0x170) BTRAP(0x171) BTRAP(0x172)
+tl0_resv170: KPROBES_TRAP(0x170) KPROBES_TRAP(0x171) BTRAP(0x172)
tl0_resv173: BTRAP(0x173) BTRAP(0x174) BTRAP(0x175) BTRAP(0x176) BTRAP(0x177)
tl0_resv178: BTRAP(0x178) BTRAP(0x179) BTRAP(0x17a) BTRAP(0x17b) BTRAP(0x17c)
tl0_resv17d: BTRAP(0x17d) BTRAP(0x17e) BTRAP(0x17f)
diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c
index 49c3dd29a5b6..5d9a0f5f275f 100644
--- a/arch/sparc64/mm/fault.c
+++ b/arch/sparc64/mm/fault.c
@@ -27,6 +27,7 @@
#include <asm/asi.h>
#include <asm/lsu.h>
#include <asm/sections.h>
+#include <asm/kdebug.h>
#define ELEMENTS(arr) (sizeof (arr)/sizeof (arr[0]))
@@ -147,6 +148,9 @@ static void unhandled_fault(unsigned long address, struct task_struct *tsk,
printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %016lx\n",
(tsk->mm ? (unsigned long) tsk->mm->pgd :
(unsigned long) tsk->active_mm->pgd));
+ if (notify_die(DIE_GPF, "general protection fault", regs,
+ 0, 0, SIGSEGV) == NOTIFY_OK)
+ return;
die_if_kernel("Oops", regs);
}
@@ -318,8 +322,13 @@ asmlinkage void do_sparc64_fault(struct pt_regs *regs)
int si_code, fault_code;
unsigned long address;
- si_code = SEGV_MAPERR;
fault_code = get_thread_fault_code();
+
+ if (notify_die(DIE_PAGE_FAULT, "page_fault", regs,
+ fault_code, 0, SIGSEGV) == NOTIFY_OK)
+ return;
+
+ si_code = SEGV_MAPERR;
address = current_thread_info()->fault_address;
if ((fault_code & FAULT_CODE_ITLB) &&
diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c
index 4a3db02e1e2e..a7d097546901 100644
--- a/arch/sparc64/solaris/misc.c
+++ b/arch/sparc64/solaris/misc.c
@@ -137,21 +137,34 @@ asmlinkage int solaris_brk(u32 brk)
return sunos_brk(brk);
}
-#define set_utsfield(to, from, dotchop, countfrom) { \
- char *p; \
- int i, len = (countfrom) ? \
- ((sizeof(to) > sizeof(from) ? \
- sizeof(from) : sizeof(to))) : sizeof(to); \
- if (copy_to_user(to, from, len)) \
- return -EFAULT; \
- if (dotchop) \
- for (p=from,i=0; *p && *p != '.' && --len; p++,i++); \
- else \
- i = len - 1; \
- if (__put_user('\0', (char __user *)((to)+i))) \
- return -EFAULT; \
+static int __set_utsfield(char __user *to, int to_size,
+ const char *from, int from_size,
+ int dotchop, int countfrom)
+{
+ int len = countfrom ? (to_size > from_size ?
+ from_size : to_size) : to_size;
+ int off;
+
+ if (copy_to_user(to, from, len))
+ return -EFAULT;
+
+ if (dotchop) {
+ off = (strnchr(from, len, '.') - from);
+ } else{
+ off = len - 1;
+ }
+
+ if (__put_user('\0', to + off))
+ return -EFAULT;
+
+ return 0;
}
+#define set_utsfield(to, from, dotchop, countfrom) \
+ __set_utsfield((to), sizeof(to), \
+ (from), sizeof(from), \
+ (dotchop), (countfrom))
+
struct sol_uname {
char sysname[9];
char nodename[9];
@@ -219,17 +232,20 @@ static char *serial(char *buffer)
asmlinkage int solaris_utssys(u32 buf, u32 flags, int which, u32 buf2)
{
struct sol_uname __user *v = A(buf);
+ int err;
+
switch (which) {
case 0: /* old uname */
/* Let's cheat */
- set_utsfield(v->sysname, "SunOS", 1, 0);
+ err = set_utsfield(v->sysname, "SunOS", 1, 0);
down_read(&uts_sem);
- set_utsfield(v->nodename, system_utsname.nodename, 1, 1);
+ err |= set_utsfield(v->nodename, system_utsname.nodename,
+ 1, 1);
up_read(&uts_sem);
- set_utsfield(v->release, "2.6", 0, 0);
- set_utsfield(v->version, "Generic", 0, 0);
- set_utsfield(v->machine, machine(), 0, 0);
- return 0;
+ err |= set_utsfield(v->release, "2.6", 0, 0);
+ err |= set_utsfield(v->version, "Generic", 0, 0);
+ err |= set_utsfield(v->machine, machine(), 0, 0);
+ return (err ? -EFAULT : 0);
case 2: /* ustat */
return -ENOSYS;
case 3: /* fusers */
@@ -242,15 +258,18 @@ asmlinkage int solaris_utssys(u32 buf, u32 flags, int which, u32 buf2)
asmlinkage int solaris_utsname(u32 buf)
{
struct sol_utsname __user *v = A(buf);
+ int err;
+
/* Why should we not lie a bit? */
down_read(&uts_sem);
- set_utsfield(v->sysname, "SunOS", 0, 0);
- set_utsfield(v->nodename, system_utsname.nodename, 1, 1);
- set_utsfield(v->release, "5.6", 0, 0);
- set_utsfield(v->version, "Generic", 0, 0);
- set_utsfield(v->machine, machine(), 0, 0);
+ err = set_utsfield(v->sysname, "SunOS", 0, 0);
+ err |= set_utsfield(v->nodename, system_utsname.nodename, 1, 1);
+ err |= set_utsfield(v->release, "5.6", 0, 0);
+ err |= set_utsfield(v->version, "Generic", 0, 0);
+ err |= set_utsfield(v->machine, machine(), 0, 0);
up_read(&uts_sem);
- return 0;
+
+ return (err ? -EFAULT : 0);
}
#define SI_SYSNAME 1 /* return name of operating system */
diff --git a/include/asm-sparc/unistd.h b/include/asm-sparc/unistd.h
index 62eaacd9fe2f..f73f7e660190 100644
--- a/include/asm-sparc/unistd.h
+++ b/include/asm-sparc/unistd.h
@@ -290,11 +290,12 @@
#define __NR_io_cancel 271
#define __NR_io_getevents 272
#define __NR_mq_open 273
-#define __NR_mq_unlink (__NR_mq_open+1)
-#define __NR_mq_timedsend (__NR_mq_open+2)
-#define __NR_mq_timedreceive (__NR_mq_open+3)
-#define __NR_mq_notify (__NR_mq_open+4)
-#define __NR_mq_getsetattr (__NR_mq_open+5)
+#define __NR_mq_unlink 274
+#define __NR_mq_timedsend 275
+#define __NR_mq_timedreceive 276
+#define __NR_mq_notify 277
+#define __NR_mq_getsetattr 278
+#define __NR_waitid 279
/* WARNING: You MAY NOT add syscall numbers larger than 282, since
* all of the syscall tables in the Sparc kernel are
diff --git a/include/asm-sparc64/kdebug.h b/include/asm-sparc64/kdebug.h
index 65862f7c4dc3..f70d3dad01f9 100644
--- a/include/asm-sparc64/kdebug.h
+++ b/include/asm-sparc64/kdebug.h
@@ -1,9 +1,52 @@
#ifndef _SPARC64_KDEBUG_H
#define _SPARC64_KDEBUG_H
-/*
- * No kernel debugger on sparc64. Kept here because drivers/sbus/char/
- * includes it for sparc32 sake.
+/* Nearly identical to x86_64/i386 code. */
+
+#include <linux/notifier.h>
+
+struct pt_regs;
+
+struct die_args {
+ struct pt_regs *regs;
+ const char *str;
+ long err;
+ int trapnr;
+ int signr;
+};
+
+/* Note - you should never unregister because that can race with NMIs.
+ * If you really want to do it first unregister - then synchronize_kernel
+ * - then free.
*/
+int register_die_notifier(struct notifier_block *nb);
+extern struct notifier_block *sparc64die_chain;
+
+extern void bad_trap(struct pt_regs *, long);
+
+/* Grossly misnamed. */
+enum die_val {
+ DIE_OOPS = 1,
+ DIE_DEBUG, /* ta 0x70 */
+ DIE_DEBUG_2, /* ta 0x71 */
+ DIE_DIE,
+ DIE_TRAP,
+ DIE_TRAP_TL1,
+ DIE_GPF,
+ DIE_CALL,
+ DIE_PAGE_FAULT,
+};
+
+static inline int notify_die(enum die_val val,char *str, struct pt_regs *regs,
+ long err, int trap, int sig)
+{
+ struct die_args args = { .regs = regs,
+ .str = str,
+ .err = err,
+ .trapnr = trap,
+ .signr = sig };
+
+ return notifier_call_chain(&sparc64die_chain, val, &args);
+}
#endif
diff --git a/include/asm-sparc64/kprobes.h b/include/asm-sparc64/kprobes.h
new file mode 100644
index 000000000000..42b4cc6e424f
--- /dev/null
+++ b/include/asm-sparc64/kprobes.h
@@ -0,0 +1,24 @@
+#ifndef _SPARC64_KPROBES_H
+#define _SPARC64_KPROBES_H
+
+#include <linux/config.h>
+#include <linux/types.h>
+
+typedef u32 kprobe_opcode_t;
+
+#define BREAKPOINT_INSTRUCTION 0x91d02070 /* ta 0x70 */
+#define BREAKPOINT_INSTRUCTION_2 0x91d02071 /* ta 0x71 */
+#define MAX_INSN_SIZE 2
+
+#ifdef CONFIG_KPROBES
+extern int kprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data);
+#else /* !CONFIG_KPROBES */
+static inline int kprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ return 0;
+}
+#endif
+
+#endif /* _SPARC64_KPROBES_H */
diff --git a/include/asm-sparc64/siginfo.h b/include/asm-sparc64/siginfo.h
index fde85211a008..94b272f5d1b3 100644
--- a/include/asm-sparc64/siginfo.h
+++ b/include/asm-sparc64/siginfo.h
@@ -24,57 +24,8 @@ typedef union sigval32 {
u32 sival_ptr;
} sigval_t32;
-typedef struct siginfo32 {
- int si_signo;
- int si_errno;
- int si_code;
+struct siginfo32;
- union {
- int _pad[SI_PAD_SIZE32];
-
- /* kill() */
- struct {
- compat_pid_t _pid; /* sender's pid */
- unsigned int _uid; /* sender's uid */
- } _kill;
-
- /* POSIX.1b timers */
- struct {
- timer_t _tid; /* timer id */
- int _overrun; /* overrun count */
- sigval_t32 _sigval; /* same as below */
- int _sys_private; /* not to be passed to user */
- } _timer;
-
- /* POSIX.1b signals */
- struct {
- compat_pid_t _pid; /* sender's pid */
- unsigned int _uid; /* sender's uid */
- sigval_t32 _sigval;
- } _rt;
-
- /* SIGCHLD */
- struct {
- compat_pid_t _pid; /* which child */
- unsigned int _uid; /* sender's uid */
- int _status; /* exit code */
- compat_clock_t _utime;
- compat_clock_t _stime;
- } _sigchld;
-
- /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGEMT */
- struct {
- u32 _addr; /* faulting insn/memory ref. */
- int _trapno;
- } _sigfault;
-
- /* SIGPOLL */
- struct {
- int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
- int _fd;
- } _sigpoll;
- } _sifields;
-} siginfo_t32;
#endif /* CONFIG_COMPAT */
#endif /* __KERNEL__ */
@@ -105,7 +56,8 @@ typedef struct sigevent32 {
} _sigev_un;
} sigevent_t32;
-extern int copy_siginfo_to_user32(siginfo_t32 __user *to, siginfo_t *from);
+extern int copy_siginfo_to_user32(struct siginfo32 __user *to, siginfo_t *from);
+extern int copy_siginfo_to_kernel32(siginfo_t *to, struct siginfo32 __user *from);
#endif /* CONFIG_COMPAT */
diff --git a/include/asm-sparc64/ttable.h b/include/asm-sparc64/ttable.h
index 2b454ef67782..2784f80094c3 100644
--- a/include/asm-sparc64/ttable.h
+++ b/include/asm-sparc64/ttable.h
@@ -176,6 +176,12 @@
ba,pt %xcc, rtrap_clr_l6; \
stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC];
+#ifdef CONFIG_KPROBES
+#define KPROBES_TRAP(lvl) TRAP_IRQ(kprobe_trap, lvl)
+#else
+#define KPROBES_TRAP(lvl) TRAP_ARG(bad_trap, lvl)
+#endif
+
/* Before touching these macros, you owe it to yourself to go and
* see how arch/sparc64/kernel/winfixup.S works... -DaveM
*
diff --git a/include/asm-sparc64/unistd.h b/include/asm-sparc64/unistd.h
index 0ce57c1b46e9..645cf7208e8e 100644
--- a/include/asm-sparc64/unistd.h
+++ b/include/asm-sparc64/unistd.h
@@ -292,11 +292,13 @@
#define __NR_io_cancel 271
#define __NR_io_getevents 272
#define __NR_mq_open 273
-#define __NR_mq_unlink (__NR_mq_open+1)
-#define __NR_mq_timedsend (__NR_mq_open+2)
-#define __NR_mq_timedreceive (__NR_mq_open+3)
-#define __NR_mq_notify (__NR_mq_open+4)
-#define __NR_mq_getsetattr (__NR_mq_open+5)
+#define __NR_mq_unlink 274
+#define __NR_mq_timedsend 275
+#define __NR_mq_timedreceive 276
+#define __NR_mq_notify 277
+#define __NR_mq_getsetattr 278
+#define __NR_waitid 279
+
/* WARNING: You MAY NOT add syscall numbers larger than 282, since
* all of the syscall tables in the Sparc kernel are
* sized to have 283 entries (starting at zero). Therefore
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 9e3dc417c67c..01436a31c690 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -88,7 +88,8 @@ int register_kprobe(struct kprobe *p)
arch_prepare_kprobe(p);
p->opcode = *p->addr;
*p->addr = BREAKPOINT_INSTRUCTION;
- flush_icache_range(p->addr, p->addr + sizeof(kprobe_opcode_t));
+ flush_icache_range((unsigned long) p->addr,
+ (unsigned long) p->addr + sizeof(kprobe_opcode_t));
out:
spin_unlock_irqrestore(&kprobe_lock, flags);
return ret;
@@ -100,7 +101,8 @@ void unregister_kprobe(struct kprobe *p)
spin_lock_irqsave(&kprobe_lock, flags);
*p->addr = p->opcode;
hlist_del(&p->hlist);
- flush_icache_range(p->addr, p->addr + sizeof(kprobe_opcode_t));
+ flush_icache_range((unsigned long) p->addr,
+ (unsigned long) p->addr + sizeof(kprobe_opcode_t));
spin_unlock_irqrestore(&kprobe_lock, flags);
}