diff options
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r-- | arch/arm/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/kernel/kprobes-test.c | 66 | ||||
-rw-r--r-- | arch/arm/kernel/opcodes.c | 72 | ||||
-rw-r--r-- | arch/arm/kernel/smp_twd.c | 95 | ||||
-rw-r--r-- | arch/arm/kernel/swp_emulate.c | 16 | ||||
-rw-r--r-- | arch/arm/kernel/tcm.c | 22 |
6 files changed, 203 insertions, 70 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 16eed6aebfa4..43b740d0e374 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
@@ -13,7 +13,7 @@ CFLAGS_REMOVE_return_address.o = -pg | |||
13 | 13 | ||
14 | # Object file lists. | 14 | # Object file lists. |
15 | 15 | ||
16 | obj-y := elf.o entry-armv.o entry-common.o irq.o \ | 16 | obj-y := elf.o entry-armv.o entry-common.o irq.o opcodes.o \ |
17 | process.o ptrace.o return_address.o setup.o signal.o \ | 17 | process.o ptrace.o return_address.o setup.o signal.o \ |
18 | sys_arm.o stacktrace.o time.o traps.o | 18 | sys_arm.o stacktrace.o time.o traps.o |
19 | 19 | ||
diff --git a/arch/arm/kernel/kprobes-test.c b/arch/arm/kernel/kprobes-test.c index e17cdd6d90d8..1862d8f2fd44 100644 --- a/arch/arm/kernel/kprobes-test.c +++ b/arch/arm/kernel/kprobes-test.c | |||
@@ -202,6 +202,8 @@ | |||
202 | #include <linux/slab.h> | 202 | #include <linux/slab.h> |
203 | #include <linux/kprobes.h> | 203 | #include <linux/kprobes.h> |
204 | 204 | ||
205 | #include <asm/opcodes.h> | ||
206 | |||
205 | #include "kprobes.h" | 207 | #include "kprobes.h" |
206 | #include "kprobes-test.h" | 208 | #include "kprobes-test.h" |
207 | 209 | ||
@@ -1050,65 +1052,9 @@ static int test_instance; | |||
1050 | 1052 | ||
1051 | static unsigned long test_check_cc(int cc, unsigned long cpsr) | 1053 | static unsigned long test_check_cc(int cc, unsigned long cpsr) |
1052 | { | 1054 | { |
1053 | unsigned long temp; | 1055 | int ret = arm_check_condition(cc << 28, cpsr); |
1054 | |||
1055 | switch (cc) { | ||
1056 | case 0x0: /* eq */ | ||
1057 | return cpsr & PSR_Z_BIT; | ||
1058 | |||
1059 | case 0x1: /* ne */ | ||
1060 | return (~cpsr) & PSR_Z_BIT; | ||
1061 | |||
1062 | case 0x2: /* cs */ | ||
1063 | return cpsr & PSR_C_BIT; | ||
1064 | |||
1065 | case 0x3: /* cc */ | ||
1066 | return (~cpsr) & PSR_C_BIT; | ||
1067 | |||
1068 | case 0x4: /* mi */ | ||
1069 | return cpsr & PSR_N_BIT; | ||
1070 | |||
1071 | case 0x5: /* pl */ | ||
1072 | return (~cpsr) & PSR_N_BIT; | ||
1073 | |||
1074 | case 0x6: /* vs */ | ||
1075 | return cpsr & PSR_V_BIT; | ||
1076 | |||
1077 | case 0x7: /* vc */ | ||
1078 | return (~cpsr) & PSR_V_BIT; | ||
1079 | 1056 | ||
1080 | case 0x8: /* hi */ | 1057 | return (ret != ARM_OPCODE_CONDTEST_FAIL); |
1081 | cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ | ||
1082 | return cpsr & PSR_C_BIT; | ||
1083 | |||
1084 | case 0x9: /* ls */ | ||
1085 | cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ | ||
1086 | return (~cpsr) & PSR_C_BIT; | ||
1087 | |||
1088 | case 0xa: /* ge */ | ||
1089 | cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ | ||
1090 | return (~cpsr) & PSR_N_BIT; | ||
1091 | |||
1092 | case 0xb: /* lt */ | ||
1093 | cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ | ||
1094 | return cpsr & PSR_N_BIT; | ||
1095 | |||
1096 | case 0xc: /* gt */ | ||
1097 | temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ | ||
1098 | temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */ | ||
1099 | return (~temp) & PSR_N_BIT; | ||
1100 | |||
1101 | case 0xd: /* le */ | ||
1102 | temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ | ||
1103 | temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */ | ||
1104 | return temp & PSR_N_BIT; | ||
1105 | |||
1106 | case 0xe: /* al */ | ||
1107 | case 0xf: /* unconditional */ | ||
1108 | return true; | ||
1109 | } | ||
1110 | BUG(); | ||
1111 | return false; | ||
1112 | } | 1058 | } |
1113 | 1059 | ||
1114 | static int is_last_scenario; | 1060 | static int is_last_scenario; |
@@ -1128,7 +1074,9 @@ static unsigned long test_context_cpsr(int scenario) | |||
1128 | 1074 | ||
1129 | if (!test_case_is_thumb) { | 1075 | if (!test_case_is_thumb) { |
1130 | /* Testing ARM code */ | 1076 | /* Testing ARM code */ |
1131 | probe_should_run = test_check_cc(current_instruction >> 28, cpsr) != 0; | 1077 | int cc = current_instruction >> 28; |
1078 | |||
1079 | probe_should_run = test_check_cc(cc, cpsr) != 0; | ||
1132 | if (scenario == 15) | 1080 | if (scenario == 15) |
1133 | is_last_scenario = true; | 1081 | is_last_scenario = true; |
1134 | 1082 | ||
diff --git a/arch/arm/kernel/opcodes.c b/arch/arm/kernel/opcodes.c new file mode 100644 index 000000000000..f8179c6a817f --- /dev/null +++ b/arch/arm/kernel/opcodes.c | |||
@@ -0,0 +1,72 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/kernel/opcodes.c | ||
3 | * | ||
4 | * A32 condition code lookup feature moved from nwfpe/fpopcode.c | ||
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 | |||
11 | #include <linux/module.h> | ||
12 | #include <asm/opcodes.h> | ||
13 | |||
14 | #define ARM_OPCODE_CONDITION_UNCOND 0xf | ||
15 | |||
16 | /* | ||
17 | * condition code lookup table | ||
18 | * index into the table is test code: EQ, NE, ... LT, GT, AL, NV | ||
19 | * | ||
20 | * bit position in short is condition code: NZCV | ||
21 | */ | ||
22 | static const unsigned short cc_map[16] = { | ||
23 | 0xF0F0, /* EQ == Z set */ | ||
24 | 0x0F0F, /* NE */ | ||
25 | 0xCCCC, /* CS == C set */ | ||
26 | 0x3333, /* CC */ | ||
27 | 0xFF00, /* MI == N set */ | ||
28 | 0x00FF, /* PL */ | ||
29 | 0xAAAA, /* VS == V set */ | ||
30 | 0x5555, /* VC */ | ||
31 | 0x0C0C, /* HI == C set && Z clear */ | ||
32 | 0xF3F3, /* LS == C clear || Z set */ | ||
33 | 0xAA55, /* GE == (N==V) */ | ||
34 | 0x55AA, /* LT == (N!=V) */ | ||
35 | 0x0A05, /* GT == (!Z && (N==V)) */ | ||
36 | 0xF5FA, /* LE == (Z || (N!=V)) */ | ||
37 | 0xFFFF, /* AL always */ | ||
38 | 0 /* NV */ | ||
39 | }; | ||
40 | |||
41 | /* | ||
42 | * Returns: | ||
43 | * ARM_OPCODE_CONDTEST_FAIL - if condition fails | ||
44 | * ARM_OPCODE_CONDTEST_PASS - if condition passes (including AL) | ||
45 | * ARM_OPCODE_CONDTEST_UNCOND - if NV condition, or separate unconditional | ||
46 | * opcode space from v5 onwards | ||
47 | * | ||
48 | * Code that tests whether a conditional instruction would pass its condition | ||
49 | * check should check that return value == ARM_OPCODE_CONDTEST_PASS. | ||
50 | * | ||
51 | * Code that tests if a condition means that the instruction would be executed | ||
52 | * (regardless of conditional or unconditional) should instead check that the | ||
53 | * return value != ARM_OPCODE_CONDTEST_FAIL. | ||
54 | */ | ||
55 | asmlinkage unsigned int arm_check_condition(u32 opcode, u32 psr) | ||
56 | { | ||
57 | u32 cc_bits = opcode >> 28; | ||
58 | u32 psr_cond = psr >> 28; | ||
59 | unsigned int ret; | ||
60 | |||
61 | if (cc_bits != ARM_OPCODE_CONDITION_UNCOND) { | ||
62 | if ((cc_map[cc_bits] >> (psr_cond)) & 1) | ||
63 | ret = ARM_OPCODE_CONDTEST_PASS; | ||
64 | else | ||
65 | ret = ARM_OPCODE_CONDTEST_FAIL; | ||
66 | } else { | ||
67 | ret = ARM_OPCODE_CONDTEST_UNCOND; | ||
68 | } | ||
69 | |||
70 | return ret; | ||
71 | } | ||
72 | EXPORT_SYMBOL_GPL(arm_check_condition); | ||
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index a8a6682d6b52..c8e938553d47 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c | |||
@@ -10,8 +10,11 @@ | |||
10 | */ | 10 | */ |
11 | #include <linux/init.h> | 11 | #include <linux/init.h> |
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/clk.h> | ||
14 | #include <linux/cpufreq.h> | ||
13 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
14 | #include <linux/device.h> | 16 | #include <linux/device.h> |
17 | #include <linux/err.h> | ||
15 | #include <linux/smp.h> | 18 | #include <linux/smp.h> |
16 | #include <linux/jiffies.h> | 19 | #include <linux/jiffies.h> |
17 | #include <linux/clockchips.h> | 20 | #include <linux/clockchips.h> |
@@ -25,6 +28,7 @@ | |||
25 | /* set up by the platform code */ | 28 | /* set up by the platform code */ |
26 | void __iomem *twd_base; | 29 | void __iomem *twd_base; |
27 | 30 | ||
31 | static struct clk *twd_clk; | ||
28 | static unsigned long twd_timer_rate; | 32 | static unsigned long twd_timer_rate; |
29 | 33 | ||
30 | static struct clock_event_device __percpu **twd_evt; | 34 | static struct clock_event_device __percpu **twd_evt; |
@@ -89,6 +93,52 @@ void twd_timer_stop(struct clock_event_device *clk) | |||
89 | disable_percpu_irq(clk->irq); | 93 | disable_percpu_irq(clk->irq); |
90 | } | 94 | } |
91 | 95 | ||
96 | #ifdef CONFIG_CPU_FREQ | ||
97 | |||
98 | /* | ||
99 | * Updates clockevent frequency when the cpu frequency changes. | ||
100 | * Called on the cpu that is changing frequency with interrupts disabled. | ||
101 | */ | ||
102 | static void twd_update_frequency(void *data) | ||
103 | { | ||
104 | twd_timer_rate = clk_get_rate(twd_clk); | ||
105 | |||
106 | clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate); | ||
107 | } | ||
108 | |||
109 | static int twd_cpufreq_transition(struct notifier_block *nb, | ||
110 | unsigned long state, void *data) | ||
111 | { | ||
112 | struct cpufreq_freqs *freqs = data; | ||
113 | |||
114 | /* | ||
115 | * The twd clock events must be reprogrammed to account for the new | ||
116 | * frequency. The timer is local to a cpu, so cross-call to the | ||
117 | * changing cpu. | ||
118 | */ | ||
119 | if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) | ||
120 | smp_call_function_single(freqs->cpu, twd_update_frequency, | ||
121 | NULL, 1); | ||
122 | |||
123 | return NOTIFY_OK; | ||
124 | } | ||
125 | |||
126 | static struct notifier_block twd_cpufreq_nb = { | ||
127 | .notifier_call = twd_cpufreq_transition, | ||
128 | }; | ||
129 | |||
130 | static int twd_cpufreq_init(void) | ||
131 | { | ||
132 | if (!IS_ERR(twd_clk)) | ||
133 | return cpufreq_register_notifier(&twd_cpufreq_nb, | ||
134 | CPUFREQ_TRANSITION_NOTIFIER); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | core_initcall(twd_cpufreq_init); | ||
139 | |||
140 | #endif | ||
141 | |||
92 | static void __cpuinit twd_calibrate_rate(void) | 142 | static void __cpuinit twd_calibrate_rate(void) |
93 | { | 143 | { |
94 | unsigned long count; | 144 | unsigned long count; |
@@ -140,6 +190,35 @@ static irqreturn_t twd_handler(int irq, void *dev_id) | |||
140 | return IRQ_NONE; | 190 | return IRQ_NONE; |
141 | } | 191 | } |
142 | 192 | ||
193 | static struct clk *twd_get_clock(void) | ||
194 | { | ||
195 | struct clk *clk; | ||
196 | int err; | ||
197 | |||
198 | clk = clk_get_sys("smp_twd", NULL); | ||
199 | if (IS_ERR(clk)) { | ||
200 | pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); | ||
201 | return clk; | ||
202 | } | ||
203 | |||
204 | err = clk_prepare(clk); | ||
205 | if (err) { | ||
206 | pr_err("smp_twd: clock failed to prepare: %d\n", err); | ||
207 | clk_put(clk); | ||
208 | return ERR_PTR(err); | ||
209 | } | ||
210 | |||
211 | err = clk_enable(clk); | ||
212 | if (err) { | ||
213 | pr_err("smp_twd: clock failed to enable: %d\n", err); | ||
214 | clk_unprepare(clk); | ||
215 | clk_put(clk); | ||
216 | return ERR_PTR(err); | ||
217 | } | ||
218 | |||
219 | return clk; | ||
220 | } | ||
221 | |||
143 | /* | 222 | /* |
144 | * Setup the local clock events for a CPU. | 223 | * Setup the local clock events for a CPU. |
145 | */ | 224 | */ |
@@ -165,7 +244,13 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) | |||
165 | } | 244 | } |
166 | } | 245 | } |
167 | 246 | ||
168 | twd_calibrate_rate(); | 247 | if (!twd_clk) |
248 | twd_clk = twd_get_clock(); | ||
249 | |||
250 | if (!IS_ERR_OR_NULL(twd_clk)) | ||
251 | twd_timer_rate = clk_get_rate(twd_clk); | ||
252 | else | ||
253 | twd_calibrate_rate(); | ||
169 | 254 | ||
170 | clk->name = "local_timer"; | 255 | clk->name = "local_timer"; |
171 | clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | | 256 | clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | |
@@ -173,15 +258,11 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) | |||
173 | clk->rating = 350; | 258 | clk->rating = 350; |
174 | clk->set_mode = twd_set_mode; | 259 | clk->set_mode = twd_set_mode; |
175 | clk->set_next_event = twd_set_next_event; | 260 | clk->set_next_event = twd_set_next_event; |
176 | clk->shift = 20; | ||
177 | clk->mult = div_sc(twd_timer_rate, NSEC_PER_SEC, clk->shift); | ||
178 | clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk); | ||
179 | clk->min_delta_ns = clockevent_delta2ns(0xf, clk); | ||
180 | 261 | ||
181 | this_cpu_clk = __this_cpu_ptr(twd_evt); | 262 | this_cpu_clk = __this_cpu_ptr(twd_evt); |
182 | *this_cpu_clk = clk; | 263 | *this_cpu_clk = clk; |
183 | 264 | ||
184 | clockevents_register_device(clk); | 265 | clockevents_config_and_register(clk, twd_timer_rate, |
185 | 266 | 0xf, 0xffffffff); | |
186 | enable_percpu_irq(clk->irq, 0); | 267 | enable_percpu_irq(clk->irq, 0); |
187 | } | 268 | } |
diff --git a/arch/arm/kernel/swp_emulate.c b/arch/arm/kernel/swp_emulate.c index 5f452f8fde05..df745188f5de 100644 --- a/arch/arm/kernel/swp_emulate.c +++ b/arch/arm/kernel/swp_emulate.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/syscalls.h> | 25 | #include <linux/syscalls.h> |
26 | #include <linux/perf_event.h> | 26 | #include <linux/perf_event.h> |
27 | 27 | ||
28 | #include <asm/opcodes.h> | ||
28 | #include <asm/traps.h> | 29 | #include <asm/traps.h> |
29 | #include <asm/uaccess.h> | 30 | #include <asm/uaccess.h> |
30 | 31 | ||
@@ -185,6 +186,21 @@ static int swp_handler(struct pt_regs *regs, unsigned int instr) | |||
185 | 186 | ||
186 | perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->ARM_pc); | 187 | perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->ARM_pc); |
187 | 188 | ||
189 | res = arm_check_condition(instr, regs->ARM_cpsr); | ||
190 | switch (res) { | ||
191 | case ARM_OPCODE_CONDTEST_PASS: | ||
192 | break; | ||
193 | case ARM_OPCODE_CONDTEST_FAIL: | ||
194 | /* Condition failed - return to next instruction */ | ||
195 | regs->ARM_pc += 4; | ||
196 | return 0; | ||
197 | case ARM_OPCODE_CONDTEST_UNCOND: | ||
198 | /* If unconditional encoding - not a SWP, undef */ | ||
199 | return -EFAULT; | ||
200 | default: | ||
201 | return -EINVAL; | ||
202 | } | ||
203 | |||
188 | if (current->pid != previous_pid) { | 204 | if (current->pid != previous_pid) { |
189 | pr_debug("\"%s\" (%ld) uses deprecated SWP{B} instruction\n", | 205 | pr_debug("\"%s\" (%ld) uses deprecated SWP{B} instruction\n", |
190 | current->comm, (unsigned long)current->pid); | 206 | current->comm, (unsigned long)current->pid); |
diff --git a/arch/arm/kernel/tcm.c b/arch/arm/kernel/tcm.c index 30e302d33e0a..01ec453bb924 100644 --- a/arch/arm/kernel/tcm.c +++ b/arch/arm/kernel/tcm.c | |||
@@ -180,9 +180,9 @@ static int __init setup_tcm_bank(u8 type, u8 bank, u8 banks, | |||
180 | */ | 180 | */ |
181 | void __init tcm_init(void) | 181 | void __init tcm_init(void) |
182 | { | 182 | { |
183 | u32 tcm_status = read_cpuid_tcmstatus(); | 183 | u32 tcm_status; |
184 | u8 dtcm_banks = (tcm_status >> 16) & 0x03; | 184 | u8 dtcm_banks; |
185 | u8 itcm_banks = (tcm_status & 0x03); | 185 | u8 itcm_banks; |
186 | size_t dtcm_code_sz = &__edtcm_data - &__sdtcm_data; | 186 | size_t dtcm_code_sz = &__edtcm_data - &__sdtcm_data; |
187 | size_t itcm_code_sz = &__eitcm_text - &__sitcm_text; | 187 | size_t itcm_code_sz = &__eitcm_text - &__sitcm_text; |
188 | char *start; | 188 | char *start; |
@@ -191,6 +191,22 @@ void __init tcm_init(void) | |||
191 | int ret; | 191 | int ret; |
192 | int i; | 192 | int i; |
193 | 193 | ||
194 | /* | ||
195 | * Prior to ARMv5 there is no TCM, and trying to read the status | ||
196 | * register will hang the processor. | ||
197 | */ | ||
198 | if (cpu_architecture() < CPU_ARCH_ARMv5) { | ||
199 | if (dtcm_code_sz || itcm_code_sz) | ||
200 | pr_info("CPU TCM: %u bytes of DTCM and %u bytes of " | ||
201 | "ITCM code compiled in, but no TCM present " | ||
202 | "in pre-v5 CPU\n", dtcm_code_sz, itcm_code_sz); | ||
203 | return; | ||
204 | } | ||
205 | |||
206 | tcm_status = read_cpuid_tcmstatus(); | ||
207 | dtcm_banks = (tcm_status >> 16) & 0x03; | ||
208 | itcm_banks = (tcm_status & 0x03); | ||
209 | |||
194 | /* Values greater than 2 for D/ITCM banks are "reserved" */ | 210 | /* Values greater than 2 for D/ITCM banks are "reserved" */ |
195 | if (dtcm_banks > 2) | 211 | if (dtcm_banks > 2) |
196 | dtcm_banks = 0; | 212 | dtcm_banks = 0; |