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; | ... | ... |