Commit 5a91de8c902e8de43d80df2db6f056710d414e56
1 parent
e3b32540
precise exception support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@189 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
137 additions
and
49 deletions
translate-i386.c
... | ... | @@ -160,7 +160,7 @@ enum { |
160 | 160 | }; |
161 | 161 | |
162 | 162 | enum { |
163 | -#define DEF(s, n) INDEX_op_ ## s, | |
163 | +#define DEF(s, n, copy_size) INDEX_op_ ## s, | |
164 | 164 | #include "opc-i386.h" |
165 | 165 | #undef DEF |
166 | 166 | NB_OPS, |
... | ... | @@ -1215,9 +1215,13 @@ static void gen_setcc(DisasContext *s, int b) |
1215 | 1215 | } |
1216 | 1216 | |
1217 | 1217 | /* move T0 to seg_reg and compute if the CPU state may change */ |
1218 | -static void gen_movl_seg_T0(DisasContext *s, int seg_reg) | |
1218 | +static void gen_movl_seg_T0(DisasContext *s, int seg_reg, unsigned int cur_eip) | |
1219 | 1219 | { |
1220 | - gen_op_movl_seg_T0(seg_reg); | |
1220 | + if (!s->vm86) | |
1221 | + gen_op_movl_seg_T0(seg_reg, cur_eip); | |
1222 | + else | |
1223 | + gen_op_movl_seg_T0_vm(offsetof(CPUX86State,segs[seg_reg]), | |
1224 | + offsetof(CPUX86State,seg_cache[seg_reg].base)); | |
1221 | 1225 | if (!s->addseg && seg_reg < R_FS) |
1222 | 1226 | s->is_jmp = 2; /* abort translation because the register may |
1223 | 1227 | have a non zero base */ |
... | ... | @@ -1374,6 +1378,18 @@ static void gen_exception(DisasContext *s, int trapno, unsigned int cur_eip) |
1374 | 1378 | s->is_jmp = 1; |
1375 | 1379 | } |
1376 | 1380 | |
1381 | +/* an interrupt is different from an exception because of the | |
1382 | + priviledge checks */ | |
1383 | +static void gen_interrupt(DisasContext *s, int intno, | |
1384 | + unsigned int cur_eip, unsigned int next_eip) | |
1385 | +{ | |
1386 | + if (s->cc_op != CC_OP_DYNAMIC) | |
1387 | + gen_op_set_cc_op(s->cc_op); | |
1388 | + gen_op_jmp_im(cur_eip); | |
1389 | + gen_op_raise_interrupt(intno, next_eip); | |
1390 | + s->is_jmp = 1; | |
1391 | +} | |
1392 | + | |
1377 | 1393 | /* generate a jump to eip. No segment change must happen before as a |
1378 | 1394 | direct call to the next block may occur */ |
1379 | 1395 | static void gen_jmp(DisasContext *s, unsigned int eip) |
... | ... | @@ -1650,28 +1666,28 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
1650 | 1666 | case 6: /* div */ |
1651 | 1667 | switch(ot) { |
1652 | 1668 | case OT_BYTE: |
1653 | - gen_op_divb_AL_T0(); | |
1669 | + gen_op_divb_AL_T0(pc_start - s->cs_base); | |
1654 | 1670 | break; |
1655 | 1671 | case OT_WORD: |
1656 | - gen_op_divw_AX_T0(); | |
1672 | + gen_op_divw_AX_T0(pc_start - s->cs_base); | |
1657 | 1673 | break; |
1658 | 1674 | default: |
1659 | 1675 | case OT_LONG: |
1660 | - gen_op_divl_EAX_T0(); | |
1676 | + gen_op_divl_EAX_T0(pc_start - s->cs_base); | |
1661 | 1677 | break; |
1662 | 1678 | } |
1663 | 1679 | break; |
1664 | 1680 | case 7: /* idiv */ |
1665 | 1681 | switch(ot) { |
1666 | 1682 | case OT_BYTE: |
1667 | - gen_op_idivb_AL_T0(); | |
1683 | + gen_op_idivb_AL_T0(pc_start - s->cs_base); | |
1668 | 1684 | break; |
1669 | 1685 | case OT_WORD: |
1670 | - gen_op_idivw_AX_T0(); | |
1686 | + gen_op_idivw_AX_T0(pc_start - s->cs_base); | |
1671 | 1687 | break; |
1672 | 1688 | default: |
1673 | 1689 | case OT_LONG: |
1674 | - gen_op_idivl_EAX_T0(); | |
1690 | + gen_op_idivl_EAX_T0(pc_start - s->cs_base); | |
1675 | 1691 | break; |
1676 | 1692 | } |
1677 | 1693 | break; |
... | ... | @@ -1738,7 +1754,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
1738 | 1754 | gen_op_ld_T1_A0[ot](); |
1739 | 1755 | gen_op_addl_A0_im(1 << (ot - OT_WORD + 1)); |
1740 | 1756 | gen_op_lduw_T0_A0(); |
1741 | - gen_movl_seg_T0(s, R_CS); | |
1757 | + gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); | |
1742 | 1758 | gen_op_movl_T0_T1(); |
1743 | 1759 | gen_op_jmp_T0(); |
1744 | 1760 | s->is_jmp = 1; |
... | ... | @@ -1753,7 +1769,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
1753 | 1769 | gen_op_ld_T1_A0[ot](); |
1754 | 1770 | gen_op_addl_A0_im(1 << (ot - OT_WORD + 1)); |
1755 | 1771 | gen_op_lduw_T0_A0(); |
1756 | - gen_movl_seg_T0(s, R_CS); | |
1772 | + gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); | |
1757 | 1773 | gen_op_movl_T0_T1(); |
1758 | 1774 | gen_op_jmp_T0(); |
1759 | 1775 | s->is_jmp = 1; |
... | ... | @@ -1970,13 +1986,13 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
1970 | 1986 | case 0x17: /* pop ss */ |
1971 | 1987 | case 0x1f: /* pop ds */ |
1972 | 1988 | gen_pop_T0(s); |
1973 | - gen_movl_seg_T0(s, b >> 3); | |
1989 | + gen_movl_seg_T0(s, b >> 3, pc_start - s->cs_base); | |
1974 | 1990 | gen_pop_update(s); |
1975 | 1991 | break; |
1976 | 1992 | case 0x1a1: /* pop fs */ |
1977 | 1993 | case 0x1a9: /* pop gs */ |
1978 | 1994 | gen_pop_T0(s); |
1979 | - gen_movl_seg_T0(s, (b >> 3) & 7); | |
1995 | + gen_movl_seg_T0(s, (b >> 3) & 7, pc_start - s->cs_base); | |
1980 | 1996 | gen_pop_update(s); |
1981 | 1997 | break; |
1982 | 1998 | |
... | ... | @@ -2030,7 +2046,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
2030 | 2046 | gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); |
2031 | 2047 | if (reg >= 6 || reg == R_CS) |
2032 | 2048 | goto illegal_op; |
2033 | - gen_movl_seg_T0(s, reg); | |
2049 | + gen_movl_seg_T0(s, reg, pc_start - s->cs_base); | |
2034 | 2050 | break; |
2035 | 2051 | case 0x8c: /* mov Gv, seg */ |
2036 | 2052 | ot = dflag ? OT_LONG : OT_WORD; |
... | ... | @@ -2231,7 +2247,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
2231 | 2247 | gen_op_addl_A0_im(1 << (ot - OT_WORD + 1)); |
2232 | 2248 | /* load the segment first to handle exceptions properly */ |
2233 | 2249 | gen_op_lduw_T0_A0(); |
2234 | - gen_movl_seg_T0(s, op); | |
2250 | + gen_movl_seg_T0(s, op, pc_start - s->cs_base); | |
2235 | 2251 | /* then put the data */ |
2236 | 2252 | gen_op_mov_reg_T1[ot][reg](); |
2237 | 2253 | break; |
... | ... | @@ -2914,7 +2930,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
2914 | 2930 | gen_pop_update(s); |
2915 | 2931 | /* pop selector */ |
2916 | 2932 | gen_pop_T0(s); |
2917 | - gen_movl_seg_T0(s, R_CS); | |
2933 | + gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); | |
2918 | 2934 | gen_pop_update(s); |
2919 | 2935 | /* add stack offset */ |
2920 | 2936 | if (s->ss32) |
... | ... | @@ -2933,7 +2949,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
2933 | 2949 | gen_pop_update(s); |
2934 | 2950 | /* pop selector */ |
2935 | 2951 | gen_pop_T0(s); |
2936 | - gen_movl_seg_T0(s, R_CS); | |
2952 | + gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); | |
2937 | 2953 | gen_pop_update(s); |
2938 | 2954 | s->is_jmp = 1; |
2939 | 2955 | break; |
... | ... | @@ -2950,7 +2966,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
2950 | 2966 | gen_pop_update(s); |
2951 | 2967 | /* pop selector */ |
2952 | 2968 | gen_pop_T0(s); |
2953 | - gen_movl_seg_T0(s, R_CS); | |
2969 | + gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); | |
2954 | 2970 | gen_pop_update(s); |
2955 | 2971 | /* pop eflags */ |
2956 | 2972 | gen_pop_T0(s); |
... | ... | @@ -2995,7 +3011,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
2995 | 3011 | |
2996 | 3012 | /* change cs and pc */ |
2997 | 3013 | gen_op_movl_T0_im(selector); |
2998 | - gen_movl_seg_T0(s, R_CS); | |
3014 | + gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); | |
2999 | 3015 | gen_op_jmp_im((unsigned long)offset); |
3000 | 3016 | s->is_jmp = 1; |
3001 | 3017 | } |
... | ... | @@ -3018,7 +3034,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
3018 | 3034 | |
3019 | 3035 | /* change cs and pc */ |
3020 | 3036 | gen_op_movl_T0_im(selector); |
3021 | - gen_movl_seg_T0(s, R_CS); | |
3037 | + gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); | |
3022 | 3038 | gen_op_jmp_im((unsigned long)offset); |
3023 | 3039 | s->is_jmp = 1; |
3024 | 3040 | } |
... | ... | @@ -3255,14 +3271,15 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
3255 | 3271 | case 0x9b: /* fwait */ |
3256 | 3272 | break; |
3257 | 3273 | case 0xcc: /* int3 */ |
3258 | - gen_exception(s, EXCP03_INT3, s->pc - s->cs_base); | |
3274 | + gen_interrupt(s, EXCP03_INT3, pc_start - s->cs_base, s->pc - s->cs_base); | |
3259 | 3275 | break; |
3260 | 3276 | case 0xcd: /* int N */ |
3261 | 3277 | val = ldub(s->pc++); |
3262 | - if (s->cc_op != CC_OP_DYNAMIC) | |
3263 | - gen_op_set_cc_op(s->cc_op); | |
3264 | - gen_op_int_im(val, pc_start - s->cs_base); | |
3265 | - s->is_jmp = 1; | |
3278 | + /* XXX: add error code for vm86 GPF */ | |
3279 | + if (!s->vm86) | |
3280 | + gen_interrupt(s, val, pc_start - s->cs_base, s->pc - s->cs_base); | |
3281 | + else | |
3282 | + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); | |
3266 | 3283 | break; |
3267 | 3284 | case 0xce: /* into */ |
3268 | 3285 | if (s->cc_op != CC_OP_DYNAMIC) |
... | ... | @@ -3309,9 +3326,9 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
3309 | 3326 | gen_op_mov_reg_T0[ot][reg](); |
3310 | 3327 | gen_lea_modrm(s, modrm, ®_addr, &offset_addr); |
3311 | 3328 | if (ot == OT_WORD) |
3312 | - gen_op_boundw(); | |
3329 | + gen_op_boundw(pc_start - s->cs_base); | |
3313 | 3330 | else |
3314 | - gen_op_boundl(); | |
3331 | + gen_op_boundl(pc_start - s->cs_base); | |
3315 | 3332 | break; |
3316 | 3333 | case 0x1c8 ... 0x1cf: /* bswap reg */ |
3317 | 3334 | reg = b & 7; |
... | ... | @@ -3670,13 +3687,13 @@ static void optimize_flags(uint16_t *opc_buf, int opc_buf_len) |
3670 | 3687 | |
3671 | 3688 | #ifdef DEBUG_DISAS |
3672 | 3689 | static const char *op_str[] = { |
3673 | -#define DEF(s, n) #s, | |
3690 | +#define DEF(s, n, copy_size) #s, | |
3674 | 3691 | #include "opc-i386.h" |
3675 | 3692 | #undef DEF |
3676 | 3693 | }; |
3677 | 3694 | |
3678 | 3695 | static uint8_t op_nb_args[] = { |
3679 | -#define DEF(s, n) n, | |
3696 | +#define DEF(s, n, copy_size) n, | |
3680 | 3697 | #include "opc-i386.h" |
3681 | 3698 | #undef DEF |
3682 | 3699 | }; |
... | ... | @@ -3706,7 +3723,6 @@ static void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf) |
3706 | 3723 | |
3707 | 3724 | #endif |
3708 | 3725 | |
3709 | -/* XXX: make this buffer thread safe */ | |
3710 | 3726 | /* XXX: make safe guess about sizes */ |
3711 | 3727 | #define MAX_OP_PER_INSTR 32 |
3712 | 3728 | #define OPC_BUF_SIZE 512 |
... | ... | @@ -3716,30 +3732,27 @@ static void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf) |
3716 | 3732 | |
3717 | 3733 | static uint16_t gen_opc_buf[OPC_BUF_SIZE]; |
3718 | 3734 | static uint32_t gen_opparam_buf[OPPARAM_BUF_SIZE]; |
3735 | +static uint32_t gen_opc_pc[OPC_BUF_SIZE]; | |
3736 | +static uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; | |
3719 | 3737 | |
3720 | -/* return non zero if the very first instruction is invalid so that | |
3721 | - the virtual CPU can trigger an exception. | |
3722 | - | |
3723 | - '*code_size_ptr' contains the target code size including the | |
3724 | - instruction which triggered an exception, except in case of invalid | |
3725 | - illegal opcode. It must never exceed one target page. | |
3726 | - | |
3727 | - '*gen_code_size_ptr' contains the size of the generated code (host | |
3728 | - code). | |
3729 | -*/ | |
3730 | -int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, | |
3731 | - int *gen_code_size_ptr, | |
3732 | - uint8_t *pc_start, uint8_t *cs_base, int flags, | |
3733 | - int *code_size_ptr, TranslationBlock *tb) | |
3738 | +/* generate intermediate code in gen_opc_buf and gen_opparam_buf for | |
3739 | + basic block 'tb'. If search_pc is TRUE, also generate PC | |
3740 | + information for each intermediate instruction. */ | |
3741 | +static inline int gen_intermediate_code(TranslationBlock *tb, int search_pc) | |
3734 | 3742 | { |
3735 | 3743 | DisasContext dc1, *dc = &dc1; |
3736 | 3744 | uint8_t *pc_ptr; |
3737 | 3745 | uint16_t *gen_opc_end; |
3738 | - int gen_code_size; | |
3746 | + int flags, j, lj; | |
3739 | 3747 | long ret; |
3748 | + uint8_t *pc_start; | |
3749 | + uint8_t *cs_base; | |
3740 | 3750 | |
3741 | 3751 | /* generate intermediate code */ |
3742 | - | |
3752 | + pc_start = (uint8_t *)tb->pc; | |
3753 | + cs_base = (uint8_t *)tb->cs_base; | |
3754 | + flags = tb->flags; | |
3755 | + | |
3743 | 3756 | dc->code32 = (flags >> GEN_FLAG_CODE32_SHIFT) & 1; |
3744 | 3757 | dc->ss32 = (flags >> GEN_FLAG_SS32_SHIFT) & 1; |
3745 | 3758 | dc->addseg = (flags >> GEN_FLAG_ADDSEG_SHIFT) & 1; |
... | ... | @@ -3758,7 +3771,18 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, |
3758 | 3771 | |
3759 | 3772 | dc->is_jmp = 0; |
3760 | 3773 | pc_ptr = pc_start; |
3774 | + lj = -1; | |
3761 | 3775 | do { |
3776 | + if (search_pc) { | |
3777 | + j = gen_opc_ptr - gen_opc_buf; | |
3778 | + if (lj < j) { | |
3779 | + lj++; | |
3780 | + while (lj < j) | |
3781 | + gen_opc_instr_start[lj++] = 0; | |
3782 | + gen_opc_pc[lj] = (uint32_t)pc_ptr; | |
3783 | + gen_opc_instr_start[lj] = 1; | |
3784 | + } | |
3785 | + } | |
3762 | 3786 | ret = disas_insn(dc, pc_ptr); |
3763 | 3787 | if (ret == -1) { |
3764 | 3788 | /* we trigger an illegal instruction operation only if it |
... | ... | @@ -3819,10 +3843,31 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, |
3819 | 3843 | fprintf(logfile, "\n"); |
3820 | 3844 | } |
3821 | 3845 | #endif |
3846 | + if (!search_pc) | |
3847 | + tb->size = pc_ptr - pc_start; | |
3848 | + return 0; | |
3849 | +} | |
3850 | + | |
3851 | + | |
3852 | +/* return non zero if the very first instruction is invalid so that | |
3853 | + the virtual CPU can trigger an exception. | |
3854 | + | |
3855 | + '*gen_code_size_ptr' contains the size of the generated code (host | |
3856 | + code). | |
3857 | +*/ | |
3858 | +int cpu_x86_gen_code(TranslationBlock *tb, | |
3859 | + int max_code_size, int *gen_code_size_ptr) | |
3860 | +{ | |
3861 | + uint8_t *gen_code_buf; | |
3862 | + int gen_code_size; | |
3863 | + | |
3864 | + if (gen_intermediate_code(tb, 0) < 0) | |
3865 | + return -1; | |
3822 | 3866 | |
3823 | 3867 | /* generate machine code */ |
3824 | 3868 | tb->tb_next_offset[0] = 0xffff; |
3825 | 3869 | tb->tb_next_offset[1] = 0xffff; |
3870 | + gen_code_buf = tb->tc_ptr; | |
3826 | 3871 | gen_code_size = dyngen_code(gen_code_buf, tb->tb_next_offset, |
3827 | 3872 | #ifdef USE_DIRECT_JUMP |
3828 | 3873 | tb->tb_jmp_offset, |
... | ... | @@ -3831,13 +3876,12 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, |
3831 | 3876 | #endif |
3832 | 3877 | gen_opc_buf, gen_opparam_buf); |
3833 | 3878 | flush_icache_range((unsigned long)gen_code_buf, (unsigned long)(gen_code_buf + gen_code_size)); |
3834 | - | |
3879 | + | |
3835 | 3880 | *gen_code_size_ptr = gen_code_size; |
3836 | - *code_size_ptr = pc_ptr - pc_start; | |
3837 | 3881 | #ifdef DEBUG_DISAS |
3838 | 3882 | if (loglevel) { |
3839 | 3883 | fprintf(logfile, "OUT: [size=%d]\n", *gen_code_size_ptr); |
3840 | - disas(logfile, gen_code_buf, *gen_code_size_ptr, DISAS_TARGET); | |
3884 | + disas(logfile, gen_code_buf, *gen_code_size_ptr, DISAS_TARGET); | |
3841 | 3885 | fprintf(logfile, "\n"); |
3842 | 3886 | fflush(logfile); |
3843 | 3887 | } |
... | ... | @@ -3845,6 +3889,50 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, |
3845 | 3889 | return 0; |
3846 | 3890 | } |
3847 | 3891 | |
3892 | +static const unsigned short opc_copy_size[] = { | |
3893 | +#define DEF(s, n, copy_size) copy_size, | |
3894 | +#include "opc-i386.h" | |
3895 | +#undef DEF | |
3896 | +}; | |
3897 | + | |
3898 | +/* The simulated PC corresponding to | |
3899 | + 'searched_pc' in the generated code is searched. 0 is returned if | |
3900 | + found. *found_pc contains the found PC. | |
3901 | + */ | |
3902 | +int cpu_x86_search_pc(TranslationBlock *tb, | |
3903 | + uint32_t *found_pc, unsigned long searched_pc) | |
3904 | +{ | |
3905 | + int j, c; | |
3906 | + unsigned long tc_ptr; | |
3907 | + uint16_t *opc_ptr; | |
3908 | + | |
3909 | + if (gen_intermediate_code(tb, 1) < 0) | |
3910 | + return -1; | |
3911 | + | |
3912 | + /* find opc index corresponding to search_pc */ | |
3913 | + tc_ptr = (unsigned long)tb->tc_ptr; | |
3914 | + if (searched_pc < tc_ptr) | |
3915 | + return -1; | |
3916 | + j = 0; | |
3917 | + opc_ptr = gen_opc_buf; | |
3918 | + for(;;) { | |
3919 | + c = *opc_ptr; | |
3920 | + if (c == INDEX_op_end) | |
3921 | + return -1; | |
3922 | + tc_ptr += opc_copy_size[c]; | |
3923 | + if (searched_pc < tc_ptr) | |
3924 | + break; | |
3925 | + opc_ptr++; | |
3926 | + } | |
3927 | + j = opc_ptr - gen_opc_buf; | |
3928 | + /* now find start of instruction before */ | |
3929 | + while (gen_opc_instr_start[j] == 0) | |
3930 | + j--; | |
3931 | + *found_pc = gen_opc_pc[j]; | |
3932 | + return 0; | |
3933 | +} | |
3934 | + | |
3935 | + | |
3848 | 3936 | CPUX86State *cpu_x86_init(void) |
3849 | 3937 | { |
3850 | 3938 | CPUX86State *env; | ... | ... |