diff options
author | Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | 2013-08-13 05:45:19 -0400 |
---|---|---|
committer | Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | 2013-12-16 12:17:33 -0500 |
commit | 2f04304587544dd14277413ebff12fa0f4fc932c (patch) | |
tree | a98aa6a8de4dd5f7c00f6eaeab725791ed688395 /arch/arm64 | |
parent | 1fcf7ce0c60213994269fb59569ec161eb6e08d6 (diff) |
arm64: kernel: refactor code to install/uninstall breakpoints
Most of the code executed to install and uninstall breakpoints is
common and can be factored out in a function that through a runtime
operations type provides the requested implementation.
This patch creates a common function that can be used to install/uninstall
breakpoints and defines the set of operations that can be carried out
through it.
Reviewed-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Diffstat (limited to 'arch/arm64')
-rw-r--r-- | arch/arm64/kernel/hw_breakpoint.c | 142 |
1 files changed, 88 insertions, 54 deletions
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index ff516f6691e4..e89459123fa3 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c | |||
@@ -169,15 +169,63 @@ static enum debug_el debug_exception_level(int privilege) | |||
169 | } | 169 | } |
170 | } | 170 | } |
171 | 171 | ||
172 | /* | 172 | enum hw_breakpoint_ops { |
173 | * Install a perf counter breakpoint. | 173 | HW_BREAKPOINT_INSTALL, |
174 | HW_BREAKPOINT_UNINSTALL | ||
175 | }; | ||
176 | |||
177 | /** | ||
178 | * hw_breakpoint_slot_setup - Find and setup a perf slot according to | ||
179 | * operations | ||
180 | * | ||
181 | * @slots: pointer to array of slots | ||
182 | * @max_slots: max number of slots | ||
183 | * @bp: perf_event to setup | ||
184 | * @ops: operation to be carried out on the slot | ||
185 | * | ||
186 | * Return: | ||
187 | * slot index on success | ||
188 | * -ENOSPC if no slot is available/matches | ||
189 | * -EINVAL on wrong operations parameter | ||
174 | */ | 190 | */ |
175 | int arch_install_hw_breakpoint(struct perf_event *bp) | 191 | static int hw_breakpoint_slot_setup(struct perf_event **slots, int max_slots, |
192 | struct perf_event *bp, | ||
193 | enum hw_breakpoint_ops ops) | ||
194 | { | ||
195 | int i; | ||
196 | struct perf_event **slot; | ||
197 | |||
198 | for (i = 0; i < max_slots; ++i) { | ||
199 | slot = &slots[i]; | ||
200 | switch (ops) { | ||
201 | case HW_BREAKPOINT_INSTALL: | ||
202 | if (!*slot) { | ||
203 | *slot = bp; | ||
204 | return i; | ||
205 | } | ||
206 | break; | ||
207 | case HW_BREAKPOINT_UNINSTALL: | ||
208 | if (*slot == bp) { | ||
209 | *slot = NULL; | ||
210 | return i; | ||
211 | } | ||
212 | break; | ||
213 | default: | ||
214 | pr_warn_once("Unhandled hw breakpoint ops %d\n", ops); | ||
215 | return -EINVAL; | ||
216 | } | ||
217 | } | ||
218 | return -ENOSPC; | ||
219 | } | ||
220 | |||
221 | static int hw_breakpoint_control(struct perf_event *bp, | ||
222 | enum hw_breakpoint_ops ops) | ||
176 | { | 223 | { |
177 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | 224 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); |
178 | struct perf_event **slot, **slots; | 225 | struct perf_event **slots; |
179 | struct debug_info *debug_info = ¤t->thread.debug; | 226 | struct debug_info *debug_info = ¤t->thread.debug; |
180 | int i, max_slots, ctrl_reg, val_reg, reg_enable; | 227 | int i, max_slots, ctrl_reg, val_reg, reg_enable; |
228 | enum debug_el dbg_el = debug_exception_level(info->ctrl.privilege); | ||
181 | u32 ctrl; | 229 | u32 ctrl; |
182 | 230 | ||
183 | if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { | 231 | if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { |
@@ -196,67 +244,53 @@ int arch_install_hw_breakpoint(struct perf_event *bp) | |||
196 | reg_enable = !debug_info->wps_disabled; | 244 | reg_enable = !debug_info->wps_disabled; |
197 | } | 245 | } |
198 | 246 | ||
199 | for (i = 0; i < max_slots; ++i) { | 247 | i = hw_breakpoint_slot_setup(slots, max_slots, bp, ops); |
200 | slot = &slots[i]; | ||
201 | 248 | ||
202 | if (!*slot) { | 249 | if (WARN_ONCE(i < 0, "Can't find any breakpoint slot")) |
203 | *slot = bp; | 250 | return i; |
204 | break; | ||
205 | } | ||
206 | } | ||
207 | 251 | ||
208 | if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot")) | 252 | switch (ops) { |
209 | return -ENOSPC; | 253 | case HW_BREAKPOINT_INSTALL: |
254 | /* | ||
255 | * Ensure debug monitors are enabled at the correct exception | ||
256 | * level. | ||
257 | */ | ||
258 | enable_debug_monitors(dbg_el); | ||
210 | 259 | ||
211 | /* Ensure debug monitors are enabled at the correct exception level. */ | 260 | /* Setup the address register. */ |
212 | enable_debug_monitors(debug_exception_level(info->ctrl.privilege)); | 261 | write_wb_reg(val_reg, i, info->address); |
213 | 262 | ||
214 | /* Setup the address register. */ | 263 | /* Setup the control register. */ |
215 | write_wb_reg(val_reg, i, info->address); | 264 | ctrl = encode_ctrl_reg(info->ctrl); |
265 | write_wb_reg(ctrl_reg, i, | ||
266 | reg_enable ? ctrl | 0x1 : ctrl & ~0x1); | ||
267 | break; | ||
268 | case HW_BREAKPOINT_UNINSTALL: | ||
269 | /* Reset the control register. */ | ||
270 | write_wb_reg(ctrl_reg, i, 0); | ||
216 | 271 | ||
217 | /* Setup the control register. */ | 272 | /* |
218 | ctrl = encode_ctrl_reg(info->ctrl); | 273 | * Release the debug monitors for the correct exception |
219 | write_wb_reg(ctrl_reg, i, reg_enable ? ctrl | 0x1 : ctrl & ~0x1); | 274 | * level. |
275 | */ | ||
276 | disable_debug_monitors(dbg_el); | ||
277 | break; | ||
278 | } | ||
220 | 279 | ||
221 | return 0; | 280 | return 0; |
222 | } | 281 | } |
223 | 282 | ||
224 | void arch_uninstall_hw_breakpoint(struct perf_event *bp) | 283 | /* |
284 | * Install a perf counter breakpoint. | ||
285 | */ | ||
286 | int arch_install_hw_breakpoint(struct perf_event *bp) | ||
225 | { | 287 | { |
226 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | 288 | return hw_breakpoint_control(bp, HW_BREAKPOINT_INSTALL); |
227 | struct perf_event **slot, **slots; | 289 | } |
228 | int i, max_slots, base; | ||
229 | |||
230 | if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { | ||
231 | /* Breakpoint */ | ||
232 | base = AARCH64_DBG_REG_BCR; | ||
233 | slots = this_cpu_ptr(bp_on_reg); | ||
234 | max_slots = core_num_brps; | ||
235 | } else { | ||
236 | /* Watchpoint */ | ||
237 | base = AARCH64_DBG_REG_WCR; | ||
238 | slots = this_cpu_ptr(wp_on_reg); | ||
239 | max_slots = core_num_wrps; | ||
240 | } | ||
241 | |||
242 | /* Remove the breakpoint. */ | ||
243 | for (i = 0; i < max_slots; ++i) { | ||
244 | slot = &slots[i]; | ||
245 | |||
246 | if (*slot == bp) { | ||
247 | *slot = NULL; | ||
248 | break; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot")) | ||
253 | return; | ||
254 | |||
255 | /* Reset the control register. */ | ||
256 | write_wb_reg(base, i, 0); | ||
257 | 290 | ||
258 | /* Release the debug monitors for the correct exception level. */ | 291 | void arch_uninstall_hw_breakpoint(struct perf_event *bp) |
259 | disable_debug_monitors(debug_exception_level(info->ctrl.privilege)); | 292 | { |
293 | hw_breakpoint_control(bp, HW_BREAKPOINT_UNINSTALL); | ||
260 | } | 294 | } |
261 | 295 | ||
262 | static int get_hbp_len(u8 hbp_len) | 296 | static int get_hbp_len(u8 hbp_len) |