diff options
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r-- | arch/arm/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/kernel/calls.S | 1 | ||||
-rw-r--r-- | arch/arm/kernel/entry-armv.S | 35 | ||||
-rw-r--r-- | arch/arm/kernel/entry-header.S | 15 | ||||
-rw-r--r-- | arch/arm/kernel/etm.c | 641 | ||||
-rw-r--r-- | arch/arm/kernel/head-common.S | 2 | ||||
-rw-r--r-- | arch/arm/kernel/head-nommu.S | 2 | ||||
-rw-r--r-- | arch/arm/kernel/head.S | 2 | ||||
-rw-r--r-- | arch/arm/kernel/isa.c | 17 | ||||
-rw-r--r-- | arch/arm/kernel/process.c | 9 | ||||
-rw-r--r-- | arch/arm/kernel/signal.c | 45 | ||||
-rw-r--r-- | arch/arm/kernel/signal.h | 4 | ||||
-rw-r--r-- | arch/arm/kernel/smp_scu.c | 4 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 84 | ||||
-rw-r--r-- | arch/arm/kernel/unwind.c | 9 | ||||
-rw-r--r-- | arch/arm/kernel/vmlinux.lds.S | 85 |
16 files changed, 804 insertions, 153 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 79087dd6d86..e7ccf7e697c 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -17,6 +17,8 @@ obj-y := compat.o elf.o entry-armv.o entry-common.o irq.o \ process.o ptrace.o return_address.o setup.o signal.o \ sys_arm.o stacktrace.o time.o traps.o +obj-$(CONFIG_OC_ETM) += etm.o + obj-$(CONFIG_ISA_DMA_API) += dma.o obj-$(CONFIG_ARCH_ACORN) += ecard.o obj-$(CONFIG_FIQ) += fiq.o diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S index fafce1b5c69..f58c1156e77 100644 --- a/arch/arm/kernel/calls.S +++ b/arch/arm/kernel/calls.S @@ -374,6 +374,7 @@ CALL(sys_pwritev) CALL(sys_rt_tgsigqueueinfo) CALL(sys_perf_event_open) +/* 365 */ CALL(sys_recvmmsg) #ifndef syscalls_counted .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls #define syscalls_counted diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 322410be573..d2903e3bc86 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -21,6 +21,7 @@ #include <mach/entry-macro.S> #include <asm/thread_notify.h> #include <asm/unwind.h> +#include <asm/unistd.h> #include "entry-header.S" @@ -608,33 +609,33 @@ call_fpe: THUMB( add pc, r8 ) nop - W(mov) pc, lr @ CP#0 + movw_pc lr @ CP#0 W(b) do_fpe @ CP#1 (FPE) W(b) do_fpe @ CP#2 (FPE) - W(mov) pc, lr @ CP#3 + movw_pc lr @ CP#3 #ifdef CONFIG_CRUNCH b crunch_task_enable @ CP#4 (MaverickCrunch) b crunch_task_enable @ CP#5 (MaverickCrunch) b crunch_task_enable @ CP#6 (MaverickCrunch) #else - W(mov) pc, lr @ CP#4 - W(mov) pc, lr @ CP#5 - W(mov) pc, lr @ CP#6 + movw_pc lr @ CP#4 + movw_pc lr @ CP#5 + movw_pc lr @ CP#6 #endif - W(mov) pc, lr @ CP#7 - W(mov) pc, lr @ CP#8 - W(mov) pc, lr @ CP#9 + movw_pc lr @ CP#7 + movw_pc lr @ CP#8 + movw_pc lr @ CP#9 #ifdef CONFIG_VFP W(b) do_vfp @ CP#10 (VFP) W(b) do_vfp @ CP#11 (VFP) #else - W(mov) pc, lr @ CP#10 (VFP) - W(mov) pc, lr @ CP#11 (VFP) + movw_pc lr @ CP#10 (VFP) + movw_pc lr @ CP#11 (VFP) #endif - W(mov) pc, lr @ CP#12 - W(mov) pc, lr @ CP#13 - W(mov) pc, lr @ CP#14 (Debug) - W(mov) pc, lr @ CP#15 (Control) + movw_pc lr @ CP#12 + movw_pc lr @ CP#13 + movw_pc lr @ CP#14 (Debug) + movw_pc lr @ CP#15 (Control) #ifdef CONFIG_NEON .align 6 @@ -908,10 +909,10 @@ __kuser_cmpxchg: @ 0xffff0fc0 * A special ghost syscall is used for that (see traps.c). */ stmfd sp!, {r7, lr} - mov r7, #0xff00 @ 0xfff0 into r7 for EABI - orr r7, r7, #0xf0 - swi #0x9ffff0 + ldr r7, =1f @ it's 20 bits + swi __ARM_NR_cmpxchg ldmfd sp!, {r7, pc} +1: .word __ARM_NR_cmpxchg #elif __LINUX_ARM_ARCH__ < 6 diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S index ac34c0d9384..7e9ed1eea40 100644 --- a/arch/arm/kernel/entry-header.S +++ b/arch/arm/kernel/entry-header.S @@ -110,6 +110,13 @@ mov \rd, sp, lsr #13 mov \rd, \rd, lsl #13 .endm + + @ + @ 32-bit wide "mov pc, reg" + @ + .macro movw_pc, reg + mov pc, \reg + .endm #else /* CONFIG_THUMB2_KERNEL */ .macro svc_exit, rpsr clrex @ clear the exclusive monitor @@ -146,6 +153,14 @@ lsr \rd, \rd, #13 mov \rd, \rd, lsl #13 .endm + + @ + @ 32-bit wide "mov pc, reg" + @ + .macro movw_pc, reg + mov pc, \reg + nop + .endm #endif /* !CONFIG_THUMB2_KERNEL */ /* diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c new file mode 100644 index 00000000000..82775396630 --- /dev/null +++ b/arch/arm/kernel/etm.c @@ -0,0 +1,641 @@ +/* + * linux/arch/arm/kernel/etm.c + * + * Driver for ARM's Embedded Trace Macrocell and Embedded Trace Buffer. + * + * Copyright (C) 2009 Nokia Corporation. + * Alexander Shishkin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/io.h> +#include <linux/sysrq.h> +#include <linux/device.h> +#include <linux/clk.h> +#include <linux/amba/bus.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/vmalloc.h> +#include <linux/mutex.h> +#include <asm/hardware/coresight.h> +#include <asm/sections.h> + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alexander Shishkin"); + +static struct tracectx tracer; + +static inline bool trace_isrunning(struct tracectx *t) +{ + return !!(t->flags & TRACER_RUNNING); +} + +static int etm_setup_address_range(struct tracectx *t, int n, + unsigned long start, unsigned long end, int exclude, int data) +{ + u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \ + ETMAAT_NOVALCMP; + + if (n < 1 || n > t->ncmppairs) + return -EINVAL; + + /* comparators and ranges are numbered starting with 1 as opposed + * to bits in a word */ + n--; + + if (data) + flags |= ETMAAT_DLOADSTORE; + else + flags |= ETMAAT_IEXEC; + + /* first comparator for the range */ + etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2)); + etm_writel(t, start, ETMR_COMP_VAL(n * 2)); + + /* second comparator is right next to it */ + etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); + etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1)); + + flags = exclude ? ETMTE_INCLEXCL : 0; + etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL); + + return 0; +} + +static int trace_start(struct tracectx *t) +{ + u32 v; + unsigned long timeout = TRACER_TIMEOUT; + + etb_unlock(t); + + etb_writel(t, 0, ETBR_FORMATTERCTRL); + etb_writel(t, 1, ETBR_CTRL); + + etb_lock(t); + + /* configure etm */ + v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz); + + if (t->flags & TRACER_CYCLE_ACC) + v |= ETMCTRL_CYCLEACCURATE; + + etm_unlock(t); + + etm_writel(t, v, ETMR_CTRL); + + while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) + ; + if (!timeout) { + dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); + etm_lock(t); + return -EFAULT; + } + + etm_setup_address_range(t, 1, (unsigned long)_stext, + (unsigned long)_etext, 0, 0); + etm_writel(t, 0, ETMR_TRACEENCTRL2); + etm_writel(t, 0, ETMR_TRACESSCTRL); + etm_writel(t, 0x6f, ETMR_TRACEENEVT); + + v &= ~ETMCTRL_PROGRAM; + v |= ETMCTRL_PORTSEL; + + etm_writel(t, v, ETMR_CTRL); + + timeout = TRACER_TIMEOUT; + while (etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) + ; + if (!timeout) { + dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n"); + etm_lock(t); + return -EFAULT; + } + + etm_lock(t); + + t->flags |= TRACER_RUNNING; + + return 0; +} + +static int trace_stop(struct tracectx *t) +{ + unsigned long timeout = TRACER_TIMEOUT; + + etm_unlock(t); + + etm_writel(t, 0x440, ETMR_CTRL); + while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) + ; + if (!timeout) { + dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); + etm_lock(t); + return -EFAULT; + } + + etm_lock(t); + + etb_unlock(t); + etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); + + timeout = TRACER_TIMEOUT; + while (etb_readl(t, ETBR_FORMATTERCTRL) & + ETBFF_MANUAL_FLUSH && --timeout) + ; + if (!timeout) { + dev_dbg(t->dev, "Waiting for formatter flush to commence " + "timed out\n"); + etb_lock(t); + return -EFAULT; + } + + etb_writel(t, 0, ETBR_CTRL); + + etb_lock(t); + + t->flags &= ~TRACER_RUNNING; + + return 0; +} + +static int etb_getdatalen(struct tracectx *t) +{ + u32 v; + int rp, wp; + + v = etb_readl(t, ETBR_STATUS); + + if (v & 1) + return t->etb_bufsz; + + rp = etb_readl(t, ETBR_READADDR); + wp = etb_readl(t, ETBR_WRITEADDR); + + if (rp > wp) { + etb_writel(t, 0, ETBR_READADDR); + etb_writel(t, 0, ETBR_WRITEADDR); + + return 0; + } + + return wp - rp; +} + +/* sysrq+v will always stop the running trace and leave it at that */ +static void etm_dump(void) +{ + struct tracectx *t = &tracer; + u32 first = 0; + int length; + + if (!t->etb_regs) { + printk(KERN_INFO "No tracing hardware found\n"); + return; + } + + if (trace_isrunning(t)) + trace_stop(t); + + etb_unlock(t); + + length = etb_getdatalen(t); + + if (length == t->etb_bufsz) + first = etb_readl(t, ETBR_WRITEADDR); + + etb_writel(t, first, ETBR_READADDR); + + printk(KERN_INFO "Trace buffer contents length: %d\n", length); + printk(KERN_INFO "--- ETB buffer begin ---\n"); + for (; length; length--) + printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM))); + printk(KERN_INFO "\n--- ETB buffer end ---\n"); + + /* deassert the overflow bit */ + etb_writel(t, 1, ETBR_CTRL); + etb_writel(t, 0, ETBR_CTRL); + + etb_writel(t, 0, ETBR_TRIGGERCOUNT); + etb_writel(t, 0, ETBR_READADDR); + etb_writel(t, 0, ETBR_WRITEADDR); + + etb_lock(t); +} + +static void sysrq_etm_dump(int key, struct tty_struct *tty) +{ + dev_dbg(tracer.dev, "Dumping ETB buffer\n"); + etm_dump(); +} + +static struct sysrq_key_op sysrq_etm_op = { + .handler = sysrq_etm_dump, + .help_msg = "ETM buffer dump", + .action_msg = "etm", +}; + +static int etb_open(struct inode *inode, struct file *file) +{ + if (!tracer.etb_regs) + return -ENODEV; + + file->private_data = &tracer; + + return nonseekable_open(inode, file); +} + +static ssize_t etb_read(struct file *file, char __user *data, + size_t len, loff_t *ppos) +{ + int total, i; + long length; + struct tracectx *t = file->private_data; + u32 first = 0; + u32 *buf; + + mutex_lock(&t->mutex); + + if (trace_isrunning(t)) { + length = 0; + goto out; + } + + etb_unlock(t); + + total = etb_getdatalen(t); + if (total == t->etb_bufsz) + first = etb_readl(t, ETBR_WRITEADDR); + + etb_writel(t, first, ETBR_READADDR); + + length = min(total * 4, (int)len); + buf = vmalloc(length); + + dev_dbg(t->dev, "ETB buffer length: %d\n", total); + dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS)); + for (i = 0; i < length / 4; i++) + buf[i] = etb_readl(t, ETBR_READMEM); + + /* the only way to deassert overflow bit in ETB status is this */ + etb_writel(t, 1, ETBR_CTRL); + etb_writel(t, 0, ETBR_CTRL); + + etb_writel(t, 0, ETBR_WRITEADDR); + etb_writel(t, 0, ETBR_READADDR); + etb_writel(t, 0, ETBR_TRIGGERCOUNT); + + etb_lock(t); + + length -= copy_to_user(data, buf, length); + vfree(buf); + +out: + mutex_unlock(&t->mutex); + + return length; +} + +static int etb_release(struct inode *inode, struct file *file) +{ + /* there's nothing to do here, actually */ + return 0; +} + +static const struct file_operations etb_fops = { + .owner = THIS_MODULE, + .read = etb_read, + .open = etb_open, + .release = etb_release, +}; + +static struct miscdevice etb_miscdev = { + .name = "tracebuf", + .minor = 0, + .fops = &etb_fops, +}; + +static int __init etb_probe(struct amba_device *dev, struct amba_id *id) +{ + struct tracectx *t = &tracer; + int ret = 0; + + ret = amba_request_regions(dev, NULL); + if (ret) + goto out; + + t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); + if (!t->etb_regs) { + ret = -ENOMEM; + goto out_release; + } + + amba_set_drvdata(dev, t); + + etb_miscdev.parent = &dev->dev; + + ret = misc_register(&etb_miscdev); + if (ret) + goto out_unmap; + + t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); + if (IS_ERR(t->emu_clk)) { + dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); + return -EFAULT; + } + + clk_enable(t->emu_clk); + + etb_unlock(t); + t->etb_bufsz = etb_readl(t, ETBR_DEPTH); + dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz); + + /* make sure trace capture is disabled */ + etb_writel(t, 0, ETBR_CTRL); + etb_writel(t, 0x1000, ETBR_FORMATTERCTRL); + etb_lock(t); + + dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n"); + +out: + return ret; + +out_unmap: + amba_set_drvdata(dev, NULL); + iounmap(t->etb_regs); + +out_release: + amba_release_regions(dev); + + return ret; +} + +static int etb_remove(struct amba_device *dev) +{ + struct tracectx *t = amba_get_drvdata(dev); + + amba_set_drvdata(dev, NULL); + + iounmap(t->etb_regs); + t->etb_regs = NULL; + + clk_disable(t->emu_clk); + clk_put(t->emu_clk); + + amba_release_regions(dev); + + return 0; +} + +static struct amba_id etb_ids[] = { + { + .id = 0x0003b907, + .mask = 0x0007ffff, + }, + { 0, 0 }, +}; + +static struct amba_driver etb_driver = { + .drv = { + .name = "etb", + .owner = THIS_MODULE, + }, + .probe = etb_probe, + .remove = etb_remove, + .id_table = etb_ids, +}; + +/* use a sysfs file "trace_running" to start/stop tracing */ +static ssize_t trace_running_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%x\n", trace_isrunning(&tracer)); +} + +static ssize_t trace_running_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned int value; + int ret; + + if (sscanf(buf, "%u", &value) != 1) + return -EINVAL; + + mutex_lock(&tracer.mutex); + ret = value ? trace_start(&tracer) : trace_stop(&tracer); + mutex_unlock(&tracer.mutex); + + return ret ? : n; +} + +static struct kobj_attribute trace_running_attr = + __ATTR(trace_running, 0644, trace_running_show, trace_running_store); + +static ssize_t trace_info_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st; + int datalen; + + etb_unlock(&tracer); + datalen = etb_getdatalen(&tracer); + etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); + etb_ra = etb_readl(&tracer, ETBR_READADDR); + etb_st = etb_readl(&tracer, ETBR_STATUS); + etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); + etb_lock(&tracer); + + etm_unlock(&tracer); + etm_ctrl = etm_readl(&tracer, ETMR_CTRL); + etm_st = etm_readl(&tracer, ETMR_STATUS); + etm_lock(&tracer); + + return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" + "ETBR_WRITEADDR:\t%08x\n" + "ETBR_READADDR:\t%08x\n" + "ETBR_STATUS:\t%08x\n" + "ETBR_FORMATTERCTRL:\t%08x\n" + "ETMR_CTRL:\t%08x\n" + "ETMR_STATUS:\t%08x\n", + datalen, + tracer.ncmppairs, + etb_wa, + etb_ra, + etb_st, + etb_fc, + etm_ctrl, + etm_st + ); +} + +static struct kobj_attribute trace_info_attr = + __ATTR(trace_info, 0444, trace_info_show, NULL); + +static ssize_t trace_mode_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d %d\n", + !!(tracer.flags & TRACER_CYCLE_ACC), + tracer.etm_portsz); +} + +static ssize_t trace_mode_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned int cycacc, portsz; + + if (sscanf(buf, "%u %u", &cycacc, &portsz) != 2) + return -EINVAL; + + mutex_lock(&tracer.mutex); + if (cycacc) + tracer.flags |= TRACER_CYCLE_ACC; + else + tracer.flags &= ~TRACER_CYCLE_ACC; + + tracer.etm_portsz = portsz & 0x0f; + mutex_unlock(&tracer.mutex); + + return n; +} + +static struct kobj_attribute trace_mode_attr = + __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store); + +static int __init etm_probe(struct amba_device *dev, struct amba_id *id) +{ + struct tracectx *t = &tracer; + int ret = 0; + + if (t->etm_regs) { + dev_dbg(&dev->dev, "ETM already initialized\n"); + ret = -EBUSY; + goto out; + } + + ret = amba_request_regions(dev, NULL); + if (ret) + goto out; + + t->etm_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); + if (!t->etm_regs) { + ret = -ENOMEM; + goto out_release; + } + + amba_set_drvdata(dev, t); + + mutex_init(&t->mutex); + t->dev = &dev->dev; + t->flags = TRACER_CYCLE_ACC; + t->etm_portsz = 1; + + etm_unlock(t); + ret = etm_readl(t, CSCR_PRSR); + + t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf; + etm_writel(t, 0x440, ETMR_CTRL); + etm_lock(t); + + ret = sysfs_create_file(&dev->dev.kobj, + &trace_running_attr.attr); + if (ret) + goto out_unmap; + + /* failing to create any of these two is not fatal */ + ret = sysfs_create_file(&dev->dev.kobj, &trace_info_attr.attr); + if (ret) + dev_dbg(&dev->dev, "Failed to create trace_info in sysfs\n"); + + ret = sysfs_create_file(&dev->dev.kobj, &trace_mode_attr.attr); + if (ret) + dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n"); + + dev_dbg(t->dev, "ETM AMBA driver initialized.\n"); + +out: + return ret; + +out_unmap: + amba_set_drvdata(dev, NULL); + iounmap(t->etm_regs); + +out_release: + amba_release_regions(dev); + + return ret; +} + +static int etm_remove(struct amba_device *dev) +{ + struct tracectx *t = amba_get_drvdata(dev); + + amba_set_drvdata(dev, NULL); + + iounmap(t->etm_regs); + t->etm_regs = NULL; + + amba_release_regions(dev); + + sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr); + sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr); + sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr); + + return 0; +} + +static struct amba_id etm_ids[] = { + { + .id = 0x0003b921, + .mask = 0x0007ffff, + }, + { 0, 0 }, +}; + +static struct amba_driver etm_driver = { + .drv = { + .name = "etm", + .owner = THIS_MODULE, + }, + .probe = etm_probe, + .remove = etm_remove, + .id_table = etm_ids, +}; + +static int __init etm_init(void) +{ + int retval; + + retval = amba_driver_register(&etb_driver); + if (retval) { + printk(KERN_ERR "Failed to register etb\n"); + return retval; + } + + retval = amba_driver_register(&etm_driver); + if (retval) { + amba_driver_unregister(&etb_driver); + printk(KERN_ERR "Failed to probe etm\n"); + return retval; + } + + /* not being able to install this handler is not fatal */ + (void)register_sysrq_key('v', &sysrq_etm_op); + + return 0; +} + +device_initcall(etm_init); + diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S index 885a7214418..b9505aa267c 100644 --- a/arch/arm/kernel/head-common.S +++ b/arch/arm/kernel/head-common.S @@ -97,7 +97,7 @@ __error_a: bl printhex8 adr r0, str_a2 bl printascii - adr r3, 3f + adr r3, 4f ldmia r3, {r4, r5, r6} @ get machine desc list sub r4, r3, r4 @ get offset between virt&phys add r5, r5, r4 @ convert virt addresses to diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S index e5dfc2895e2..573b803dc6b 100644 --- a/arch/arm/kernel/head-nommu.S +++ b/arch/arm/kernel/head-nommu.S @@ -32,7 +32,7 @@ * numbers for r1. * */ - .section ".text.head", "ax" + __HEAD ENTRY(stext) setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode @ and irqs disabled diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index 38ccbe1d3b2..eb62bf94721 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -74,7 +74,7 @@ * crap here - that's what the boot loader (or in extreme, well justified * circumstances, zImage) is for. */ - .section ".text.head", "ax" + __HEAD ENTRY(stext) setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode @ and irqs disabled diff --git a/arch/arm/kernel/isa.c b/arch/arm/kernel/isa.c index 8ac9b842400..34648591073 100644 --- a/arch/arm/kernel/isa.c +++ b/arch/arm/kernel/isa.c @@ -22,47 +22,42 @@ static unsigned int isa_membase, isa_portbase, isa_portshift; static ctl_table ctl_isa_vars[4] = { { - .ctl_name = BUS_ISA_MEM_BASE, .procname = "membase", .data = &isa_membase, .maxlen = sizeof(isa_membase), .mode = 0444, - .proc_handler = &proc_dointvec, + .proc_handler = proc_dointvec, }, { - .ctl_name = BUS_ISA_PORT_BASE, .procname = "portbase", .data = &isa_portbase, .maxlen = sizeof(isa_portbase), .mode = 0444, - .proc_handler = &proc_dointvec, + .proc_handler = proc_dointvec, }, { - .ctl_name = BUS_ISA_PORT_SHIFT, .procname = "portshift", .data = &isa_portshift, .maxlen = sizeof(isa_portshift), .mode = 0444, - .proc_handler = &proc_dointvec, - }, {0} + .proc_handler = proc_dointvec, + }, {} }; static struct ctl_table_header *isa_sysctl_header; static ctl_table ctl_isa[2] = { { - .ctl_name = CTL_BUS_ISA, .procname = "isa", .mode = 0555, .child = ctl_isa_vars, - }, {0} + }, {} }; static ctl_table ctl_bus[2] = { { - .ctl_name = CTL_BUS, .procname = "bus", .mode = 0555, .child = ctl_isa, - }, {0} + }, {} }; void __init diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 790fbee92ec..0d96d0171c0 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -328,6 +328,15 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start, } /* + * Fill in the task's elfregs structure for a core dump. + */ +int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs) +{ + elf_core_copy_regs(elfregs, task_pt_regs(t)); + return 1; +} + +/* * fill in the fpe structure for a core dump... */ int dump_fpu (struct pt_regs *regs, struct user_fp *fp) diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 1423a341978..e7714f367eb 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -1,7 +1,7 @@ /* * linux/arch/arm/kernel/signal.c * - * Copyright (C) 1995-2002 Russell King + * Copyright (C) 1995-2009 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -29,6 +29,7 @@ */ #define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE)) #define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE)) +#define SWI_SYS_RESTART (0xef000000|__NR_restart_syscall|__NR_OABI_SYSCALL_BASE) /* * With EABI, the syscall number has to be loaded into r7. @@ -49,6 +50,18 @@ const unsigned long sigreturn_codes[7] = { }; /* + * Either we support OABI only, or we have EABI with the OABI + * compat layer enabled. In the later case we don't know if + * user space is EABI or not, and if not we must not clobber r7. + * Always using the OABI syscall solves that issue and works for + * all those cases. + */ +const unsigned long syscall_restart_code[2] = { + SWI_SYS_RESTART, /* swi __NR_restart_syscall */ + 0xe49df004, /* ldr pc, [sp], #4 */ +}; + +/* * atomically swap in the new signal mask, and wait for a signal. */ asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask) @@ -645,32 +658,16 @@ static void do_signal(struct pt_regs *regs, int syscall) regs->ARM_pc -= 4; #else u32 __user *usp; - u32 swival = __NR_restart_syscall; - regs->ARM_sp -= 12; + regs->ARM_sp -= 4; usp = (u32 __user *)regs->ARM_sp; - /* - * Either we supports OABI only, or we have - * EABI with the OABI compat layer enabled. - * In the later case we don't know if user - * space is EABI or not, and if not we must - * not clobber r7. Always using the OABI - * syscall solves that issue and works for - * all those cases. - */ - swival = swival - __NR_SYSCALL_BASE + __NR_OABI_SYSCALL_BASE; - - put_user(regs->ARM_pc, &usp[0]); - /* swi __NR_restart_syscall */ - put_user(0xef000000 | swival, &usp[1]); - /* ldr pc, [sp], #12 */ - put_user(0xe49df00c, &usp[2]); - - flush_icache_range((unsigned long)usp, - (unsigned long)(usp + 3)); - - regs->ARM_pc = regs->ARM_sp + 4; + if (put_user(regs->ARM_pc, usp) == 0) { + regs->ARM_pc = KERN_RESTART_CODE; + } else { + regs->ARM_sp += 4; + force_sigsegv(0, current); + } #endif } } diff --git a/arch/arm/kernel/signal.h b/arch/arm/kernel/signal.h index 27beece1550..6fcfe8398aa 100644 --- a/arch/arm/kernel/signal.h +++ b/arch/arm/kernel/signal.h @@ -1,12 +1,14 @@ /* * linux/arch/arm/kernel/signal.h * - * Copyright (C) 2005 Russell King. + * Copyright (C) 2005-2009 Russell King. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define KERN_SIGRETURN_CODE (CONFIG_VECTORS_BASE + 0x00000500) +#define KERN_RESTART_CODE (KERN_SIGRETURN_CODE + sizeof(sigreturn_codes)) extern const unsigned long sigreturn_codes[7]; +extern const unsigned long syscall_restart_code[2]; diff --git a/arch/arm/kernel/smp_scu.c b/arch/arm/kernel/smp_scu.c index d3831f616ee..9ab4149bd98 100644 --- a/arch/arm/kernel/smp_scu.c +++ b/arch/arm/kernel/smp_scu.c @@ -37,6 +37,10 @@ void __init scu_enable(void __iomem *scu_base) u32 scu_ctrl; scu_ctrl = __raw_readl(scu_base + SCU_CTRL); + /* already enabled? */ + if (scu_ctrl & 1) + return; + scu_ctrl |= 1; __raw_writel(scu_ctrl, scu_base + SCU_CTRL); diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 467b69ed102..3f361a783f4 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -1,7 +1,7 @@ /* * linux/arch/arm/kernel/traps.c * - * Copyright (C) 1995-2002 Russell King + * Copyright (C) 1995-2009 Russell King * Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds * * This program is free software; you can redistribute it and/or modify @@ -45,21 +45,21 @@ static int __init user_debug_setup(char *str) __setup("user_debug=", user_debug_setup); #endif -static void dump_mem(const char *str, unsigned long bottom, unsigned long top); +static void dump_mem(const char *, const char *, unsigned long, unsigned long); void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame) { #ifdef CONFIG_KALLSYMS - printk("[<%08lx>] ", where); - print_symbol("(%s) ", where); - printk("from [<%08lx>] ", from); - print_symbol("(%s)\n", from); + char sym1[KSYM_SYMBOL_LEN], sym2[KSYM_SYMBOL_LEN]; + sprint_symbol(sym1, where); + sprint_symbol(sym2, from); + printk("[<%08lx>] (%s) from [<%08lx>] (%s)\n", where, sym1, from, sym2); #else printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from); #endif if (in_exception_text(where)) - dump_mem("Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); + dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); } #ifndef CONFIG_ARM_UNWIND @@ -81,9 +81,10 @@ static int verify_stack(unsigned long sp) /* * Dump out the contents of some memory nicely... */ -static void dump_mem(const char *str, unsigned long bottom, unsigned long top) +static void dump_mem(const char *lvl, const char *str, unsigned long bottom, + unsigned long top) { - unsigned long p = bottom & ~31; + unsigned long first; mm_segment_t fs; int i; @@ -95,33 +96,37 @@ static void dump_mem(const char *str, unsigned long bottom, unsigned long top) fs = get_fs(); set_fs(KERNEL_DS); - printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top); + printk("%s%s(0x%08lx to 0x%08lx)\n", lvl, str, bottom, top); - for (p = bottom & ~31; p < top;) { - printk("%04lx: ", p & 0xffff); + for (first = bottom & ~31; first < top; first += 32) { + unsigned long p; + char str[sizeof(" 12345678") * 8 + 1]; - for (i = 0; i < 8; i++, p += 4) { - unsigned int val; + memset(str, ' ', sizeof(str)); + str[sizeof(str) - 1] = '\0'; - if (p < bottom || p >= top) - printk(" "); - else { - __get_user(val, (unsigned long *)p); - printk("%08x ", val); + for (p = first, i = 0; i < 8 && p < top; i++, p += 4) { + if (p >= bottom && p < top) { + unsigned long val; + if (__get_user(val, (unsigned long *)p) == 0) + sprintf(str + i * 9, " %08lx", val); + else + sprintf(str + i * 9, " ????????"); } } - printk ("\n"); + printk("%s%04lx:%s\n", lvl, first & 0xffff, str); } set_fs(fs); } -static void dump_instr(struct pt_regs *regs) +static void dump_instr(const char *lvl, struct pt_regs *regs) { unsigned long addr = instruction_pointer(regs); const int thumb = thumb_mode(regs); const int width = thumb ? 4 : 8; mm_segment_t fs; + char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str; int i; /* @@ -132,7 +137,6 @@ static void dump_instr(struct pt_regs *regs) fs = get_fs(); set_fs(KERNEL_DS); - printk("Code: "); for (i = -4; i < 1; i++) { unsigned int val, bad; @@ -142,13 +146,14 @@ static void dump_instr(struct pt_regs *regs) bad = __get_user(val, &((u32 *)addr)[i]); if (!bad) - printk(i == 0 ? "(%0*x) " : "%0*x ", width, val); + p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ", + width, val); else { - printk("bad PC value."); + p += sprintf(p, "bad PC value"); break; } } - printk("\n"); + printk("%sCode: %s\n", lvl, str); set_fs(fs); } @@ -224,18 +229,19 @@ static void __die(const char *str, int err, struct thread_info *thread, struct p struct task_struct *tsk = thread->task; static int die_counter; - printk("Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n", + printk(KERN_EMERG "Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n", str, err, ++die_counter); + sysfs_printk_last_file(); print_modules(); __show_regs(regs); - printk("Process %s (pid: %d, stack limit = 0x%p)\n", - tsk->comm, task_pid_nr(tsk), thread + 1); + printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n", + TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1); if (!user_mode(regs) || in_interrupt()) { - dump_mem("Stack: ", regs->ARM_sp, + dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp, THREAD_SIZE + (unsigned long)task_stack_page(tsk)); dump_backtrace(regs, tsk); - dump_instr(regs); + dump_instr(KERN_EMERG, regs); } } @@ -250,13 +256,14 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) oops_enter(); - console_verbose(); spin_lock_irq(&die_lock); + console_verbose(); bust_spinlocks(1); __die(str, err, thread, regs); bust_spinlocks(0); add_taint(TAINT_DIE); spin_unlock_irq(&die_lock); + oops_exit(); if (in_interrupt()) panic("Fatal exception in interrupt"); @@ -264,7 +271,6 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) if (panic_on_oops) panic("Fatal exception"); - oops_exit(); do_exit(SIGSEGV); } @@ -349,7 +355,7 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs) if (user_debug & UDBG_UNDEFINED) { printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n", current->comm, task_pid_nr(current), pc); - dump_instr(regs); + dump_instr(KERN_INFO, regs); } #endif @@ -400,7 +406,7 @@ static int bad_syscall(int n, struct pt_regs *regs) if (user_debug & UDBG_SYSCALL) { printk(KERN_ERR "[%d] %s: obsolete system call %08x.\n", task_pid_nr(current), current->comm, n); - dump_instr(regs); + dump_instr(KERN_ERR, regs); } #endif @@ -522,7 +528,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) * __kuser_cmpxchg code in entry-armv.S should be aware of its * existence. Don't ever use this from user code. */ - case 0xfff0: + case NR(cmpxchg): for (;;) { extern void do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs); @@ -567,7 +573,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) if not implemented, rather than raising SIGILL. This way the calling program can gracefully determine whether a feature is supported. */ - if (no <= 0x7ff) + if ((no & 0xffff) <= 0x7ff) return -ENOSYS; break; } @@ -579,7 +585,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) if (user_debug & UDBG_SYSCALL) { printk("[%d] %s: arm syscall %d\n", task_pid_nr(current), current->comm, no); - dump_instr(regs); + dump_instr("", regs); if (user_mode(regs)) { __show_regs(regs); c_backtrace(regs->ARM_fp, processor_mode(regs)); @@ -656,7 +662,7 @@ baddataabort(int code, unsigned long instr, struct pt_regs *regs) if (user_debug & UDBG_BADABORT) { printk(KERN_ERR "[%d] %s: bad data abort: code %d instr 0x%08lx\n", task_pid_nr(current), current->comm, code, instr); - dump_instr(regs); + dump_instr(KERN_ERR, regs); show_pte(current->mm, addr); } #endif @@ -745,6 +751,8 @@ void __init early_trap_init(void) */ memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes, sizeof(sigreturn_codes)); + memcpy((void *)KERN_RESTART_CODE, syscall_restart_code, + sizeof(syscall_restart_code)); flush_icache_range(vectors, vectors + PAGE_SIZE); modify_domain(DOMAIN_USER, DOMAIN_CLIENT); diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c index 39baf1128bf..786ac2b6914 100644 --- a/arch/arm/kernel/unwind.c +++ b/arch/arm/kernel/unwind.c @@ -26,6 +26,15 @@ * http://infocenter.arm.com/help/topic/com.arm.doc.subset.swdev.abi/index.html */ +#if !defined (__ARM_EABI__) +#warning Your compiler does not have EABI support. +#warning ARM unwind is known to compile only with EABI compilers. +#warning Change compiler or disable ARM_UNWIND option. +#elif (__GNUC__ == 4 && __GNUC_MINOR__ <= 2) +#warning Your compiler is too buggy; it is known to not compile ARM unwind support. +#warning Change compiler or disable ARM_UNWIND option. +#endif + #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index aecf87dfbae..71151bd87a3 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -24,13 +24,11 @@ SECTIONS #else . = PAGE_OFFSET + TEXT_OFFSET; #endif - .text.head : { - _stext = .; - _sinittext = .; - *(.text.head) - } .init : { /* Init code and data */ + _stext = .; + _sinittext = .; + HEAD_TEXT INIT_TEXT _einittext = .; __proc_info_begin = .; @@ -42,43 +40,31 @@ SECTIONS __tagtable_begin = .; *(.taglist.init) __tagtable_end = .; - . = ALIGN(16); - __setup_start = .; - *(.init.setup) - __setup_end = .; + + INIT_SETUP(16) + __early_begin = .; *(.early_param.init) __early_end = .; - __initcall_start = .; - INITCALLS - __initcall_end = .; - __con_initcall_start = .; - *(.con_initcall.init) - __con_initcall_end = .; - __security_initcall_start = .; - *(.security_initcall.init) - __security_initcall_end = .; -#ifdef CONFIG_BLK_DEV_INITRD - . = ALIGN(32); - __initramfs_start = .; - usr/built-in.o(.init.ramfs) - __initramfs_end = .; -#endif - . = ALIGN(PAGE_SIZE); - __per_cpu_load = .; - __per_cpu_start = .; - *(.data.percpu.page_aligned) - *(.data.percpu) - *(.data.percpu.shared_aligned) - __per_cpu_end = .; + + INIT_CALLS + CON_INITCALL + SECURITY_INITCALL + INIT_RAM_FS + #ifndef CONFIG_XIP_KERNEL __init_begin = _stext; INIT_DATA - . = ALIGN(PAGE_SIZE); - __init_end = .; #endif } + PERCPU(PAGE_SIZE) + +#ifndef CONFIG_XIP_KERNEL + . = ALIGN(PAGE_SIZE); + __init_end = .; +#endif + /DISCARD/ : { /* Exit code and data */ EXIT_TEXT EXIT_DATA @@ -157,7 +143,7 @@ SECTIONS * first, the init task union, aligned * to an 8192 byte boundary. */ - *(.data.init_task) + INIT_TASK_DATA(THREAD_SIZE) #ifdef CONFIG_XIP_KERNEL . = ALIGN(PAGE_SIZE); @@ -167,17 +153,8 @@ SECTIONS __init_end = .; #endif - . = ALIGN(PAGE_SIZE); - __nosave_begin = .; - *(.data.nosave) - . = ALIGN(PAGE_SIZE); - __nosave_end = .; - - /* - * then the cacheline aligned data - */ - . = ALIGN(32); - *(.data.cacheline_aligned) + NOSAVE_DATA + CACHELINE_ALIGNED_DATA(32) /* * The exception fixup table (might need resorting at runtime) @@ -256,20 +233,10 @@ SECTIONS } #endif - .bss : { - __bss_start = .; /* BSS */ - *(.bss) - *(COMMON) - __bss_stop = .; - _end = .; - } - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } + BSS_SECTION(0, 0, 0) + _end = .; + + STABS_DEBUG .comment 0 : { *(.comment) } } |