diff options
Diffstat (limited to 'arch/arm/kernel/unwind.c')
-rw-r--r-- | arch/arm/kernel/unwind.c | 137 |
1 files changed, 100 insertions, 37 deletions
diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c index 00df012c4678..3c217694ebec 100644 --- a/arch/arm/kernel/unwind.c +++ b/arch/arm/kernel/unwind.c | |||
@@ -68,6 +68,12 @@ EXPORT_SYMBOL(__aeabi_unwind_cpp_pr2); | |||
68 | struct unwind_ctrl_block { | 68 | struct unwind_ctrl_block { |
69 | unsigned long vrs[16]; /* virtual register set */ | 69 | unsigned long vrs[16]; /* virtual register set */ |
70 | const unsigned long *insn; /* pointer to the current instructions word */ | 70 | const unsigned long *insn; /* pointer to the current instructions word */ |
71 | unsigned long sp_high; /* highest value of sp allowed */ | ||
72 | /* | ||
73 | * 1 : check for stack overflow for each register pop. | ||
74 | * 0 : save overhead if there is plenty of stack remaining. | ||
75 | */ | ||
76 | int check_each_pop; | ||
71 | int entries; /* number of entries left to interpret */ | 77 | int entries; /* number of entries left to interpret */ |
72 | int byte; /* current byte number in the instructions word */ | 78 | int byte; /* current byte number in the instructions word */ |
73 | }; | 79 | }; |
@@ -235,12 +241,85 @@ static unsigned long unwind_get_byte(struct unwind_ctrl_block *ctrl) | |||
235 | return ret; | 241 | return ret; |
236 | } | 242 | } |
237 | 243 | ||
244 | /* Before poping a register check whether it is feasible or not */ | ||
245 | static int unwind_pop_register(struct unwind_ctrl_block *ctrl, | ||
246 | unsigned long **vsp, unsigned int reg) | ||
247 | { | ||
248 | if (unlikely(ctrl->check_each_pop)) | ||
249 | if (*vsp >= (unsigned long *)ctrl->sp_high) | ||
250 | return -URC_FAILURE; | ||
251 | |||
252 | ctrl->vrs[reg] = *(*vsp)++; | ||
253 | return URC_OK; | ||
254 | } | ||
255 | |||
256 | /* Helper functions to execute the instructions */ | ||
257 | static int unwind_exec_pop_subset_r4_to_r13(struct unwind_ctrl_block *ctrl, | ||
258 | unsigned long mask) | ||
259 | { | ||
260 | unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; | ||
261 | int load_sp, reg = 4; | ||
262 | |||
263 | load_sp = mask & (1 << (13 - 4)); | ||
264 | while (mask) { | ||
265 | if (mask & 1) | ||
266 | if (unwind_pop_register(ctrl, &vsp, reg)) | ||
267 | return -URC_FAILURE; | ||
268 | mask >>= 1; | ||
269 | reg++; | ||
270 | } | ||
271 | if (!load_sp) | ||
272 | ctrl->vrs[SP] = (unsigned long)vsp; | ||
273 | |||
274 | return URC_OK; | ||
275 | } | ||
276 | |||
277 | static int unwind_exec_pop_r4_to_rN(struct unwind_ctrl_block *ctrl, | ||
278 | unsigned long insn) | ||
279 | { | ||
280 | unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; | ||
281 | int reg; | ||
282 | |||
283 | /* pop R4-R[4+bbb] */ | ||
284 | for (reg = 4; reg <= 4 + (insn & 7); reg++) | ||
285 | if (unwind_pop_register(ctrl, &vsp, reg)) | ||
286 | return -URC_FAILURE; | ||
287 | |||
288 | if (insn & 0x80) | ||
289 | if (unwind_pop_register(ctrl, &vsp, 14)) | ||
290 | return -URC_FAILURE; | ||
291 | |||
292 | ctrl->vrs[SP] = (unsigned long)vsp; | ||
293 | |||
294 | return URC_OK; | ||
295 | } | ||
296 | |||
297 | static int unwind_exec_pop_subset_r0_to_r3(struct unwind_ctrl_block *ctrl, | ||
298 | unsigned long mask) | ||
299 | { | ||
300 | unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; | ||
301 | int reg = 0; | ||
302 | |||
303 | /* pop R0-R3 according to mask */ | ||
304 | while (mask) { | ||
305 | if (mask & 1) | ||
306 | if (unwind_pop_register(ctrl, &vsp, reg)) | ||
307 | return -URC_FAILURE; | ||
308 | mask >>= 1; | ||
309 | reg++; | ||
310 | } | ||
311 | ctrl->vrs[SP] = (unsigned long)vsp; | ||
312 | |||
313 | return URC_OK; | ||
314 | } | ||
315 | |||
238 | /* | 316 | /* |
239 | * Execute the current unwind instruction. | 317 | * Execute the current unwind instruction. |
240 | */ | 318 | */ |
241 | static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) | 319 | static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) |
242 | { | 320 | { |
243 | unsigned long insn = unwind_get_byte(ctrl); | 321 | unsigned long insn = unwind_get_byte(ctrl); |
322 | int ret = URC_OK; | ||
244 | 323 | ||
245 | pr_debug("%s: insn = %08lx\n", __func__, insn); | 324 | pr_debug("%s: insn = %08lx\n", __func__, insn); |
246 | 325 | ||
@@ -250,8 +329,6 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) | |||
250 | ctrl->vrs[SP] -= ((insn & 0x3f) << 2) + 4; | 329 | ctrl->vrs[SP] -= ((insn & 0x3f) << 2) + 4; |
251 | else if ((insn & 0xf0) == 0x80) { | 330 | else if ((insn & 0xf0) == 0x80) { |
252 | unsigned long mask; | 331 | unsigned long mask; |
253 | unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; | ||
254 | int load_sp, reg = 4; | ||
255 | 332 | ||
256 | insn = (insn << 8) | unwind_get_byte(ctrl); | 333 | insn = (insn << 8) | unwind_get_byte(ctrl); |
257 | mask = insn & 0x0fff; | 334 | mask = insn & 0x0fff; |
@@ -261,29 +338,16 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) | |||
261 | return -URC_FAILURE; | 338 | return -URC_FAILURE; |
262 | } | 339 | } |
263 | 340 | ||
264 | /* pop R4-R15 according to mask */ | 341 | ret = unwind_exec_pop_subset_r4_to_r13(ctrl, mask); |
265 | load_sp = mask & (1 << (13 - 4)); | 342 | if (ret) |
266 | while (mask) { | 343 | goto error; |
267 | if (mask & 1) | ||
268 | ctrl->vrs[reg] = *vsp++; | ||
269 | mask >>= 1; | ||
270 | reg++; | ||
271 | } | ||
272 | if (!load_sp) | ||
273 | ctrl->vrs[SP] = (unsigned long)vsp; | ||
274 | } else if ((insn & 0xf0) == 0x90 && | 344 | } else if ((insn & 0xf0) == 0x90 && |
275 | (insn & 0x0d) != 0x0d) | 345 | (insn & 0x0d) != 0x0d) |
276 | ctrl->vrs[SP] = ctrl->vrs[insn & 0x0f]; | 346 | ctrl->vrs[SP] = ctrl->vrs[insn & 0x0f]; |
277 | else if ((insn & 0xf0) == 0xa0) { | 347 | else if ((insn & 0xf0) == 0xa0) { |
278 | unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; | 348 | ret = unwind_exec_pop_r4_to_rN(ctrl, insn); |
279 | int reg; | 349 | if (ret) |
280 | 350 | goto error; | |
281 | /* pop R4-R[4+bbb] */ | ||
282 | for (reg = 4; reg <= 4 + (insn & 7); reg++) | ||
283 | ctrl->vrs[reg] = *vsp++; | ||
284 | if (insn & 0x80) | ||
285 | ctrl->vrs[14] = *vsp++; | ||
286 | ctrl->vrs[SP] = (unsigned long)vsp; | ||
287 | } else if (insn == 0xb0) { | 351 | } else if (insn == 0xb0) { |
288 | if (ctrl->vrs[PC] == 0) | 352 | if (ctrl->vrs[PC] == 0) |
289 | ctrl->vrs[PC] = ctrl->vrs[LR]; | 353 | ctrl->vrs[PC] = ctrl->vrs[LR]; |
@@ -291,8 +355,6 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) | |||
291 | ctrl->entries = 0; | 355 | ctrl->entries = 0; |
292 | } else if (insn == 0xb1) { | 356 | } else if (insn == 0xb1) { |
293 | unsigned long mask = unwind_get_byte(ctrl); | 357 | unsigned long mask = unwind_get_byte(ctrl); |
294 | unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; | ||
295 | int reg = 0; | ||
296 | 358 | ||
297 | if (mask == 0 || mask & 0xf0) { | 359 | if (mask == 0 || mask & 0xf0) { |
298 | pr_warning("unwind: Spare encoding %04lx\n", | 360 | pr_warning("unwind: Spare encoding %04lx\n", |
@@ -300,14 +362,9 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) | |||
300 | return -URC_FAILURE; | 362 | return -URC_FAILURE; |
301 | } | 363 | } |
302 | 364 | ||
303 | /* pop R0-R3 according to mask */ | 365 | ret = unwind_exec_pop_subset_r0_to_r3(ctrl, mask); |
304 | while (mask) { | 366 | if (ret) |
305 | if (mask & 1) | 367 | goto error; |
306 | ctrl->vrs[reg] = *vsp++; | ||
307 | mask >>= 1; | ||
308 | reg++; | ||
309 | } | ||
310 | ctrl->vrs[SP] = (unsigned long)vsp; | ||
311 | } else if (insn == 0xb2) { | 368 | } else if (insn == 0xb2) { |
312 | unsigned long uleb128 = unwind_get_byte(ctrl); | 369 | unsigned long uleb128 = unwind_get_byte(ctrl); |
313 | 370 | ||
@@ -320,7 +377,8 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) | |||
320 | pr_debug("%s: fp = %08lx sp = %08lx lr = %08lx pc = %08lx\n", __func__, | 377 | pr_debug("%s: fp = %08lx sp = %08lx lr = %08lx pc = %08lx\n", __func__, |
321 | ctrl->vrs[FP], ctrl->vrs[SP], ctrl->vrs[LR], ctrl->vrs[PC]); | 378 | ctrl->vrs[FP], ctrl->vrs[SP], ctrl->vrs[LR], ctrl->vrs[PC]); |
322 | 379 | ||
323 | return URC_OK; | 380 | error: |
381 | return ret; | ||
324 | } | 382 | } |
325 | 383 | ||
326 | /* | 384 | /* |
@@ -329,13 +387,13 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) | |||
329 | */ | 387 | */ |
330 | int unwind_frame(struct stackframe *frame) | 388 | int unwind_frame(struct stackframe *frame) |
331 | { | 389 | { |
332 | unsigned long high, low; | 390 | unsigned long low; |
333 | const struct unwind_idx *idx; | 391 | const struct unwind_idx *idx; |
334 | struct unwind_ctrl_block ctrl; | 392 | struct unwind_ctrl_block ctrl; |
335 | 393 | ||
336 | /* only go to a higher address on the stack */ | 394 | /* store the highest address on the stack to avoid crossing it*/ |
337 | low = frame->sp; | 395 | low = frame->sp; |
338 | high = ALIGN(low, THREAD_SIZE); | 396 | ctrl.sp_high = ALIGN(low, THREAD_SIZE); |
339 | 397 | ||
340 | pr_debug("%s(pc = %08lx lr = %08lx sp = %08lx)\n", __func__, | 398 | pr_debug("%s(pc = %08lx lr = %08lx sp = %08lx)\n", __func__, |
341 | frame->pc, frame->lr, frame->sp); | 399 | frame->pc, frame->lr, frame->sp); |
@@ -382,11 +440,16 @@ int unwind_frame(struct stackframe *frame) | |||
382 | return -URC_FAILURE; | 440 | return -URC_FAILURE; |
383 | } | 441 | } |
384 | 442 | ||
443 | ctrl.check_each_pop = 0; | ||
444 | |||
385 | while (ctrl.entries > 0) { | 445 | while (ctrl.entries > 0) { |
386 | int urc = unwind_exec_insn(&ctrl); | 446 | int urc; |
447 | if ((ctrl.sp_high - ctrl.vrs[SP]) < sizeof(ctrl.vrs)) | ||
448 | ctrl.check_each_pop = 1; | ||
449 | urc = unwind_exec_insn(&ctrl); | ||
387 | if (urc < 0) | 450 | if (urc < 0) |
388 | return urc; | 451 | return urc; |
389 | if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= high) | 452 | if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= ctrl.sp_high) |
390 | return -URC_FAILURE; | 453 | return -URC_FAILURE; |
391 | } | 454 | } |
392 | 455 | ||