diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-03-28 17:03:14 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-03-28 17:03:14 -0400 |
commit | 0fe41b8982001cd14ee2c77cd776735a5024e98b (patch) | |
tree | 83e65d595c413d55259ea14fb97748ce5efe5707 /arch/arm/kernel | |
parent | eedf2c5296a8dfaaf9aec1a938c1d3bd73159a30 (diff) | |
parent | 9759d22c8348343b0da4e25d6150c41712686c14 (diff) |
Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
* 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm: (422 commits)
[ARM] 5435/1: fix compile warning in sanity_check_meminfo()
[ARM] 5434/1: ARM: OMAP: Fix mailbox compile for 24xx
[ARM] pxa: fix the bad assumption that PCMCIA sockets always start with 0
[ARM] pxa: fix Colibri PXA300 and PXA320 LCD backlight pins
imxfb: Fix TFT mode
i.MX21/27: remove ifdef CONFIG_FB_IMX
imxfb: add clock support
mxc: add arch_reset() function
clkdev: add possibility to get a clock based on the device name
i.MX1: remove fb support from mach-imx
[ARM] pxa: build arch/arm/plat-pxa/mfp.c only when PXA3xx or ARCH_MMP defined
Gemini: Add support for Teltonika RUT100
Gemini: gpiolib based GPIO support v2
MAINTAINERS: add myself as Gemini architecture maintainer
ARM: Add Gemini architecture v3
[ARM] OMAP: Fix compile for omap2_init_common_hw()
MAINTAINERS: Add myself as Faraday ARM core variant maintainer
ARM: Add support for FA526 v2
[ARM] acorn,ebsa110,footbridge,integrator,sa1100: Convert asm/io.h to linux/io.h
[ARM] collie: fix two minor formatting nits
...
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r-- | arch/arm/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/kernel/debug.S | 27 | ||||
-rw-r--r-- | arch/arm/kernel/dma-isa.c | 67 | ||||
-rw-r--r-- | arch/arm/kernel/dma.c | 119 | ||||
-rw-r--r-- | arch/arm/kernel/entry-armv.S | 19 | ||||
-rw-r--r-- | arch/arm/kernel/entry-common.S | 4 | ||||
-rw-r--r-- | arch/arm/kernel/module.c | 73 | ||||
-rw-r--r-- | arch/arm/kernel/process.c | 31 | ||||
-rw-r--r-- | arch/arm/kernel/ptrace.c | 58 | ||||
-rw-r--r-- | arch/arm/kernel/setup.c | 5 | ||||
-rw-r--r-- | arch/arm/kernel/smp.c | 2 | ||||
-rw-r--r-- | arch/arm/kernel/stacktrace.c | 88 | ||||
-rw-r--r-- | arch/arm/kernel/stacktrace.h | 9 | ||||
-rw-r--r-- | arch/arm/kernel/time.c | 21 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 44 | ||||
-rw-r--r-- | arch/arm/kernel/unwind.c | 434 | ||||
-rw-r--r-- | arch/arm/kernel/vmlinux.lds.S | 19 |
17 files changed, 863 insertions, 159 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 4305345987d3..11a5197a221f 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
@@ -29,12 +29,14 @@ obj-$(CONFIG_ATAGS_PROC) += atags.o | |||
29 | obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o | 29 | obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o |
30 | obj-$(CONFIG_ARM_THUMBEE) += thumbee.o | 30 | obj-$(CONFIG_ARM_THUMBEE) += thumbee.o |
31 | obj-$(CONFIG_KGDB) += kgdb.o | 31 | obj-$(CONFIG_KGDB) += kgdb.o |
32 | obj-$(CONFIG_ARM_UNWIND) += unwind.o | ||
32 | 33 | ||
33 | obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o | 34 | obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o |
34 | AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 | 35 | AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 |
35 | 36 | ||
36 | obj-$(CONFIG_CPU_XSCALE) += xscale-cp0.o | 37 | obj-$(CONFIG_CPU_XSCALE) += xscale-cp0.o |
37 | obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o | 38 | obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o |
39 | obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o | ||
38 | obj-$(CONFIG_IWMMXT) += iwmmxt.o | 40 | obj-$(CONFIG_IWMMXT) += iwmmxt.o |
39 | AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt | 41 | AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt |
40 | 42 | ||
diff --git a/arch/arm/kernel/debug.S b/arch/arm/kernel/debug.S index f53c58290543..b121b6053cce 100644 --- a/arch/arm/kernel/debug.S +++ b/arch/arm/kernel/debug.S | |||
@@ -49,6 +49,33 @@ | |||
49 | 1002: | 49 | 1002: |
50 | .endm | 50 | .endm |
51 | 51 | ||
52 | #elif defined(CONFIG_CPU_XSCALE) | ||
53 | |||
54 | .macro addruart, rx | ||
55 | .endm | ||
56 | |||
57 | .macro senduart, rd, rx | ||
58 | mcr p14, 0, \rd, c8, c0, 0 | ||
59 | .endm | ||
60 | |||
61 | .macro busyuart, rd, rx | ||
62 | 1001: | ||
63 | mrc p14, 0, \rx, c14, c0, 0 | ||
64 | tst \rx, #0x10000000 | ||
65 | beq 1001b | ||
66 | .endm | ||
67 | |||
68 | .macro waituart, rd, rx | ||
69 | mov \rd, #0x10000000 | ||
70 | 1001: | ||
71 | subs \rd, \rd, #1 | ||
72 | bmi 1002f | ||
73 | mrc p14, 0, \rx, c14, c0, 0 | ||
74 | tst \rx, #0x10000000 | ||
75 | bne 1001b | ||
76 | 1002: | ||
77 | .endm | ||
78 | |||
52 | #else | 79 | #else |
53 | 80 | ||
54 | .macro addruart, rx | 81 | .macro addruart, rx |
diff --git a/arch/arm/kernel/dma-isa.c b/arch/arm/kernel/dma-isa.c index 4a3a50495c60..0e88e46fc732 100644 --- a/arch/arm/kernel/dma-isa.c +++ b/arch/arm/kernel/dma-isa.c | |||
@@ -24,11 +24,6 @@ | |||
24 | #include <asm/dma.h> | 24 | #include <asm/dma.h> |
25 | #include <asm/mach/dma.h> | 25 | #include <asm/mach/dma.h> |
26 | 26 | ||
27 | #define ISA_DMA_MODE_READ 0x44 | ||
28 | #define ISA_DMA_MODE_WRITE 0x48 | ||
29 | #define ISA_DMA_MODE_CASCADE 0xc0 | ||
30 | #define ISA_DMA_AUTOINIT 0x10 | ||
31 | |||
32 | #define ISA_DMA_MASK 0 | 27 | #define ISA_DMA_MASK 0 |
33 | #define ISA_DMA_MODE 1 | 28 | #define ISA_DMA_MODE 1 |
34 | #define ISA_DMA_CLRFF 2 | 29 | #define ISA_DMA_CLRFF 2 |
@@ -49,38 +44,35 @@ static unsigned int isa_dma_port[8][7] = { | |||
49 | { 0xd4, 0xd6, 0xd8, 0x48a, 0x08a, 0xcc, 0xce } | 44 | { 0xd4, 0xd6, 0xd8, 0x48a, 0x08a, 0xcc, 0xce } |
50 | }; | 45 | }; |
51 | 46 | ||
52 | static int isa_get_dma_residue(dmach_t channel, dma_t *dma) | 47 | static int isa_get_dma_residue(unsigned int chan, dma_t *dma) |
53 | { | 48 | { |
54 | unsigned int io_port = isa_dma_port[channel][ISA_DMA_COUNT]; | 49 | unsigned int io_port = isa_dma_port[chan][ISA_DMA_COUNT]; |
55 | int count; | 50 | int count; |
56 | 51 | ||
57 | count = 1 + inb(io_port); | 52 | count = 1 + inb(io_port); |
58 | count |= inb(io_port) << 8; | 53 | count |= inb(io_port) << 8; |
59 | 54 | ||
60 | return channel < 4 ? count : (count << 1); | 55 | return chan < 4 ? count : (count << 1); |
61 | } | 56 | } |
62 | 57 | ||
63 | static void isa_enable_dma(dmach_t channel, dma_t *dma) | 58 | static void isa_enable_dma(unsigned int chan, dma_t *dma) |
64 | { | 59 | { |
65 | if (dma->invalid) { | 60 | if (dma->invalid) { |
66 | unsigned long address, length; | 61 | unsigned long address, length; |
67 | unsigned int mode; | 62 | unsigned int mode; |
68 | enum dma_data_direction direction; | 63 | enum dma_data_direction direction; |
69 | 64 | ||
70 | mode = channel & 3; | 65 | mode = (chan & 3) | dma->dma_mode; |
71 | switch (dma->dma_mode & DMA_MODE_MASK) { | 66 | switch (dma->dma_mode & DMA_MODE_MASK) { |
72 | case DMA_MODE_READ: | 67 | case DMA_MODE_READ: |
73 | mode |= ISA_DMA_MODE_READ; | ||
74 | direction = DMA_FROM_DEVICE; | 68 | direction = DMA_FROM_DEVICE; |
75 | break; | 69 | break; |
76 | 70 | ||
77 | case DMA_MODE_WRITE: | 71 | case DMA_MODE_WRITE: |
78 | mode |= ISA_DMA_MODE_WRITE; | ||
79 | direction = DMA_TO_DEVICE; | 72 | direction = DMA_TO_DEVICE; |
80 | break; | 73 | break; |
81 | 74 | ||
82 | case DMA_MODE_CASCADE: | 75 | case DMA_MODE_CASCADE: |
83 | mode |= ISA_DMA_MODE_CASCADE; | ||
84 | direction = DMA_BIDIRECTIONAL; | 76 | direction = DMA_BIDIRECTIONAL; |
85 | break; | 77 | break; |
86 | 78 | ||
@@ -105,34 +97,31 @@ static void isa_enable_dma(dmach_t channel, dma_t *dma) | |||
105 | address = dma->buf.dma_address; | 97 | address = dma->buf.dma_address; |
106 | length = dma->buf.length - 1; | 98 | length = dma->buf.length - 1; |
107 | 99 | ||
108 | outb(address >> 16, isa_dma_port[channel][ISA_DMA_PGLO]); | 100 | outb(address >> 16, isa_dma_port[chan][ISA_DMA_PGLO]); |
109 | outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]); | 101 | outb(address >> 24, isa_dma_port[chan][ISA_DMA_PGHI]); |
110 | 102 | ||
111 | if (channel >= 4) { | 103 | if (chan >= 4) { |
112 | address >>= 1; | 104 | address >>= 1; |
113 | length >>= 1; | 105 | length >>= 1; |
114 | } | 106 | } |
115 | 107 | ||
116 | outb(0, isa_dma_port[channel][ISA_DMA_CLRFF]); | 108 | outb(0, isa_dma_port[chan][ISA_DMA_CLRFF]); |
117 | |||
118 | outb(address, isa_dma_port[channel][ISA_DMA_ADDR]); | ||
119 | outb(address >> 8, isa_dma_port[channel][ISA_DMA_ADDR]); | ||
120 | 109 | ||
121 | outb(length, isa_dma_port[channel][ISA_DMA_COUNT]); | 110 | outb(address, isa_dma_port[chan][ISA_DMA_ADDR]); |
122 | outb(length >> 8, isa_dma_port[channel][ISA_DMA_COUNT]); | 111 | outb(address >> 8, isa_dma_port[chan][ISA_DMA_ADDR]); |
123 | 112 | ||
124 | if (dma->dma_mode & DMA_AUTOINIT) | 113 | outb(length, isa_dma_port[chan][ISA_DMA_COUNT]); |
125 | mode |= ISA_DMA_AUTOINIT; | 114 | outb(length >> 8, isa_dma_port[chan][ISA_DMA_COUNT]); |
126 | 115 | ||
127 | outb(mode, isa_dma_port[channel][ISA_DMA_MODE]); | 116 | outb(mode, isa_dma_port[chan][ISA_DMA_MODE]); |
128 | dma->invalid = 0; | 117 | dma->invalid = 0; |
129 | } | 118 | } |
130 | outb(channel & 3, isa_dma_port[channel][ISA_DMA_MASK]); | 119 | outb(chan & 3, isa_dma_port[chan][ISA_DMA_MASK]); |
131 | } | 120 | } |
132 | 121 | ||
133 | static void isa_disable_dma(dmach_t channel, dma_t *dma) | 122 | static void isa_disable_dma(unsigned int chan, dma_t *dma) |
134 | { | 123 | { |
135 | outb(channel | 4, isa_dma_port[channel][ISA_DMA_MASK]); | 124 | outb(chan | 4, isa_dma_port[chan][ISA_DMA_MASK]); |
136 | } | 125 | } |
137 | 126 | ||
138 | static struct dma_ops isa_dma_ops = { | 127 | static struct dma_ops isa_dma_ops = { |
@@ -160,7 +149,12 @@ static struct resource dma_resources[] = { { | |||
160 | .end = 0x048f | 149 | .end = 0x048f |
161 | } }; | 150 | } }; |
162 | 151 | ||
163 | void __init isa_init_dma(dma_t *dma) | 152 | static dma_t isa_dma[8]; |
153 | |||
154 | /* | ||
155 | * ISA DMA always starts at channel 0 | ||
156 | */ | ||
157 | void __init isa_init_dma(void) | ||
164 | { | 158 | { |
165 | /* | 159 | /* |
166 | * Try to autodetect presence of an ISA DMA controller. | 160 | * Try to autodetect presence of an ISA DMA controller. |
@@ -178,11 +172,11 @@ void __init isa_init_dma(dma_t *dma) | |||
178 | outb(0xaa, 0x00); | 172 | outb(0xaa, 0x00); |
179 | 173 | ||
180 | if (inb(0) == 0x55 && inb(0) == 0xaa) { | 174 | if (inb(0) == 0x55 && inb(0) == 0xaa) { |
181 | int channel, i; | 175 | unsigned int chan, i; |
182 | 176 | ||
183 | for (channel = 0; channel < 8; channel++) { | 177 | for (chan = 0; chan < 8; chan++) { |
184 | dma[channel].d_ops = &isa_dma_ops; | 178 | isa_dma[chan].d_ops = &isa_dma_ops; |
185 | isa_disable_dma(channel, NULL); | 179 | isa_disable_dma(chan, NULL); |
186 | } | 180 | } |
187 | 181 | ||
188 | outb(0x40, 0x0b); | 182 | outb(0x40, 0x0b); |
@@ -217,5 +211,12 @@ void __init isa_init_dma(dma_t *dma) | |||
217 | 211 | ||
218 | for (i = 0; i < ARRAY_SIZE(dma_resources); i++) | 212 | for (i = 0; i < ARRAY_SIZE(dma_resources); i++) |
219 | request_resource(&ioport_resource, dma_resources + i); | 213 | request_resource(&ioport_resource, dma_resources + i); |
214 | |||
215 | for (chan = 0; chan < 8; chan++) { | ||
216 | int ret = isa_dma_add(chan, &isa_dma[chan]); | ||
217 | if (ret) | ||
218 | printk(KERN_ERR "ISADMA%u: unable to register: %d\n", | ||
219 | chan, ret); | ||
220 | } | ||
220 | } | 221 | } |
221 | } | 222 | } |
diff --git a/arch/arm/kernel/dma.c b/arch/arm/kernel/dma.c index d006085ed7e7..7d5b9fb01e71 100644 --- a/arch/arm/kernel/dma.c +++ b/arch/arm/kernel/dma.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/spinlock.h> | 16 | #include <linux/spinlock.h> |
17 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
18 | #include <linux/scatterlist.h> | ||
18 | 19 | ||
19 | #include <asm/dma.h> | 20 | #include <asm/dma.h> |
20 | 21 | ||
@@ -23,19 +24,40 @@ | |||
23 | DEFINE_SPINLOCK(dma_spin_lock); | 24 | DEFINE_SPINLOCK(dma_spin_lock); |
24 | EXPORT_SYMBOL(dma_spin_lock); | 25 | EXPORT_SYMBOL(dma_spin_lock); |
25 | 26 | ||
26 | static dma_t dma_chan[MAX_DMA_CHANNELS]; | 27 | static dma_t *dma_chan[MAX_DMA_CHANNELS]; |
28 | |||
29 | static inline dma_t *dma_channel(unsigned int chan) | ||
30 | { | ||
31 | if (chan >= MAX_DMA_CHANNELS) | ||
32 | return NULL; | ||
33 | |||
34 | return dma_chan[chan]; | ||
35 | } | ||
36 | |||
37 | int __init isa_dma_add(unsigned int chan, dma_t *dma) | ||
38 | { | ||
39 | if (!dma->d_ops) | ||
40 | return -EINVAL; | ||
41 | |||
42 | sg_init_table(&dma->buf, 1); | ||
43 | |||
44 | if (dma_chan[chan]) | ||
45 | return -EBUSY; | ||
46 | dma_chan[chan] = dma; | ||
47 | return 0; | ||
48 | } | ||
27 | 49 | ||
28 | /* | 50 | /* |
29 | * Request DMA channel | 51 | * Request DMA channel |
30 | * | 52 | * |
31 | * On certain platforms, we have to allocate an interrupt as well... | 53 | * On certain platforms, we have to allocate an interrupt as well... |
32 | */ | 54 | */ |
33 | int request_dma(dmach_t channel, const char *device_id) | 55 | int request_dma(unsigned int chan, const char *device_id) |
34 | { | 56 | { |
35 | dma_t *dma = dma_chan + channel; | 57 | dma_t *dma = dma_channel(chan); |
36 | int ret; | 58 | int ret; |
37 | 59 | ||
38 | if (channel >= MAX_DMA_CHANNELS || !dma->d_ops) | 60 | if (!dma) |
39 | goto bad_dma; | 61 | goto bad_dma; |
40 | 62 | ||
41 | if (xchg(&dma->lock, 1) != 0) | 63 | if (xchg(&dma->lock, 1) != 0) |
@@ -47,7 +69,7 @@ int request_dma(dmach_t channel, const char *device_id) | |||
47 | 69 | ||
48 | ret = 0; | 70 | ret = 0; |
49 | if (dma->d_ops->request) | 71 | if (dma->d_ops->request) |
50 | ret = dma->d_ops->request(channel, dma); | 72 | ret = dma->d_ops->request(chan, dma); |
51 | 73 | ||
52 | if (ret) | 74 | if (ret) |
53 | xchg(&dma->lock, 0); | 75 | xchg(&dma->lock, 0); |
@@ -55,7 +77,7 @@ int request_dma(dmach_t channel, const char *device_id) | |||
55 | return ret; | 77 | return ret; |
56 | 78 | ||
57 | bad_dma: | 79 | bad_dma: |
58 | printk(KERN_ERR "dma: trying to allocate DMA%d\n", channel); | 80 | printk(KERN_ERR "dma: trying to allocate DMA%d\n", chan); |
59 | return -EINVAL; | 81 | return -EINVAL; |
60 | 82 | ||
61 | busy: | 83 | busy: |
@@ -68,42 +90,42 @@ EXPORT_SYMBOL(request_dma); | |||
68 | * | 90 | * |
69 | * On certain platforms, we have to free interrupt as well... | 91 | * On certain platforms, we have to free interrupt as well... |
70 | */ | 92 | */ |
71 | void free_dma(dmach_t channel) | 93 | void free_dma(unsigned int chan) |
72 | { | 94 | { |
73 | dma_t *dma = dma_chan + channel; | 95 | dma_t *dma = dma_channel(chan); |
74 | 96 | ||
75 | if (channel >= MAX_DMA_CHANNELS || !dma->d_ops) | 97 | if (!dma) |
76 | goto bad_dma; | 98 | goto bad_dma; |
77 | 99 | ||
78 | if (dma->active) { | 100 | if (dma->active) { |
79 | printk(KERN_ERR "dma%d: freeing active DMA\n", channel); | 101 | printk(KERN_ERR "dma%d: freeing active DMA\n", chan); |
80 | dma->d_ops->disable(channel, dma); | 102 | dma->d_ops->disable(chan, dma); |
81 | dma->active = 0; | 103 | dma->active = 0; |
82 | } | 104 | } |
83 | 105 | ||
84 | if (xchg(&dma->lock, 0) != 0) { | 106 | if (xchg(&dma->lock, 0) != 0) { |
85 | if (dma->d_ops->free) | 107 | if (dma->d_ops->free) |
86 | dma->d_ops->free(channel, dma); | 108 | dma->d_ops->free(chan, dma); |
87 | return; | 109 | return; |
88 | } | 110 | } |
89 | 111 | ||
90 | printk(KERN_ERR "dma%d: trying to free free DMA\n", channel); | 112 | printk(KERN_ERR "dma%d: trying to free free DMA\n", chan); |
91 | return; | 113 | return; |
92 | 114 | ||
93 | bad_dma: | 115 | bad_dma: |
94 | printk(KERN_ERR "dma: trying to free DMA%d\n", channel); | 116 | printk(KERN_ERR "dma: trying to free DMA%d\n", chan); |
95 | } | 117 | } |
96 | EXPORT_SYMBOL(free_dma); | 118 | EXPORT_SYMBOL(free_dma); |
97 | 119 | ||
98 | /* Set DMA Scatter-Gather list | 120 | /* Set DMA Scatter-Gather list |
99 | */ | 121 | */ |
100 | void set_dma_sg (dmach_t channel, struct scatterlist *sg, int nr_sg) | 122 | void set_dma_sg (unsigned int chan, struct scatterlist *sg, int nr_sg) |
101 | { | 123 | { |
102 | dma_t *dma = dma_chan + channel; | 124 | dma_t *dma = dma_channel(chan); |
103 | 125 | ||
104 | if (dma->active) | 126 | if (dma->active) |
105 | printk(KERN_ERR "dma%d: altering DMA SG while " | 127 | printk(KERN_ERR "dma%d: altering DMA SG while " |
106 | "DMA active\n", channel); | 128 | "DMA active\n", chan); |
107 | 129 | ||
108 | dma->sg = sg; | 130 | dma->sg = sg; |
109 | dma->sgcount = nr_sg; | 131 | dma->sgcount = nr_sg; |
@@ -115,13 +137,13 @@ EXPORT_SYMBOL(set_dma_sg); | |||
115 | * | 137 | * |
116 | * Copy address to the structure, and set the invalid bit | 138 | * Copy address to the structure, and set the invalid bit |
117 | */ | 139 | */ |
118 | void __set_dma_addr (dmach_t channel, void *addr) | 140 | void __set_dma_addr (unsigned int chan, void *addr) |
119 | { | 141 | { |
120 | dma_t *dma = dma_chan + channel; | 142 | dma_t *dma = dma_channel(chan); |
121 | 143 | ||
122 | if (dma->active) | 144 | if (dma->active) |
123 | printk(KERN_ERR "dma%d: altering DMA address while " | 145 | printk(KERN_ERR "dma%d: altering DMA address while " |
124 | "DMA active\n", channel); | 146 | "DMA active\n", chan); |
125 | 147 | ||
126 | dma->sg = NULL; | 148 | dma->sg = NULL; |
127 | dma->addr = addr; | 149 | dma->addr = addr; |
@@ -133,13 +155,13 @@ EXPORT_SYMBOL(__set_dma_addr); | |||
133 | * | 155 | * |
134 | * Copy address to the structure, and set the invalid bit | 156 | * Copy address to the structure, and set the invalid bit |
135 | */ | 157 | */ |
136 | void set_dma_count (dmach_t channel, unsigned long count) | 158 | void set_dma_count (unsigned int chan, unsigned long count) |
137 | { | 159 | { |
138 | dma_t *dma = dma_chan + channel; | 160 | dma_t *dma = dma_channel(chan); |
139 | 161 | ||
140 | if (dma->active) | 162 | if (dma->active) |
141 | printk(KERN_ERR "dma%d: altering DMA count while " | 163 | printk(KERN_ERR "dma%d: altering DMA count while " |
142 | "DMA active\n", channel); | 164 | "DMA active\n", chan); |
143 | 165 | ||
144 | dma->sg = NULL; | 166 | dma->sg = NULL; |
145 | dma->count = count; | 167 | dma->count = count; |
@@ -149,13 +171,13 @@ EXPORT_SYMBOL(set_dma_count); | |||
149 | 171 | ||
150 | /* Set DMA direction mode | 172 | /* Set DMA direction mode |
151 | */ | 173 | */ |
152 | void set_dma_mode (dmach_t channel, dmamode_t mode) | 174 | void set_dma_mode (unsigned int chan, unsigned int mode) |
153 | { | 175 | { |
154 | dma_t *dma = dma_chan + channel; | 176 | dma_t *dma = dma_channel(chan); |
155 | 177 | ||
156 | if (dma->active) | 178 | if (dma->active) |
157 | printk(KERN_ERR "dma%d: altering DMA mode while " | 179 | printk(KERN_ERR "dma%d: altering DMA mode while " |
158 | "DMA active\n", channel); | 180 | "DMA active\n", chan); |
159 | 181 | ||
160 | dma->dma_mode = mode; | 182 | dma->dma_mode = mode; |
161 | dma->invalid = 1; | 183 | dma->invalid = 1; |
@@ -164,42 +186,42 @@ EXPORT_SYMBOL(set_dma_mode); | |||
164 | 186 | ||
165 | /* Enable DMA channel | 187 | /* Enable DMA channel |
166 | */ | 188 | */ |
167 | void enable_dma (dmach_t channel) | 189 | void enable_dma (unsigned int chan) |
168 | { | 190 | { |
169 | dma_t *dma = dma_chan + channel; | 191 | dma_t *dma = dma_channel(chan); |
170 | 192 | ||
171 | if (!dma->lock) | 193 | if (!dma->lock) |
172 | goto free_dma; | 194 | goto free_dma; |
173 | 195 | ||
174 | if (dma->active == 0) { | 196 | if (dma->active == 0) { |
175 | dma->active = 1; | 197 | dma->active = 1; |
176 | dma->d_ops->enable(channel, dma); | 198 | dma->d_ops->enable(chan, dma); |
177 | } | 199 | } |
178 | return; | 200 | return; |
179 | 201 | ||
180 | free_dma: | 202 | free_dma: |
181 | printk(KERN_ERR "dma%d: trying to enable free DMA\n", channel); | 203 | printk(KERN_ERR "dma%d: trying to enable free DMA\n", chan); |
182 | BUG(); | 204 | BUG(); |
183 | } | 205 | } |
184 | EXPORT_SYMBOL(enable_dma); | 206 | EXPORT_SYMBOL(enable_dma); |
185 | 207 | ||
186 | /* Disable DMA channel | 208 | /* Disable DMA channel |
187 | */ | 209 | */ |
188 | void disable_dma (dmach_t channel) | 210 | void disable_dma (unsigned int chan) |
189 | { | 211 | { |
190 | dma_t *dma = dma_chan + channel; | 212 | dma_t *dma = dma_channel(chan); |
191 | 213 | ||
192 | if (!dma->lock) | 214 | if (!dma->lock) |
193 | goto free_dma; | 215 | goto free_dma; |
194 | 216 | ||
195 | if (dma->active == 1) { | 217 | if (dma->active == 1) { |
196 | dma->active = 0; | 218 | dma->active = 0; |
197 | dma->d_ops->disable(channel, dma); | 219 | dma->d_ops->disable(chan, dma); |
198 | } | 220 | } |
199 | return; | 221 | return; |
200 | 222 | ||
201 | free_dma: | 223 | free_dma: |
202 | printk(KERN_ERR "dma%d: trying to disable free DMA\n", channel); | 224 | printk(KERN_ERR "dma%d: trying to disable free DMA\n", chan); |
203 | BUG(); | 225 | BUG(); |
204 | } | 226 | } |
205 | EXPORT_SYMBOL(disable_dma); | 227 | EXPORT_SYMBOL(disable_dma); |
@@ -207,45 +229,38 @@ EXPORT_SYMBOL(disable_dma); | |||
207 | /* | 229 | /* |
208 | * Is the specified DMA channel active? | 230 | * Is the specified DMA channel active? |
209 | */ | 231 | */ |
210 | int dma_channel_active(dmach_t channel) | 232 | int dma_channel_active(unsigned int chan) |
211 | { | 233 | { |
212 | return dma_chan[channel].active; | 234 | dma_t *dma = dma_channel(chan); |
235 | return dma->active; | ||
213 | } | 236 | } |
214 | EXPORT_SYMBOL(dma_channel_active); | 237 | EXPORT_SYMBOL(dma_channel_active); |
215 | 238 | ||
216 | void set_dma_page(dmach_t channel, char pagenr) | 239 | void set_dma_page(unsigned int chan, char pagenr) |
217 | { | 240 | { |
218 | printk(KERN_ERR "dma%d: trying to set_dma_page\n", channel); | 241 | printk(KERN_ERR "dma%d: trying to set_dma_page\n", chan); |
219 | } | 242 | } |
220 | EXPORT_SYMBOL(set_dma_page); | 243 | EXPORT_SYMBOL(set_dma_page); |
221 | 244 | ||
222 | void set_dma_speed(dmach_t channel, int cycle_ns) | 245 | void set_dma_speed(unsigned int chan, int cycle_ns) |
223 | { | 246 | { |
224 | dma_t *dma = dma_chan + channel; | 247 | dma_t *dma = dma_channel(chan); |
225 | int ret = 0; | 248 | int ret = 0; |
226 | 249 | ||
227 | if (dma->d_ops->setspeed) | 250 | if (dma->d_ops->setspeed) |
228 | ret = dma->d_ops->setspeed(channel, dma, cycle_ns); | 251 | ret = dma->d_ops->setspeed(chan, dma, cycle_ns); |
229 | dma->speed = ret; | 252 | dma->speed = ret; |
230 | } | 253 | } |
231 | EXPORT_SYMBOL(set_dma_speed); | 254 | EXPORT_SYMBOL(set_dma_speed); |
232 | 255 | ||
233 | int get_dma_residue(dmach_t channel) | 256 | int get_dma_residue(unsigned int chan) |
234 | { | 257 | { |
235 | dma_t *dma = dma_chan + channel; | 258 | dma_t *dma = dma_channel(chan); |
236 | int ret = 0; | 259 | int ret = 0; |
237 | 260 | ||
238 | if (dma->d_ops->residue) | 261 | if (dma->d_ops->residue) |
239 | ret = dma->d_ops->residue(channel, dma); | 262 | ret = dma->d_ops->residue(chan, dma); |
240 | 263 | ||
241 | return ret; | 264 | return ret; |
242 | } | 265 | } |
243 | EXPORT_SYMBOL(get_dma_residue); | 266 | EXPORT_SYMBOL(get_dma_residue); |
244 | |||
245 | static int __init init_dma(void) | ||
246 | { | ||
247 | arch_dma_init(dma_chan); | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | core_initcall(init_dma); | ||
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 85040cfeb5e5..d662a2f1fd85 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <asm/vfpmacros.h> | 20 | #include <asm/vfpmacros.h> |
21 | #include <mach/entry-macro.S> | 21 | #include <mach/entry-macro.S> |
22 | #include <asm/thread_notify.h> | 22 | #include <asm/thread_notify.h> |
23 | #include <asm/unwind.h> | ||
23 | 24 | ||
24 | #include "entry-header.S" | 25 | #include "entry-header.S" |
25 | 26 | ||
@@ -123,6 +124,8 @@ ENDPROC(__und_invalid) | |||
123 | #endif | 124 | #endif |
124 | 125 | ||
125 | .macro svc_entry, stack_hole=0 | 126 | .macro svc_entry, stack_hole=0 |
127 | UNWIND(.fnstart ) | ||
128 | UNWIND(.save {r0 - pc} ) | ||
126 | sub sp, sp, #(S_FRAME_SIZE + \stack_hole) | 129 | sub sp, sp, #(S_FRAME_SIZE + \stack_hole) |
127 | SPFIX( tst sp, #4 ) | 130 | SPFIX( tst sp, #4 ) |
128 | SPFIX( bicne sp, sp, #4 ) | 131 | SPFIX( bicne sp, sp, #4 ) |
@@ -196,6 +199,7 @@ __dabt_svc: | |||
196 | ldr r0, [sp, #S_PSR] | 199 | ldr r0, [sp, #S_PSR] |
197 | msr spsr_cxsf, r0 | 200 | msr spsr_cxsf, r0 |
198 | ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr | 201 | ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr |
202 | UNWIND(.fnend ) | ||
199 | ENDPROC(__dabt_svc) | 203 | ENDPROC(__dabt_svc) |
200 | 204 | ||
201 | .align 5 | 205 | .align 5 |
@@ -228,6 +232,7 @@ __irq_svc: | |||
228 | bleq trace_hardirqs_on | 232 | bleq trace_hardirqs_on |
229 | #endif | 233 | #endif |
230 | ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr | 234 | ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr |
235 | UNWIND(.fnend ) | ||
231 | ENDPROC(__irq_svc) | 236 | ENDPROC(__irq_svc) |
232 | 237 | ||
233 | .ltorg | 238 | .ltorg |
@@ -278,6 +283,7 @@ __und_svc: | |||
278 | ldr lr, [sp, #S_PSR] @ Get SVC cpsr | 283 | ldr lr, [sp, #S_PSR] @ Get SVC cpsr |
279 | msr spsr_cxsf, lr | 284 | msr spsr_cxsf, lr |
280 | ldmia sp, {r0 - pc}^ @ Restore SVC registers | 285 | ldmia sp, {r0 - pc}^ @ Restore SVC registers |
286 | UNWIND(.fnend ) | ||
281 | ENDPROC(__und_svc) | 287 | ENDPROC(__und_svc) |
282 | 288 | ||
283 | .align 5 | 289 | .align 5 |
@@ -320,6 +326,7 @@ __pabt_svc: | |||
320 | ldr r0, [sp, #S_PSR] | 326 | ldr r0, [sp, #S_PSR] |
321 | msr spsr_cxsf, r0 | 327 | msr spsr_cxsf, r0 |
322 | ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr | 328 | ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr |
329 | UNWIND(.fnend ) | ||
323 | ENDPROC(__pabt_svc) | 330 | ENDPROC(__pabt_svc) |
324 | 331 | ||
325 | .align 5 | 332 | .align 5 |
@@ -343,6 +350,8 @@ ENDPROC(__pabt_svc) | |||
343 | #endif | 350 | #endif |
344 | 351 | ||
345 | .macro usr_entry | 352 | .macro usr_entry |
353 | UNWIND(.fnstart ) | ||
354 | UNWIND(.cantunwind ) @ don't unwind the user space | ||
346 | sub sp, sp, #S_FRAME_SIZE | 355 | sub sp, sp, #S_FRAME_SIZE |
347 | stmib sp, {r1 - r12} | 356 | stmib sp, {r1 - r12} |
348 | 357 | ||
@@ -420,6 +429,7 @@ __dabt_usr: | |||
420 | mov r2, sp | 429 | mov r2, sp |
421 | adr lr, ret_from_exception | 430 | adr lr, ret_from_exception |
422 | b do_DataAbort | 431 | b do_DataAbort |
432 | UNWIND(.fnend ) | ||
423 | ENDPROC(__dabt_usr) | 433 | ENDPROC(__dabt_usr) |
424 | 434 | ||
425 | .align 5 | 435 | .align 5 |
@@ -450,6 +460,7 @@ __irq_usr: | |||
450 | 460 | ||
451 | mov why, #0 | 461 | mov why, #0 |
452 | b ret_to_user | 462 | b ret_to_user |
463 | UNWIND(.fnend ) | ||
453 | ENDPROC(__irq_usr) | 464 | ENDPROC(__irq_usr) |
454 | 465 | ||
455 | .ltorg | 466 | .ltorg |
@@ -484,6 +495,7 @@ __und_usr: | |||
484 | #else | 495 | #else |
485 | b __und_usr_unknown | 496 | b __und_usr_unknown |
486 | #endif | 497 | #endif |
498 | UNWIND(.fnend ) | ||
487 | ENDPROC(__und_usr) | 499 | ENDPROC(__und_usr) |
488 | 500 | ||
489 | @ | 501 | @ |
@@ -671,14 +683,18 @@ __pabt_usr: | |||
671 | enable_irq @ Enable interrupts | 683 | enable_irq @ Enable interrupts |
672 | mov r1, sp @ regs | 684 | mov r1, sp @ regs |
673 | bl do_PrefetchAbort @ call abort handler | 685 | bl do_PrefetchAbort @ call abort handler |
686 | UNWIND(.fnend ) | ||
674 | /* fall through */ | 687 | /* fall through */ |
675 | /* | 688 | /* |
676 | * This is the return code to user mode for abort handlers | 689 | * This is the return code to user mode for abort handlers |
677 | */ | 690 | */ |
678 | ENTRY(ret_from_exception) | 691 | ENTRY(ret_from_exception) |
692 | UNWIND(.fnstart ) | ||
693 | UNWIND(.cantunwind ) | ||
679 | get_thread_info tsk | 694 | get_thread_info tsk |
680 | mov why, #0 | 695 | mov why, #0 |
681 | b ret_to_user | 696 | b ret_to_user |
697 | UNWIND(.fnend ) | ||
682 | ENDPROC(__pabt_usr) | 698 | ENDPROC(__pabt_usr) |
683 | ENDPROC(ret_from_exception) | 699 | ENDPROC(ret_from_exception) |
684 | 700 | ||
@@ -688,6 +704,8 @@ ENDPROC(ret_from_exception) | |||
688 | * previous and next are guaranteed not to be the same. | 704 | * previous and next are guaranteed not to be the same. |
689 | */ | 705 | */ |
690 | ENTRY(__switch_to) | 706 | ENTRY(__switch_to) |
707 | UNWIND(.fnstart ) | ||
708 | UNWIND(.cantunwind ) | ||
691 | add ip, r1, #TI_CPU_SAVE | 709 | add ip, r1, #TI_CPU_SAVE |
692 | ldr r3, [r2, #TI_TP_VALUE] | 710 | ldr r3, [r2, #TI_TP_VALUE] |
693 | stmia ip!, {r4 - sl, fp, sp, lr} @ Store most regs on stack | 711 | stmia ip!, {r4 - sl, fp, sp, lr} @ Store most regs on stack |
@@ -717,6 +735,7 @@ ENTRY(__switch_to) | |||
717 | bl atomic_notifier_call_chain | 735 | bl atomic_notifier_call_chain |
718 | mov r0, r5 | 736 | mov r0, r5 |
719 | ldmia r4, {r4 - sl, fp, sp, pc} @ Load all regs saved previously | 737 | ldmia r4, {r4 - sl, fp, sp, pc} @ Load all regs saved previously |
738 | UNWIND(.fnend ) | ||
720 | ENDPROC(__switch_to) | 739 | ENDPROC(__switch_to) |
721 | 740 | ||
722 | __INIT | 741 | __INIT |
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 159d0416f270..b55cb0331809 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <asm/unistd.h> | 11 | #include <asm/unistd.h> |
12 | #include <asm/ftrace.h> | 12 | #include <asm/ftrace.h> |
13 | #include <mach/entry-macro.S> | 13 | #include <mach/entry-macro.S> |
14 | #include <asm/unwind.h> | ||
14 | 15 | ||
15 | #include "entry-header.S" | 16 | #include "entry-header.S" |
16 | 17 | ||
@@ -22,6 +23,8 @@ | |||
22 | * stack. | 23 | * stack. |
23 | */ | 24 | */ |
24 | ret_fast_syscall: | 25 | ret_fast_syscall: |
26 | UNWIND(.fnstart ) | ||
27 | UNWIND(.cantunwind ) | ||
25 | disable_irq @ disable interrupts | 28 | disable_irq @ disable interrupts |
26 | ldr r1, [tsk, #TI_FLAGS] | 29 | ldr r1, [tsk, #TI_FLAGS] |
27 | tst r1, #_TIF_WORK_MASK | 30 | tst r1, #_TIF_WORK_MASK |
@@ -38,6 +41,7 @@ ret_fast_syscall: | |||
38 | mov r0, r0 | 41 | mov r0, r0 |
39 | add sp, sp, #S_FRAME_SIZE - S_PC | 42 | add sp, sp, #S_FRAME_SIZE - S_PC |
40 | movs pc, lr @ return & move spsr_svc into cpsr | 43 | movs pc, lr @ return & move spsr_svc into cpsr |
44 | UNWIND(.fnend ) | ||
41 | 45 | ||
42 | /* | 46 | /* |
43 | * Ok, we need to do extra processing, enter the slow path. | 47 | * Ok, we need to do extra processing, enter the slow path. |
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c index dab48f27263f..d1731e39b496 100644 --- a/arch/arm/kernel/module.c +++ b/arch/arm/kernel/module.c | |||
@@ -22,6 +22,7 @@ | |||
22 | 22 | ||
23 | #include <asm/pgtable.h> | 23 | #include <asm/pgtable.h> |
24 | #include <asm/sections.h> | 24 | #include <asm/sections.h> |
25 | #include <asm/unwind.h> | ||
25 | 26 | ||
26 | #ifdef CONFIG_XIP_KERNEL | 27 | #ifdef CONFIG_XIP_KERNEL |
27 | /* | 28 | /* |
@@ -66,6 +67,24 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, | |||
66 | char *secstrings, | 67 | char *secstrings, |
67 | struct module *mod) | 68 | struct module *mod) |
68 | { | 69 | { |
70 | #ifdef CONFIG_ARM_UNWIND | ||
71 | Elf_Shdr *s, *sechdrs_end = sechdrs + hdr->e_shnum; | ||
72 | |||
73 | for (s = sechdrs; s < sechdrs_end; s++) { | ||
74 | if (strcmp(".ARM.exidx.init.text", secstrings + s->sh_name) == 0) | ||
75 | mod->arch.unw_sec_init = s; | ||
76 | else if (strcmp(".ARM.exidx.devinit.text", secstrings + s->sh_name) == 0) | ||
77 | mod->arch.unw_sec_devinit = s; | ||
78 | else if (strcmp(".ARM.exidx", secstrings + s->sh_name) == 0) | ||
79 | mod->arch.unw_sec_core = s; | ||
80 | else if (strcmp(".init.text", secstrings + s->sh_name) == 0) | ||
81 | mod->arch.sec_init_text = s; | ||
82 | else if (strcmp(".devinit.text", secstrings + s->sh_name) == 0) | ||
83 | mod->arch.sec_devinit_text = s; | ||
84 | else if (strcmp(".text", secstrings + s->sh_name) == 0) | ||
85 | mod->arch.sec_core_text = s; | ||
86 | } | ||
87 | #endif | ||
69 | return 0; | 88 | return 0; |
70 | } | 89 | } |
71 | 90 | ||
@@ -104,6 +123,10 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, | |||
104 | loc = dstsec->sh_addr + rel->r_offset; | 123 | loc = dstsec->sh_addr + rel->r_offset; |
105 | 124 | ||
106 | switch (ELF32_R_TYPE(rel->r_info)) { | 125 | switch (ELF32_R_TYPE(rel->r_info)) { |
126 | case R_ARM_NONE: | ||
127 | /* ignore */ | ||
128 | break; | ||
129 | |||
107 | case R_ARM_ABS32: | 130 | case R_ARM_ABS32: |
108 | *(u32 *)loc += sym->st_value; | 131 | *(u32 *)loc += sym->st_value; |
109 | break; | 132 | break; |
@@ -132,6 +155,20 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, | |||
132 | *(u32 *)loc |= offset & 0x00ffffff; | 155 | *(u32 *)loc |= offset & 0x00ffffff; |
133 | break; | 156 | break; |
134 | 157 | ||
158 | case R_ARM_V4BX: | ||
159 | /* Preserve Rm and the condition code. Alter | ||
160 | * other bits to re-code instruction as | ||
161 | * MOV PC,Rm. | ||
162 | */ | ||
163 | *(u32 *)loc &= 0xf000000f; | ||
164 | *(u32 *)loc |= 0x01a0f000; | ||
165 | break; | ||
166 | |||
167 | case R_ARM_PREL31: | ||
168 | offset = *(u32 *)loc + sym->st_value - loc; | ||
169 | *(u32 *)loc = offset & 0x7fffffff; | ||
170 | break; | ||
171 | |||
135 | default: | 172 | default: |
136 | printk(KERN_ERR "%s: unknown relocation: %u\n", | 173 | printk(KERN_ERR "%s: unknown relocation: %u\n", |
137 | module->name, ELF32_R_TYPE(rel->r_info)); | 174 | module->name, ELF32_R_TYPE(rel->r_info)); |
@@ -150,14 +187,50 @@ apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, | |||
150 | return -ENOEXEC; | 187 | return -ENOEXEC; |
151 | } | 188 | } |
152 | 189 | ||
190 | #ifdef CONFIG_ARM_UNWIND | ||
191 | static void register_unwind_tables(struct module *mod) | ||
192 | { | ||
193 | if (mod->arch.unw_sec_init && mod->arch.sec_init_text) | ||
194 | mod->arch.unwind_init = | ||
195 | unwind_table_add(mod->arch.unw_sec_init->sh_addr, | ||
196 | mod->arch.unw_sec_init->sh_size, | ||
197 | mod->arch.sec_init_text->sh_addr, | ||
198 | mod->arch.sec_init_text->sh_size); | ||
199 | if (mod->arch.unw_sec_devinit && mod->arch.sec_devinit_text) | ||
200 | mod->arch.unwind_devinit = | ||
201 | unwind_table_add(mod->arch.unw_sec_devinit->sh_addr, | ||
202 | mod->arch.unw_sec_devinit->sh_size, | ||
203 | mod->arch.sec_devinit_text->sh_addr, | ||
204 | mod->arch.sec_devinit_text->sh_size); | ||
205 | if (mod->arch.unw_sec_core && mod->arch.sec_core_text) | ||
206 | mod->arch.unwind_core = | ||
207 | unwind_table_add(mod->arch.unw_sec_core->sh_addr, | ||
208 | mod->arch.unw_sec_core->sh_size, | ||
209 | mod->arch.sec_core_text->sh_addr, | ||
210 | mod->arch.sec_core_text->sh_size); | ||
211 | } | ||
212 | |||
213 | static void unregister_unwind_tables(struct module *mod) | ||
214 | { | ||
215 | unwind_table_del(mod->arch.unwind_init); | ||
216 | unwind_table_del(mod->arch.unwind_devinit); | ||
217 | unwind_table_del(mod->arch.unwind_core); | ||
218 | } | ||
219 | #else | ||
220 | static inline void register_unwind_tables(struct module *mod) { } | ||
221 | static inline void unregister_unwind_tables(struct module *mod) { } | ||
222 | #endif | ||
223 | |||
153 | int | 224 | int |
154 | module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, | 225 | module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, |
155 | struct module *module) | 226 | struct module *module) |
156 | { | 227 | { |
228 | register_unwind_tables(module); | ||
157 | return 0; | 229 | return 0; |
158 | } | 230 | } |
159 | 231 | ||
160 | void | 232 | void |
161 | module_arch_cleanup(struct module *mod) | 233 | module_arch_cleanup(struct module *mod) |
162 | { | 234 | { |
235 | unregister_unwind_tables(mod); | ||
163 | } | 236 | } |
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index d3ea6fa89521..2de14e2afdc5 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <asm/processor.h> | 34 | #include <asm/processor.h> |
35 | #include <asm/system.h> | 35 | #include <asm/system.h> |
36 | #include <asm/thread_notify.h> | 36 | #include <asm/thread_notify.h> |
37 | #include <asm/stacktrace.h> | ||
37 | #include <asm/mach/time.h> | 38 | #include <asm/mach/time.h> |
38 | 39 | ||
39 | static const char *processor_modes[] = { | 40 | static const char *processor_modes[] = { |
@@ -82,7 +83,7 @@ static int __init hlt_setup(char *__unused) | |||
82 | __setup("nohlt", nohlt_setup); | 83 | __setup("nohlt", nohlt_setup); |
83 | __setup("hlt", hlt_setup); | 84 | __setup("hlt", hlt_setup); |
84 | 85 | ||
85 | void arm_machine_restart(char mode) | 86 | void arm_machine_restart(char mode, const char *cmd) |
86 | { | 87 | { |
87 | /* | 88 | /* |
88 | * Clean and disable cache, and turn off interrupts | 89 | * Clean and disable cache, and turn off interrupts |
@@ -99,7 +100,7 @@ void arm_machine_restart(char mode) | |||
99 | /* | 100 | /* |
100 | * Now call the architecture specific reboot code. | 101 | * Now call the architecture specific reboot code. |
101 | */ | 102 | */ |
102 | arch_reset(mode); | 103 | arch_reset(mode, cmd); |
103 | 104 | ||
104 | /* | 105 | /* |
105 | * Whoops - the architecture was unable to reboot. | 106 | * Whoops - the architecture was unable to reboot. |
@@ -119,7 +120,7 @@ EXPORT_SYMBOL(pm_idle); | |||
119 | void (*pm_power_off)(void); | 120 | void (*pm_power_off)(void); |
120 | EXPORT_SYMBOL(pm_power_off); | 121 | EXPORT_SYMBOL(pm_power_off); |
121 | 122 | ||
122 | void (*arm_pm_restart)(char str) = arm_machine_restart; | 123 | void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart; |
123 | EXPORT_SYMBOL_GPL(arm_pm_restart); | 124 | EXPORT_SYMBOL_GPL(arm_pm_restart); |
124 | 125 | ||
125 | 126 | ||
@@ -194,9 +195,9 @@ void machine_power_off(void) | |||
194 | pm_power_off(); | 195 | pm_power_off(); |
195 | } | 196 | } |
196 | 197 | ||
197 | void machine_restart(char * __unused) | 198 | void machine_restart(char *cmd) |
198 | { | 199 | { |
199 | arm_pm_restart(reboot_mode); | 200 | arm_pm_restart(reboot_mode, cmd); |
200 | } | 201 | } |
201 | 202 | ||
202 | void __show_regs(struct pt_regs *regs) | 203 | void __show_regs(struct pt_regs *regs) |
@@ -372,23 +373,21 @@ EXPORT_SYMBOL(kernel_thread); | |||
372 | 373 | ||
373 | unsigned long get_wchan(struct task_struct *p) | 374 | unsigned long get_wchan(struct task_struct *p) |
374 | { | 375 | { |
375 | unsigned long fp, lr; | 376 | struct stackframe frame; |
376 | unsigned long stack_start, stack_end; | ||
377 | int count = 0; | 377 | int count = 0; |
378 | if (!p || p == current || p->state == TASK_RUNNING) | 378 | if (!p || p == current || p->state == TASK_RUNNING) |
379 | return 0; | 379 | return 0; |
380 | 380 | ||
381 | stack_start = (unsigned long)end_of_stack(p); | 381 | frame.fp = thread_saved_fp(p); |
382 | stack_end = (unsigned long)task_stack_page(p) + THREAD_SIZE; | 382 | frame.sp = thread_saved_sp(p); |
383 | 383 | frame.lr = 0; /* recovered from the stack */ | |
384 | fp = thread_saved_fp(p); | 384 | frame.pc = thread_saved_pc(p); |
385 | do { | 385 | do { |
386 | if (fp < stack_start || fp > stack_end) | 386 | int ret = unwind_frame(&frame); |
387 | if (ret < 0) | ||
387 | return 0; | 388 | return 0; |
388 | lr = ((unsigned long *)fp)[-1]; | 389 | if (!in_sched_functions(frame.pc)) |
389 | if (!in_sched_functions(lr)) | 390 | return frame.pc; |
390 | return lr; | ||
391 | fp = *(unsigned long *) (fp - 12); | ||
392 | } while (count ++ < 16); | 391 | } while (count ++ < 16); |
393 | return 0; | 392 | return 0; |
394 | } | 393 | } |
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index df653ea59250..89882a1d0187 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c | |||
@@ -653,6 +653,54 @@ static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp) | |||
653 | } | 653 | } |
654 | #endif | 654 | #endif |
655 | 655 | ||
656 | #ifdef CONFIG_VFP | ||
657 | /* | ||
658 | * Get the child VFP state. | ||
659 | */ | ||
660 | static int ptrace_getvfpregs(struct task_struct *tsk, void __user *data) | ||
661 | { | ||
662 | struct thread_info *thread = task_thread_info(tsk); | ||
663 | union vfp_state *vfp = &thread->vfpstate; | ||
664 | struct user_vfp __user *ufp = data; | ||
665 | |||
666 | vfp_sync_state(thread); | ||
667 | |||
668 | /* copy the floating point registers */ | ||
669 | if (copy_to_user(&ufp->fpregs, &vfp->hard.fpregs, | ||
670 | sizeof(vfp->hard.fpregs))) | ||
671 | return -EFAULT; | ||
672 | |||
673 | /* copy the status and control register */ | ||
674 | if (put_user(vfp->hard.fpscr, &ufp->fpscr)) | ||
675 | return -EFAULT; | ||
676 | |||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | /* | ||
681 | * Set the child VFP state. | ||
682 | */ | ||
683 | static int ptrace_setvfpregs(struct task_struct *tsk, void __user *data) | ||
684 | { | ||
685 | struct thread_info *thread = task_thread_info(tsk); | ||
686 | union vfp_state *vfp = &thread->vfpstate; | ||
687 | struct user_vfp __user *ufp = data; | ||
688 | |||
689 | vfp_sync_state(thread); | ||
690 | |||
691 | /* copy the floating point registers */ | ||
692 | if (copy_from_user(&vfp->hard.fpregs, &ufp->fpregs, | ||
693 | sizeof(vfp->hard.fpregs))) | ||
694 | return -EFAULT; | ||
695 | |||
696 | /* copy the status and control register */ | ||
697 | if (get_user(vfp->hard.fpscr, &ufp->fpscr)) | ||
698 | return -EFAULT; | ||
699 | |||
700 | return 0; | ||
701 | } | ||
702 | #endif | ||
703 | |||
656 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 704 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
657 | { | 705 | { |
658 | int ret; | 706 | int ret; |
@@ -775,6 +823,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
775 | break; | 823 | break; |
776 | #endif | 824 | #endif |
777 | 825 | ||
826 | #ifdef CONFIG_VFP | ||
827 | case PTRACE_GETVFPREGS: | ||
828 | ret = ptrace_getvfpregs(child, (void __user *)data); | ||
829 | break; | ||
830 | |||
831 | case PTRACE_SETVFPREGS: | ||
832 | ret = ptrace_setvfpregs(child, (void __user *)data); | ||
833 | break; | ||
834 | #endif | ||
835 | |||
778 | default: | 836 | default: |
779 | ret = ptrace_request(child, request, addr, data); | 837 | ret = ptrace_request(child, request, addr, data); |
780 | break; | 838 | break; |
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 68d6494c0389..bc5e4128f9f3 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <asm/mach/irq.h> | 40 | #include <asm/mach/irq.h> |
41 | #include <asm/mach/time.h> | 41 | #include <asm/mach/time.h> |
42 | #include <asm/traps.h> | 42 | #include <asm/traps.h> |
43 | #include <asm/unwind.h> | ||
43 | 44 | ||
44 | #include "compat.h" | 45 | #include "compat.h" |
45 | #include "atags.h" | 46 | #include "atags.h" |
@@ -685,6 +686,8 @@ void __init setup_arch(char **cmdline_p) | |||
685 | struct machine_desc *mdesc; | 686 | struct machine_desc *mdesc; |
686 | char *from = default_command_line; | 687 | char *from = default_command_line; |
687 | 688 | ||
689 | unwind_init(); | ||
690 | |||
688 | setup_processor(); | 691 | setup_processor(); |
689 | mdesc = setup_machine(machine_arch_type); | 692 | mdesc = setup_machine(machine_arch_type); |
690 | machine_name = mdesc->name; | 693 | machine_name = mdesc->name; |
@@ -780,6 +783,8 @@ static const char *hwcap_str[] = { | |||
780 | "crunch", | 783 | "crunch", |
781 | "thumbee", | 784 | "thumbee", |
782 | "neon", | 785 | "neon", |
786 | "vfpv3", | ||
787 | "vfpv3d16", | ||
783 | NULL | 788 | NULL |
784 | }; | 789 | }; |
785 | 790 | ||
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 55fa7ff96a3e..7801aac3c043 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c | |||
@@ -93,6 +93,7 @@ int __cpuinit __cpu_up(unsigned int cpu) | |||
93 | pmd = pmd_offset(pgd + pgd_index(PHYS_OFFSET), PHYS_OFFSET); | 93 | pmd = pmd_offset(pgd + pgd_index(PHYS_OFFSET), PHYS_OFFSET); |
94 | *pmd = __pmd((PHYS_OFFSET & PGDIR_MASK) | | 94 | *pmd = __pmd((PHYS_OFFSET & PGDIR_MASK) | |
95 | PMD_TYPE_SECT | PMD_SECT_AP_WRITE); | 95 | PMD_TYPE_SECT | PMD_SECT_AP_WRITE); |
96 | flush_pmd_entry(pmd); | ||
96 | 97 | ||
97 | /* | 98 | /* |
98 | * We need to tell the secondary core where to find | 99 | * We need to tell the secondary core where to find |
@@ -130,6 +131,7 @@ int __cpuinit __cpu_up(unsigned int cpu) | |||
130 | secondary_data.pgdir = 0; | 131 | secondary_data.pgdir = 0; |
131 | 132 | ||
132 | *pmd = __pmd(0); | 133 | *pmd = __pmd(0); |
134 | clean_pmd_entry(pmd); | ||
133 | pgd_free(&init_mm, pgd); | 135 | pgd_free(&init_mm, pgd); |
134 | 136 | ||
135 | if (ret) { | 137 | if (ret) { |
diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c index fc650f64df43..9f444e5cc165 100644 --- a/arch/arm/kernel/stacktrace.c +++ b/arch/arm/kernel/stacktrace.c | |||
@@ -2,35 +2,60 @@ | |||
2 | #include <linux/sched.h> | 2 | #include <linux/sched.h> |
3 | #include <linux/stacktrace.h> | 3 | #include <linux/stacktrace.h> |
4 | 4 | ||
5 | #include "stacktrace.h" | 5 | #include <asm/stacktrace.h> |
6 | 6 | ||
7 | int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high, | 7 | #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) |
8 | int (*fn)(struct stackframe *, void *), void *data) | 8 | /* |
9 | * Unwind the current stack frame and store the new register values in the | ||
10 | * structure passed as argument. Unwinding is equivalent to a function return, | ||
11 | * hence the new PC value rather than LR should be used for backtrace. | ||
12 | * | ||
13 | * With framepointer enabled, a simple function prologue looks like this: | ||
14 | * mov ip, sp | ||
15 | * stmdb sp!, {fp, ip, lr, pc} | ||
16 | * sub fp, ip, #4 | ||
17 | * | ||
18 | * A simple function epilogue looks like this: | ||
19 | * ldm sp, {fp, sp, pc} | ||
20 | * | ||
21 | * Note that with framepointer enabled, even the leaf functions have the same | ||
22 | * prologue and epilogue, therefore we can ignore the LR value in this case. | ||
23 | */ | ||
24 | int unwind_frame(struct stackframe *frame) | ||
9 | { | 25 | { |
10 | struct stackframe *frame; | 26 | unsigned long high, low; |
11 | 27 | unsigned long fp = frame->fp; | |
12 | do { | ||
13 | /* | ||
14 | * Check current frame pointer is within bounds | ||
15 | */ | ||
16 | if (fp < (low + 12) || fp + 4 >= high) | ||
17 | break; | ||
18 | 28 | ||
19 | frame = (struct stackframe *)(fp - 12); | 29 | /* only go to a higher address on the stack */ |
30 | low = frame->sp; | ||
31 | high = ALIGN(low, THREAD_SIZE) + THREAD_SIZE; | ||
20 | 32 | ||
21 | if (fn(frame, data)) | 33 | /* check current frame pointer is within bounds */ |
22 | break; | 34 | if (fp < (low + 12) || fp + 4 >= high) |
35 | return -EINVAL; | ||
23 | 36 | ||
24 | /* | 37 | /* restore the registers from the stack frame */ |
25 | * Update the low bound - the next frame must always | 38 | frame->fp = *(unsigned long *)(fp - 12); |
26 | * be at a higher address than the current frame. | 39 | frame->sp = *(unsigned long *)(fp - 8); |
27 | */ | 40 | frame->pc = *(unsigned long *)(fp - 4); |
28 | low = fp + 4; | ||
29 | fp = frame->fp; | ||
30 | } while (fp); | ||
31 | 41 | ||
32 | return 0; | 42 | return 0; |
33 | } | 43 | } |
44 | #endif | ||
45 | |||
46 | void walk_stackframe(struct stackframe *frame, | ||
47 | int (*fn)(struct stackframe *, void *), void *data) | ||
48 | { | ||
49 | while (1) { | ||
50 | int ret; | ||
51 | |||
52 | if (fn(frame, data)) | ||
53 | break; | ||
54 | ret = unwind_frame(frame); | ||
55 | if (ret < 0) | ||
56 | break; | ||
57 | } | ||
58 | } | ||
34 | EXPORT_SYMBOL(walk_stackframe); | 59 | EXPORT_SYMBOL(walk_stackframe); |
35 | 60 | ||
36 | #ifdef CONFIG_STACKTRACE | 61 | #ifdef CONFIG_STACKTRACE |
@@ -44,7 +69,7 @@ static int save_trace(struct stackframe *frame, void *d) | |||
44 | { | 69 | { |
45 | struct stack_trace_data *data = d; | 70 | struct stack_trace_data *data = d; |
46 | struct stack_trace *trace = data->trace; | 71 | struct stack_trace *trace = data->trace; |
47 | unsigned long addr = frame->lr; | 72 | unsigned long addr = frame->pc; |
48 | 73 | ||
49 | if (data->no_sched_functions && in_sched_functions(addr)) | 74 | if (data->no_sched_functions && in_sched_functions(addr)) |
50 | return 0; | 75 | return 0; |
@@ -61,11 +86,10 @@ static int save_trace(struct stackframe *frame, void *d) | |||
61 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | 86 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) |
62 | { | 87 | { |
63 | struct stack_trace_data data; | 88 | struct stack_trace_data data; |
64 | unsigned long fp, base; | 89 | struct stackframe frame; |
65 | 90 | ||
66 | data.trace = trace; | 91 | data.trace = trace; |
67 | data.skip = trace->skip; | 92 | data.skip = trace->skip; |
68 | base = (unsigned long)task_stack_page(tsk); | ||
69 | 93 | ||
70 | if (tsk != current) { | 94 | if (tsk != current) { |
71 | #ifdef CONFIG_SMP | 95 | #ifdef CONFIG_SMP |
@@ -76,14 +100,22 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |||
76 | BUG(); | 100 | BUG(); |
77 | #else | 101 | #else |
78 | data.no_sched_functions = 1; | 102 | data.no_sched_functions = 1; |
79 | fp = thread_saved_fp(tsk); | 103 | frame.fp = thread_saved_fp(tsk); |
104 | frame.sp = thread_saved_sp(tsk); | ||
105 | frame.lr = 0; /* recovered from the stack */ | ||
106 | frame.pc = thread_saved_pc(tsk); | ||
80 | #endif | 107 | #endif |
81 | } else { | 108 | } else { |
109 | register unsigned long current_sp asm ("sp"); | ||
110 | |||
82 | data.no_sched_functions = 0; | 111 | data.no_sched_functions = 0; |
83 | asm("mov %0, fp" : "=r" (fp)); | 112 | frame.fp = (unsigned long)__builtin_frame_address(0); |
113 | frame.sp = current_sp; | ||
114 | frame.lr = (unsigned long)__builtin_return_address(0); | ||
115 | frame.pc = (unsigned long)save_stack_trace_tsk; | ||
84 | } | 116 | } |
85 | 117 | ||
86 | walk_stackframe(fp, base, base + THREAD_SIZE, save_trace, &data); | 118 | walk_stackframe(&frame, save_trace, &data); |
87 | if (trace->nr_entries < trace->max_entries) | 119 | if (trace->nr_entries < trace->max_entries) |
88 | trace->entries[trace->nr_entries++] = ULONG_MAX; | 120 | trace->entries[trace->nr_entries++] = ULONG_MAX; |
89 | } | 121 | } |
diff --git a/arch/arm/kernel/stacktrace.h b/arch/arm/kernel/stacktrace.h deleted file mode 100644 index e9fd20cb5662..000000000000 --- a/arch/arm/kernel/stacktrace.h +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | struct stackframe { | ||
2 | unsigned long fp; | ||
3 | unsigned long sp; | ||
4 | unsigned long lr; | ||
5 | unsigned long pc; | ||
6 | }; | ||
7 | |||
8 | int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high, | ||
9 | int (*fn)(struct stackframe *, void *), void *data); | ||
diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index c68b44aa88d2..4cdc4a0bd02d 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c | |||
@@ -33,6 +33,7 @@ | |||
33 | 33 | ||
34 | #include <asm/leds.h> | 34 | #include <asm/leds.h> |
35 | #include <asm/thread_info.h> | 35 | #include <asm/thread_info.h> |
36 | #include <asm/stacktrace.h> | ||
36 | #include <asm/mach/time.h> | 37 | #include <asm/mach/time.h> |
37 | 38 | ||
38 | /* | 39 | /* |
@@ -55,14 +56,22 @@ EXPORT_SYMBOL(rtc_lock); | |||
55 | #ifdef CONFIG_SMP | 56 | #ifdef CONFIG_SMP |
56 | unsigned long profile_pc(struct pt_regs *regs) | 57 | unsigned long profile_pc(struct pt_regs *regs) |
57 | { | 58 | { |
58 | unsigned long fp, pc = instruction_pointer(regs); | 59 | struct stackframe frame; |
59 | 60 | ||
60 | if (in_lock_functions(pc)) { | 61 | if (!in_lock_functions(regs->ARM_pc)) |
61 | fp = regs->ARM_fp; | 62 | return regs->ARM_pc; |
62 | pc = ((unsigned long *)fp)[-1]; | 63 | |
63 | } | 64 | frame.fp = regs->ARM_fp; |
65 | frame.sp = regs->ARM_sp; | ||
66 | frame.lr = regs->ARM_lr; | ||
67 | frame.pc = regs->ARM_pc; | ||
68 | do { | ||
69 | int ret = unwind_frame(&frame); | ||
70 | if (ret < 0) | ||
71 | return 0; | ||
72 | } while (in_lock_functions(frame.pc)); | ||
64 | 73 | ||
65 | return pc; | 74 | return frame.pc; |
66 | } | 75 | } |
67 | EXPORT_SYMBOL(profile_pc); | 76 | EXPORT_SYMBOL(profile_pc); |
68 | #endif | 77 | #endif |
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 79abc4ddc0cf..57eb0f6f6005 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <asm/system.h> | 27 | #include <asm/system.h> |
28 | #include <asm/unistd.h> | 28 | #include <asm/unistd.h> |
29 | #include <asm/traps.h> | 29 | #include <asm/traps.h> |
30 | #include <asm/unwind.h> | ||
30 | 31 | ||
31 | #include "ptrace.h" | 32 | #include "ptrace.h" |
32 | #include "signal.h" | 33 | #include "signal.h" |
@@ -61,6 +62,7 @@ void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long | |||
61 | dump_mem("Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); | 62 | dump_mem("Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); |
62 | } | 63 | } |
63 | 64 | ||
65 | #ifndef CONFIG_ARM_UNWIND | ||
64 | /* | 66 | /* |
65 | * Stack pointers should always be within the kernels view of | 67 | * Stack pointers should always be within the kernels view of |
66 | * physical memory. If it is not there, then we can't dump | 68 | * physical memory. If it is not there, then we can't dump |
@@ -74,6 +76,7 @@ static int verify_stack(unsigned long sp) | |||
74 | 76 | ||
75 | return 0; | 77 | return 0; |
76 | } | 78 | } |
79 | #endif | ||
77 | 80 | ||
78 | /* | 81 | /* |
79 | * Dump out the contents of some memory nicely... | 82 | * Dump out the contents of some memory nicely... |
@@ -150,13 +153,33 @@ static void dump_instr(struct pt_regs *regs) | |||
150 | set_fs(fs); | 153 | set_fs(fs); |
151 | } | 154 | } |
152 | 155 | ||
156 | #ifdef CONFIG_ARM_UNWIND | ||
157 | static inline void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) | ||
158 | { | ||
159 | unwind_backtrace(regs, tsk); | ||
160 | } | ||
161 | #else | ||
153 | static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) | 162 | static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) |
154 | { | 163 | { |
155 | unsigned int fp; | 164 | unsigned int fp, mode; |
156 | int ok = 1; | 165 | int ok = 1; |
157 | 166 | ||
158 | printk("Backtrace: "); | 167 | printk("Backtrace: "); |
159 | fp = regs->ARM_fp; | 168 | |
169 | if (!tsk) | ||
170 | tsk = current; | ||
171 | |||
172 | if (regs) { | ||
173 | fp = regs->ARM_fp; | ||
174 | mode = processor_mode(regs); | ||
175 | } else if (tsk != current) { | ||
176 | fp = thread_saved_fp(tsk); | ||
177 | mode = 0x10; | ||
178 | } else { | ||
179 | asm("mov %0, fp" : "=r" (fp) : : "cc"); | ||
180 | mode = 0x10; | ||
181 | } | ||
182 | |||
160 | if (!fp) { | 183 | if (!fp) { |
161 | printk("no frame pointer"); | 184 | printk("no frame pointer"); |
162 | ok = 0; | 185 | ok = 0; |
@@ -168,29 +191,20 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) | |||
168 | printk("\n"); | 191 | printk("\n"); |
169 | 192 | ||
170 | if (ok) | 193 | if (ok) |
171 | c_backtrace(fp, processor_mode(regs)); | 194 | c_backtrace(fp, mode); |
172 | } | 195 | } |
196 | #endif | ||
173 | 197 | ||
174 | void dump_stack(void) | 198 | void dump_stack(void) |
175 | { | 199 | { |
176 | __backtrace(); | 200 | dump_backtrace(NULL, NULL); |
177 | } | 201 | } |
178 | 202 | ||
179 | EXPORT_SYMBOL(dump_stack); | 203 | EXPORT_SYMBOL(dump_stack); |
180 | 204 | ||
181 | void show_stack(struct task_struct *tsk, unsigned long *sp) | 205 | void show_stack(struct task_struct *tsk, unsigned long *sp) |
182 | { | 206 | { |
183 | unsigned long fp; | 207 | dump_backtrace(NULL, tsk); |
184 | |||
185 | if (!tsk) | ||
186 | tsk = current; | ||
187 | |||
188 | if (tsk != current) | ||
189 | fp = thread_saved_fp(tsk); | ||
190 | else | ||
191 | asm("mov %0, fp" : "=r" (fp) : : "cc"); | ||
192 | |||
193 | c_backtrace(fp, 0x10); | ||
194 | barrier(); | 208 | barrier(); |
195 | } | 209 | } |
196 | 210 | ||
diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c new file mode 100644 index 000000000000..1dedc2c7ff49 --- /dev/null +++ b/arch/arm/kernel/unwind.c | |||
@@ -0,0 +1,434 @@ | |||
1 | /* | ||
2 | * arch/arm/kernel/unwind.c | ||
3 | * | ||
4 | * Copyright (C) 2008 ARM Limited | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | * | ||
20 | * Stack unwinding support for ARM | ||
21 | * | ||
22 | * An ARM EABI version of gcc is required to generate the unwind | ||
23 | * tables. For information about the structure of the unwind tables, | ||
24 | * see "Exception Handling ABI for the ARM Architecture" at: | ||
25 | * | ||
26 | * http://infocenter.arm.com/help/topic/com.arm.doc.subset.swdev.abi/index.html | ||
27 | */ | ||
28 | |||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/sched.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/spinlock.h> | ||
35 | #include <linux/list.h> | ||
36 | |||
37 | #include <asm/stacktrace.h> | ||
38 | #include <asm/traps.h> | ||
39 | #include <asm/unwind.h> | ||
40 | |||
41 | /* Dummy functions to avoid linker complaints */ | ||
42 | void __aeabi_unwind_cpp_pr0(void) | ||
43 | { | ||
44 | }; | ||
45 | EXPORT_SYMBOL(__aeabi_unwind_cpp_pr0); | ||
46 | |||
47 | void __aeabi_unwind_cpp_pr1(void) | ||
48 | { | ||
49 | }; | ||
50 | EXPORT_SYMBOL(__aeabi_unwind_cpp_pr1); | ||
51 | |||
52 | void __aeabi_unwind_cpp_pr2(void) | ||
53 | { | ||
54 | }; | ||
55 | EXPORT_SYMBOL(__aeabi_unwind_cpp_pr2); | ||
56 | |||
57 | struct unwind_ctrl_block { | ||
58 | unsigned long vrs[16]; /* virtual register set */ | ||
59 | unsigned long *insn; /* pointer to the current instructions word */ | ||
60 | int entries; /* number of entries left to interpret */ | ||
61 | int byte; /* current byte number in the instructions word */ | ||
62 | }; | ||
63 | |||
64 | enum regs { | ||
65 | FP = 11, | ||
66 | SP = 13, | ||
67 | LR = 14, | ||
68 | PC = 15 | ||
69 | }; | ||
70 | |||
71 | extern struct unwind_idx __start_unwind_idx[]; | ||
72 | extern struct unwind_idx __stop_unwind_idx[]; | ||
73 | |||
74 | static DEFINE_SPINLOCK(unwind_lock); | ||
75 | static LIST_HEAD(unwind_tables); | ||
76 | |||
77 | /* Convert a prel31 symbol to an absolute address */ | ||
78 | #define prel31_to_addr(ptr) \ | ||
79 | ({ \ | ||
80 | /* sign-extend to 32 bits */ \ | ||
81 | long offset = (((long)*(ptr)) << 1) >> 1; \ | ||
82 | (unsigned long)(ptr) + offset; \ | ||
83 | }) | ||
84 | |||
85 | /* | ||
86 | * Binary search in the unwind index. The entries entries are | ||
87 | * guaranteed to be sorted in ascending order by the linker. | ||
88 | */ | ||
89 | static struct unwind_idx *search_index(unsigned long addr, | ||
90 | struct unwind_idx *first, | ||
91 | struct unwind_idx *last) | ||
92 | { | ||
93 | pr_debug("%s(%08lx, %p, %p)\n", __func__, addr, first, last); | ||
94 | |||
95 | if (addr < first->addr) { | ||
96 | pr_warning("unwind: Unknown symbol address %08lx\n", addr); | ||
97 | return NULL; | ||
98 | } else if (addr >= last->addr) | ||
99 | return last; | ||
100 | |||
101 | while (first < last - 1) { | ||
102 | struct unwind_idx *mid = first + ((last - first + 1) >> 1); | ||
103 | |||
104 | if (addr < mid->addr) | ||
105 | last = mid; | ||
106 | else | ||
107 | first = mid; | ||
108 | } | ||
109 | |||
110 | return first; | ||
111 | } | ||
112 | |||
113 | static struct unwind_idx *unwind_find_idx(unsigned long addr) | ||
114 | { | ||
115 | struct unwind_idx *idx = NULL; | ||
116 | unsigned long flags; | ||
117 | |||
118 | pr_debug("%s(%08lx)\n", __func__, addr); | ||
119 | |||
120 | if (core_kernel_text(addr)) | ||
121 | /* main unwind table */ | ||
122 | idx = search_index(addr, __start_unwind_idx, | ||
123 | __stop_unwind_idx - 1); | ||
124 | else { | ||
125 | /* module unwind tables */ | ||
126 | struct unwind_table *table; | ||
127 | |||
128 | spin_lock_irqsave(&unwind_lock, flags); | ||
129 | list_for_each_entry(table, &unwind_tables, list) { | ||
130 | if (addr >= table->begin_addr && | ||
131 | addr < table->end_addr) { | ||
132 | idx = search_index(addr, table->start, | ||
133 | table->stop - 1); | ||
134 | break; | ||
135 | } | ||
136 | } | ||
137 | spin_unlock_irqrestore(&unwind_lock, flags); | ||
138 | } | ||
139 | |||
140 | pr_debug("%s: idx = %p\n", __func__, idx); | ||
141 | return idx; | ||
142 | } | ||
143 | |||
144 | static unsigned long unwind_get_byte(struct unwind_ctrl_block *ctrl) | ||
145 | { | ||
146 | unsigned long ret; | ||
147 | |||
148 | if (ctrl->entries <= 0) { | ||
149 | pr_warning("unwind: Corrupt unwind table\n"); | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | ret = (*ctrl->insn >> (ctrl->byte * 8)) & 0xff; | ||
154 | |||
155 | if (ctrl->byte == 0) { | ||
156 | ctrl->insn++; | ||
157 | ctrl->entries--; | ||
158 | ctrl->byte = 3; | ||
159 | } else | ||
160 | ctrl->byte--; | ||
161 | |||
162 | return ret; | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * Execute the current unwind instruction. | ||
167 | */ | ||
168 | static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) | ||
169 | { | ||
170 | unsigned long insn = unwind_get_byte(ctrl); | ||
171 | |||
172 | pr_debug("%s: insn = %08lx\n", __func__, insn); | ||
173 | |||
174 | if ((insn & 0xc0) == 0x00) | ||
175 | ctrl->vrs[SP] += ((insn & 0x3f) << 2) + 4; | ||
176 | else if ((insn & 0xc0) == 0x40) | ||
177 | ctrl->vrs[SP] -= ((insn & 0x3f) << 2) + 4; | ||
178 | else if ((insn & 0xf0) == 0x80) { | ||
179 | unsigned long mask; | ||
180 | unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; | ||
181 | int load_sp, reg = 4; | ||
182 | |||
183 | insn = (insn << 8) | unwind_get_byte(ctrl); | ||
184 | mask = insn & 0x0fff; | ||
185 | if (mask == 0) { | ||
186 | pr_warning("unwind: 'Refuse to unwind' instruction %04lx\n", | ||
187 | insn); | ||
188 | return -URC_FAILURE; | ||
189 | } | ||
190 | |||
191 | /* pop R4-R15 according to mask */ | ||
192 | load_sp = mask & (1 << (13 - 4)); | ||
193 | while (mask) { | ||
194 | if (mask & 1) | ||
195 | ctrl->vrs[reg] = *vsp++; | ||
196 | mask >>= 1; | ||
197 | reg++; | ||
198 | } | ||
199 | if (!load_sp) | ||
200 | ctrl->vrs[SP] = (unsigned long)vsp; | ||
201 | } else if ((insn & 0xf0) == 0x90 && | ||
202 | (insn & 0x0d) != 0x0d) | ||
203 | ctrl->vrs[SP] = ctrl->vrs[insn & 0x0f]; | ||
204 | else if ((insn & 0xf0) == 0xa0) { | ||
205 | unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; | ||
206 | int reg; | ||
207 | |||
208 | /* pop R4-R[4+bbb] */ | ||
209 | for (reg = 4; reg <= 4 + (insn & 7); reg++) | ||
210 | ctrl->vrs[reg] = *vsp++; | ||
211 | if (insn & 0x80) | ||
212 | ctrl->vrs[14] = *vsp++; | ||
213 | ctrl->vrs[SP] = (unsigned long)vsp; | ||
214 | } else if (insn == 0xb0) { | ||
215 | ctrl->vrs[PC] = ctrl->vrs[LR]; | ||
216 | /* no further processing */ | ||
217 | ctrl->entries = 0; | ||
218 | } else if (insn == 0xb1) { | ||
219 | unsigned long mask = unwind_get_byte(ctrl); | ||
220 | unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; | ||
221 | int reg = 0; | ||
222 | |||
223 | if (mask == 0 || mask & 0xf0) { | ||
224 | pr_warning("unwind: Spare encoding %04lx\n", | ||
225 | (insn << 8) | mask); | ||
226 | return -URC_FAILURE; | ||
227 | } | ||
228 | |||
229 | /* pop R0-R3 according to mask */ | ||
230 | while (mask) { | ||
231 | if (mask & 1) | ||
232 | ctrl->vrs[reg] = *vsp++; | ||
233 | mask >>= 1; | ||
234 | reg++; | ||
235 | } | ||
236 | ctrl->vrs[SP] = (unsigned long)vsp; | ||
237 | } else if (insn == 0xb2) { | ||
238 | unsigned long uleb128 = unwind_get_byte(ctrl); | ||
239 | |||
240 | ctrl->vrs[SP] += 0x204 + (uleb128 << 2); | ||
241 | } else { | ||
242 | pr_warning("unwind: Unhandled instruction %02lx\n", insn); | ||
243 | return -URC_FAILURE; | ||
244 | } | ||
245 | |||
246 | pr_debug("%s: fp = %08lx sp = %08lx lr = %08lx pc = %08lx\n", __func__, | ||
247 | ctrl->vrs[FP], ctrl->vrs[SP], ctrl->vrs[LR], ctrl->vrs[PC]); | ||
248 | |||
249 | return URC_OK; | ||
250 | } | ||
251 | |||
252 | /* | ||
253 | * Unwind a single frame starting with *sp for the symbol at *pc. It | ||
254 | * updates the *pc and *sp with the new values. | ||
255 | */ | ||
256 | int unwind_frame(struct stackframe *frame) | ||
257 | { | ||
258 | unsigned long high, low; | ||
259 | struct unwind_idx *idx; | ||
260 | struct unwind_ctrl_block ctrl; | ||
261 | |||
262 | /* only go to a higher address on the stack */ | ||
263 | low = frame->sp; | ||
264 | high = ALIGN(low, THREAD_SIZE) + THREAD_SIZE; | ||
265 | |||
266 | pr_debug("%s(pc = %08lx lr = %08lx sp = %08lx)\n", __func__, | ||
267 | frame->pc, frame->lr, frame->sp); | ||
268 | |||
269 | if (!kernel_text_address(frame->pc)) | ||
270 | return -URC_FAILURE; | ||
271 | |||
272 | idx = unwind_find_idx(frame->pc); | ||
273 | if (!idx) { | ||
274 | pr_warning("unwind: Index not found %08lx\n", frame->pc); | ||
275 | return -URC_FAILURE; | ||
276 | } | ||
277 | |||
278 | ctrl.vrs[FP] = frame->fp; | ||
279 | ctrl.vrs[SP] = frame->sp; | ||
280 | ctrl.vrs[LR] = frame->lr; | ||
281 | ctrl.vrs[PC] = 0; | ||
282 | |||
283 | if (idx->insn == 1) | ||
284 | /* can't unwind */ | ||
285 | return -URC_FAILURE; | ||
286 | else if ((idx->insn & 0x80000000) == 0) | ||
287 | /* prel31 to the unwind table */ | ||
288 | ctrl.insn = (unsigned long *)prel31_to_addr(&idx->insn); | ||
289 | else if ((idx->insn & 0xff000000) == 0x80000000) | ||
290 | /* only personality routine 0 supported in the index */ | ||
291 | ctrl.insn = &idx->insn; | ||
292 | else { | ||
293 | pr_warning("unwind: Unsupported personality routine %08lx in the index at %p\n", | ||
294 | idx->insn, idx); | ||
295 | return -URC_FAILURE; | ||
296 | } | ||
297 | |||
298 | /* check the personality routine */ | ||
299 | if ((*ctrl.insn & 0xff000000) == 0x80000000) { | ||
300 | ctrl.byte = 2; | ||
301 | ctrl.entries = 1; | ||
302 | } else if ((*ctrl.insn & 0xff000000) == 0x81000000) { | ||
303 | ctrl.byte = 1; | ||
304 | ctrl.entries = 1 + ((*ctrl.insn & 0x00ff0000) >> 16); | ||
305 | } else { | ||
306 | pr_warning("unwind: Unsupported personality routine %08lx at %p\n", | ||
307 | *ctrl.insn, ctrl.insn); | ||
308 | return -URC_FAILURE; | ||
309 | } | ||
310 | |||
311 | while (ctrl.entries > 0) { | ||
312 | int urc; | ||
313 | |||
314 | if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= high) | ||
315 | return -URC_FAILURE; | ||
316 | urc = unwind_exec_insn(&ctrl); | ||
317 | if (urc < 0) | ||
318 | return urc; | ||
319 | } | ||
320 | |||
321 | if (ctrl.vrs[PC] == 0) | ||
322 | ctrl.vrs[PC] = ctrl.vrs[LR]; | ||
323 | |||
324 | frame->fp = ctrl.vrs[FP]; | ||
325 | frame->sp = ctrl.vrs[SP]; | ||
326 | frame->lr = ctrl.vrs[LR]; | ||
327 | frame->pc = ctrl.vrs[PC]; | ||
328 | |||
329 | return URC_OK; | ||
330 | } | ||
331 | |||
332 | void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk) | ||
333 | { | ||
334 | struct stackframe frame; | ||
335 | unsigned long high, low; | ||
336 | register unsigned long current_sp asm ("sp"); | ||
337 | |||
338 | pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); | ||
339 | |||
340 | if (!tsk) | ||
341 | tsk = current; | ||
342 | |||
343 | if (regs) { | ||
344 | frame.fp = regs->ARM_fp; | ||
345 | frame.sp = regs->ARM_sp; | ||
346 | frame.lr = regs->ARM_lr; | ||
347 | frame.pc = regs->ARM_pc; | ||
348 | } else if (tsk == current) { | ||
349 | frame.fp = (unsigned long)__builtin_frame_address(0); | ||
350 | frame.sp = current_sp; | ||
351 | frame.lr = (unsigned long)__builtin_return_address(0); | ||
352 | frame.pc = (unsigned long)unwind_backtrace; | ||
353 | } else { | ||
354 | /* task blocked in __switch_to */ | ||
355 | frame.fp = thread_saved_fp(tsk); | ||
356 | frame.sp = thread_saved_sp(tsk); | ||
357 | /* | ||
358 | * The function calling __switch_to cannot be a leaf function | ||
359 | * so LR is recovered from the stack. | ||
360 | */ | ||
361 | frame.lr = 0; | ||
362 | frame.pc = thread_saved_pc(tsk); | ||
363 | } | ||
364 | |||
365 | low = frame.sp & ~(THREAD_SIZE - 1); | ||
366 | high = low + THREAD_SIZE; | ||
367 | |||
368 | while (1) { | ||
369 | int urc; | ||
370 | unsigned long where = frame.pc; | ||
371 | |||
372 | urc = unwind_frame(&frame); | ||
373 | if (urc < 0) | ||
374 | break; | ||
375 | dump_backtrace_entry(where, frame.pc, frame.sp - 4); | ||
376 | } | ||
377 | } | ||
378 | |||
379 | struct unwind_table *unwind_table_add(unsigned long start, unsigned long size, | ||
380 | unsigned long text_addr, | ||
381 | unsigned long text_size) | ||
382 | { | ||
383 | unsigned long flags; | ||
384 | struct unwind_idx *idx; | ||
385 | struct unwind_table *tab = kmalloc(sizeof(*tab), GFP_KERNEL); | ||
386 | |||
387 | pr_debug("%s(%08lx, %08lx, %08lx, %08lx)\n", __func__, start, size, | ||
388 | text_addr, text_size); | ||
389 | |||
390 | if (!tab) | ||
391 | return tab; | ||
392 | |||
393 | tab->start = (struct unwind_idx *)start; | ||
394 | tab->stop = (struct unwind_idx *)(start + size); | ||
395 | tab->begin_addr = text_addr; | ||
396 | tab->end_addr = text_addr + text_size; | ||
397 | |||
398 | /* Convert the symbol addresses to absolute values */ | ||
399 | for (idx = tab->start; idx < tab->stop; idx++) | ||
400 | idx->addr = prel31_to_addr(&idx->addr); | ||
401 | |||
402 | spin_lock_irqsave(&unwind_lock, flags); | ||
403 | list_add_tail(&tab->list, &unwind_tables); | ||
404 | spin_unlock_irqrestore(&unwind_lock, flags); | ||
405 | |||
406 | return tab; | ||
407 | } | ||
408 | |||
409 | void unwind_table_del(struct unwind_table *tab) | ||
410 | { | ||
411 | unsigned long flags; | ||
412 | |||
413 | if (!tab) | ||
414 | return; | ||
415 | |||
416 | spin_lock_irqsave(&unwind_lock, flags); | ||
417 | list_del(&tab->list); | ||
418 | spin_unlock_irqrestore(&unwind_lock, flags); | ||
419 | |||
420 | kfree(tab); | ||
421 | } | ||
422 | |||
423 | int __init unwind_init(void) | ||
424 | { | ||
425 | struct unwind_idx *idx; | ||
426 | |||
427 | /* Convert the symbol addresses to absolute values */ | ||
428 | for (idx = __start_unwind_idx; idx < __stop_unwind_idx; idx++) | ||
429 | idx->addr = prel31_to_addr(&idx->addr); | ||
430 | |||
431 | pr_debug("unwind: ARM stack unwinding initialised\n"); | ||
432 | |||
433 | return 0; | ||
434 | } | ||
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 1602373e539c..c90f27250ead 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S | |||
@@ -82,6 +82,8 @@ SECTIONS | |||
82 | EXIT_TEXT | 82 | EXIT_TEXT |
83 | EXIT_DATA | 83 | EXIT_DATA |
84 | *(.exitcall.exit) | 84 | *(.exitcall.exit) |
85 | *(.ARM.exidx.exit.text) | ||
86 | *(.ARM.extab.exit.text) | ||
85 | #ifndef CONFIG_MMU | 87 | #ifndef CONFIG_MMU |
86 | *(.fixup) | 88 | *(.fixup) |
87 | *(__ex_table) | 89 | *(__ex_table) |
@@ -112,6 +114,23 @@ SECTIONS | |||
112 | 114 | ||
113 | _etext = .; /* End of text and rodata section */ | 115 | _etext = .; /* End of text and rodata section */ |
114 | 116 | ||
117 | #ifdef CONFIG_ARM_UNWIND | ||
118 | /* | ||
119 | * Stack unwinding tables | ||
120 | */ | ||
121 | . = ALIGN(8); | ||
122 | .ARM.unwind_idx : { | ||
123 | __start_unwind_idx = .; | ||
124 | *(.ARM.exidx*) | ||
125 | __stop_unwind_idx = .; | ||
126 | } | ||
127 | .ARM.unwind_tab : { | ||
128 | __start_unwind_tab = .; | ||
129 | *(.ARM.extab*) | ||
130 | __stop_unwind_tab = .; | ||
131 | } | ||
132 | #endif | ||
133 | |||
115 | #ifdef CONFIG_XIP_KERNEL | 134 | #ifdef CONFIG_XIP_KERNEL |
116 | __data_loc = ALIGN(4); /* location in binary */ | 135 | __data_loc = ALIGN(4); /* location in binary */ |
117 | . = PAGE_OFFSET + TEXT_OFFSET; | 136 | . = PAGE_OFFSET + TEXT_OFFSET; |