Commit 358bf29e802e7f03d32dd4ba2064e1bba23a11fa
1 parent
cdbdb648
Thumb prefetch abort fix.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1805 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
38 additions
and
2 deletions
target-arm/translate.c
| ... | ... | @@ -2295,8 +2295,21 @@ static void disas_thumb_insn(DisasContext *s) |
| 2295 | 2295 | |
| 2296 | 2296 | case 14: |
| 2297 | 2297 | /* unconditional branch */ |
| 2298 | - if (insn & (1 << 11)) | |
| 2299 | - goto undef; /* Second half of a blx */ | |
| 2298 | + if (insn & (1 << 11)) { | |
| 2299 | + /* Second half of blx. */ | |
| 2300 | + offset = ((insn & 0x7ff) << 1); | |
| 2301 | + gen_movl_T0_reg(s, 14); | |
| 2302 | + gen_op_movl_T1_im(offset); | |
| 2303 | + gen_op_addl_T0_T1(); | |
| 2304 | + gen_op_movl_T1_im(0xfffffffc); | |
| 2305 | + gen_op_andl_T0_T1(); | |
| 2306 | + | |
| 2307 | + val = (uint32_t)s->pc; | |
| 2308 | + gen_op_movl_T1_im(val | 1); | |
| 2309 | + gen_movl_reg_T1(s, 14); | |
| 2310 | + gen_bx(s); | |
| 2311 | + break; | |
| 2312 | + } | |
| 2300 | 2313 | val = (uint32_t)s->pc; |
| 2301 | 2314 | offset = ((int32_t)insn << 21) >> 21; |
| 2302 | 2315 | val += (offset << 1) + 2; |
| ... | ... | @@ -2305,6 +2318,29 @@ static void disas_thumb_insn(DisasContext *s) |
| 2305 | 2318 | |
| 2306 | 2319 | case 15: |
| 2307 | 2320 | /* branch and link [and switch to arm] */ |
| 2321 | + if ((s->pc & ~TARGET_PAGE_MASK) == 0) { | |
| 2322 | + /* Instruction spans a page boundary. Implement it as two | |
| 2323 | + 16-bit instructions in case the second half causes an | |
| 2324 | + prefetch abort. */ | |
| 2325 | + offset = ((int32_t)insn << 21) >> 9; | |
| 2326 | + val = s->pc + 2 + offset; | |
| 2327 | + gen_op_movl_T0_im(val); | |
| 2328 | + gen_movl_reg_T0(s, 14); | |
| 2329 | + break; | |
| 2330 | + } | |
| 2331 | + if (insn & (1 << 11)) { | |
| 2332 | + /* Second half of bl. */ | |
| 2333 | + offset = ((insn & 0x7ff) << 1) | 1; | |
| 2334 | + gen_movl_T0_reg(s, 14); | |
| 2335 | + gen_op_movl_T1_im(offset); | |
| 2336 | + gen_op_addl_T0_T1(); | |
| 2337 | + | |
| 2338 | + val = (uint32_t)s->pc; | |
| 2339 | + gen_op_movl_T1_im(val | 1); | |
| 2340 | + gen_movl_reg_T1(s, 14); | |
| 2341 | + gen_bx(s); | |
| 2342 | + break; | |
| 2343 | + } | |
| 2308 | 2344 | offset = ((int32_t)insn << 21) >> 10; |
| 2309 | 2345 | insn = lduw_code(s->pc); |
| 2310 | 2346 | offset |= insn & 0x7ff; | ... | ... |