From 1a9353d258aba69afd8a389bf5fb705caab12ce0 Mon Sep 17 00:00:00 2001 From: bellard Date: Sun, 16 Mar 2003 20:28:50 +0000 Subject: [PATCH] added loop/xadd/cmpxchg support git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@29 c046a42c-6fe2-441c-8c8c-71466251a162 --- linux-user/ioctls.h | 3 + linux-user/syscall.c | 45 ++++++++++- linux-user/syscall_defs.h | 10 +++ linux-user/syscall_types.h | 3 + op-i386.c | 19 +++++ ops_template.h | 67 ++++++++++++++++ syscall-i386.h | 22 ++--- tests/test-i386.c | 32 ++++++++ translate-i386.c | 191 +++++++++++++++++++++++++++++++++++--------- 9 files changed, 341 insertions(+), 51 deletions(-) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 49deb09..eeefcac 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -280,3 +280,6 @@ IOCTL(HDIO_SET_DMA, 0, TYPE_INT) IOCTL(HDIO_SET_32BIT, 0, TYPE_INT) IOCTL(HDIO_SET_PIO_MODE, 0, TYPE_INT) + + IOCTL(VFAT_IOCTL_READDIR_BOTH, IOC_R, MK_PTR(MK_ARRAY(MK_STRUCT(STRUCT_dirent), 2))) + IOCTL(VFAT_IOCTL_READDIR_SHORT, IOC_R, MK_PTR(MK_ARRAY(MK_STRUCT(STRUCT_dirent), 2))) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 9ed8daa..caaea1a 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -66,6 +66,10 @@ struct dirent { char d_name[256]; /* We must not include limits.h! */ }; +//#include +#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2]) +#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct dirent [2]) + #include "syscall_defs.h" #ifdef TARGET_I386 @@ -324,6 +328,40 @@ static long do_socketcall(int num, long *vptr) break; case SOCKOP_sendmsg: case SOCKOP_recvmsg: + { + int fd; + struct target_msghdr *msgp; + struct msghdr msg; + int flags, count, i; + struct iovec *vec; + struct target_iovec *target_vec; + + msgp = (void *)vptr[1]; + msg.msg_name = (void *)tswapl(msgp->msg_name); + msg.msg_namelen = tswapl(msgp->msg_namelen); + msg.msg_control = (void *)tswapl(msgp->msg_control); + msg.msg_controllen = tswapl(msgp->msg_controllen); + msg.msg_flags = tswap32(msgp->msg_flags); + + count = tswapl(msgp->msg_iovlen); + vec = alloca(count * sizeof(struct iovec)); + target_vec = (void *)tswapl(msgp->msg_iov); + for(i = 0;i < count; i++) { + vec[i].iov_base = (void *)tswapl(target_vec[i].iov_base); + vec[i].iov_len = tswapl(target_vec[i].iov_len); + } + msg.msg_iovlen = count; + msg.msg_iov = vec; + + fd = vptr[0]; + flags = vptr[2]; + if (num == SOCKOP_sendmsg) + ret = sendmsg(fd, &msg, flags); + else + ret = recvmsg(fd, &msg, flags); + ret = get_errno(ret); + } + break; case SOCKOP_setsockopt: case SOCKOP_getsockopt: default: @@ -356,7 +394,7 @@ typedef struct IOCTLEntry { int host_cmd; const char *name; int access; - const argtype arg_type[3]; + const argtype arg_type[5]; } IOCTLEntry; #define IOC_R 0x0001 @@ -962,12 +1000,11 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ret = get_errno(setsid()); break; case TARGET_NR_sigaction: -#if 0 +#if 1 { int signum = arg1; struct target_old_sigaction *tact = arg2, *toldact = arg3; - ret = get_errno(setsid()); - + ret = 0; } break; diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index e8357df..dc44272 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -29,6 +29,16 @@ struct target_iovec { target_long iov_len; /* Number of bytes */ }; +struct target_msghdr { + target_long msg_name; /* Socket name */ + int msg_namelen; /* Length of name */ + target_long msg_iov; /* Data blocks */ + target_long msg_iovlen; /* Number of blocks */ + target_long msg_control; /* Per protocol magic (eg BSD file descriptor passing) */ + target_long msg_controllen; /* Length of cmsg list */ + unsigned int msg_flags; +}; + struct target_rusage { struct target_timeval ru_utime; /* user time used */ struct target_timeval ru_stime; /* system time used */ diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index 5401144..e9ec148 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -61,3 +61,6 @@ STRUCT(cdrom_read_audio, STRUCT(hd_geometry, TYPE_CHAR, TYPE_CHAR, TYPE_SHORT, TYPE_ULONG) + +STRUCT(dirent, + TYPE_LONG, TYPE_LONG, TYPE_SHORT, MK_ARRAY(TYPE_CHAR, 256)) diff --git a/op-i386.c b/op-i386.c index 503fb88..f8188e3 100644 --- a/op-i386.c +++ b/op-i386.c @@ -2085,3 +2085,22 @@ void OPPROTO op_fldcw_A0(void) fesetround(rnd_type); } +void OPPROTO op_fclex(void) +{ + env->fpus &= 0x7f00; +} + +void OPPROTO op_fninit(void) +{ + env->fpus = 0; + env->fpstt = 0; + env->fpuc = 0x37f; + env->fptags[0] = 1; + env->fptags[1] = 1; + env->fptags[2] = 1; + env->fptags[3] = 1; + env->fptags[4] = 1; + env->fptags[5] = 1; + env->fptags[6] = 1; + env->fptags[7] = 1; +} diff --git a/ops_template.h b/ops_template.h index 745c27d..60bdbe5 100644 --- a/ops_template.h +++ b/ops_template.h @@ -277,6 +277,61 @@ void OPPROTO glue(op_jle_sub, SUFFIX)(void) FORCE_RET(); } +/* oldies */ + +#if DATA_BITS >= 16 + +void OPPROTO glue(op_loopnz, SUFFIX)(void) +{ + unsigned int tmp; + int eflags; + eflags = cc_table[CC_OP].compute_all(); + tmp = (ECX - 1) & DATA_MASK; + ECX = (ECX & ~DATA_MASK) | tmp; + if (tmp != 0 && !(eflags & CC_Z)) + PC = PARAM1; + else + PC = PARAM2; + FORCE_RET(); +} + +void OPPROTO glue(op_loopz, SUFFIX)(void) +{ + unsigned int tmp; + int eflags; + eflags = cc_table[CC_OP].compute_all(); + tmp = (ECX - 1) & DATA_MASK; + ECX = (ECX & ~DATA_MASK) | tmp; + if (tmp != 0 && (eflags & CC_Z)) + PC = PARAM1; + else + PC = PARAM2; + FORCE_RET(); +} + +void OPPROTO glue(op_loop, SUFFIX)(void) +{ + unsigned int tmp; + tmp = (ECX - 1) & DATA_MASK; + ECX = (ECX & ~DATA_MASK) | tmp; + if (tmp != 0) + PC = PARAM1; + else + PC = PARAM2; + FORCE_RET(); +} + +void OPPROTO glue(op_jecxz, SUFFIX)(void) +{ + if ((DATA_TYPE)ECX == 0) + PC = PARAM1; + else + PC = PARAM2; + FORCE_RET(); +} + +#endif + /* various optimized set cases */ void OPPROTO glue(op_setb_T0_sub, SUFFIX)(void) @@ -599,6 +654,18 @@ void OPPROTO glue(glue(op_sbb, SUFFIX), _T0_T1_cc)(void) CC_OP = CC_OP_SUBB + SHIFT + cf * 3; } +void OPPROTO glue(glue(op_cmpxchg, SUFFIX), _T0_T1_EAX_cc)(void) +{ + CC_SRC = EAX; + CC_DST = EAX - T0; + if ((DATA_TYPE)CC_DST == 0) { + T0 = T1; + } else { + EAX = (EAX & ~DATA_MASK) | (T0 & DATA_MASK); + } + FORCE_RET(); +} + /* bit operations */ #if DATA_BITS >= 16 diff --git a/syscall-i386.h b/syscall-i386.h index a54064a..fa84d9a 100644 --- a/syscall-i386.h +++ b/syscall-i386.h @@ -632,6 +632,16 @@ struct target_termios { #define TARGET_VLNEXT 15 #define TARGET_VEOL2 16 +#define TARGET_LDT_ENTRIES 8192 +#define TARGET_LDT_ENTRY_SIZE 8 + +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + target_ulong base_addr; + unsigned int limit; + unsigned int flags; +}; + /* soundcard defines (XXX: move them to generic file syscall_defs.h) */ #define TARGET_SNDCTL_COPR_HALT 0xc0144307 @@ -759,13 +769,5 @@ struct target_termios { #define TARGET_SOUND_MIXER_WRITE_LOUD 0xc0044d1f #define TARGET_SOUND_MIXER_WRITE_RECSRC 0xc0044dff -#define TARGET_LDT_ENTRIES 8192 -#define TARGET_LDT_ENTRY_SIZE 8 - -struct target_modify_ldt_ldt_s { - unsigned int entry_number; - target_ulong base_addr; - unsigned int limit; - unsigned int flags; -}; - +#define TARGET_VFAT_IOCTL_READDIR_BOTH 0x82187201 +#define TARGET_VFAT_IOCTL_READDIR_SHORT 0x82187202 diff --git a/tests/test-i386.c b/tests/test-i386.c index 95ec5d1..4d68ae2 100644 --- a/tests/test-i386.c +++ b/tests/test-i386.c @@ -689,6 +689,37 @@ void test_segs(void) printf("SS[tmp] = %02x\n", res2); } +#define TEST_XCHG(op, size, opconst)\ +{\ + int op0, op1;\ + op0 = 0x12345678;\ + op1 = 0xfbca7654;\ + asm(#op " %" size "0, %" size "1" \ + : "=q" (op0), opconst (op1) \ + : "0" (op0), "1" (op1));\ + printf("%-10s A=%08x B=%08x\n",\ + #op, op0, op1);\ +} + +void test_xchg(void) +{ + TEST_XCHG(xchgl, "", "=q"); + TEST_XCHG(xchgw, "w", "=q"); + TEST_XCHG(xchgb, "b", "=q"); + + TEST_XCHG(xchgl, "", "=m"); + TEST_XCHG(xchgw, "w", "=m"); + TEST_XCHG(xchgb, "b", "=m"); + + TEST_XCHG(xaddl, "", "=q"); + TEST_XCHG(xaddw, "w", "=q"); + TEST_XCHG(xaddb, "b", "=q"); + + TEST_XCHG(xaddl, "", "=m"); + TEST_XCHG(xaddw, "w", "=m"); + TEST_XCHG(xaddb, "b", "=m"); +} + static void *call_end __init_call = NULL; int main(int argc, char **argv) @@ -706,6 +737,7 @@ int main(int argc, char **argv) test_jcc(); test_floats(); test_bcd(); + test_xchg(); test_lea(); test_segs(); return 0; diff --git a/translate-i386.c b/translate-i386.c index 5146242..7737dc1 100644 --- a/translate-i386.c +++ b/translate-i386.c @@ -389,6 +389,12 @@ static const int cc_op_arithb[8] = { CC_OP_SUBB, }; +static GenOpFunc *gen_op_cmpxchg_T0_T1_EAX_cc[3] = { + gen_op_cmpxchgb_T0_T1_EAX_cc, + gen_op_cmpxchgw_T0_T1_EAX_cc, + gen_op_cmpxchgl_T0_T1_EAX_cc, +}; + static GenOpFunc *gen_op_shift_T0_T1_cc[3][8] = { [OT_BYTE] = { gen_op_rolb_T0_T1_cc, @@ -635,6 +641,20 @@ static GenOpFunc2 *gen_jcc_sub[3][8] = { gen_op_jle_subl, }, }; +static GenOpFunc2 *gen_op_loop[2][4] = { + [0] = { + gen_op_loopnzw, + gen_op_loopzw, + gen_op_loopw, + gen_op_jecxzw, + }, + [1] = { + gen_op_loopnzl, + gen_op_loopzl, + gen_op_loopl, + gen_op_jecxzl, + }, +}; static GenOpFunc *gen_setcc_slow[8] = { gen_op_seto_T0_cc, @@ -779,7 +799,6 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ int mod, rm, code, override, must_add_seg; /* XXX: add a generation time variable to tell if base == 0 in DS/ES/SS */ - /* XXX: fix lea case */ override = -1; must_add_seg = s->addseg; if (s->prefix & (PREFIX_CS | PREFIX_SS | PREFIX_DS | @@ -1405,8 +1424,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) } break; default: - error("GRP3: bad instruction"); - return -1; + goto illegal_op; } break; @@ -1422,8 +1440,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) rm = modrm & 7; op = (modrm >> 3) & 7; if (op >= 2 && b == 0xfe) { - error("GRP4: bad instruction"); - return -1; + goto illegal_op; } if (mod != 3) { gen_lea_modrm(s, modrm, ®_addr, &offset_addr); @@ -1461,8 +1478,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_op_pushl_T0(); break; default: - error("GRP5: bad instruction"); - return -1; + goto illegal_op; } break; @@ -1535,6 +1551,55 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_op_mov_reg_T0[ot][reg](); s->cc_op = CC_OP_MUL; break; + case 0x1c0: + case 0x1c1: /* xadd Ev, Gv */ + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + modrm = ldub(s->pc++); + reg = (modrm >> 3) & 7; + mod = (modrm >> 6) & 3; + if (mod == 3) { + rm = modrm & 7; + gen_op_mov_TN_reg[ot][0][reg](); + gen_op_mov_TN_reg[ot][1][rm](); + gen_op_addl_T0_T1_cc(); + gen_op_mov_reg_T0[ot][rm](); + gen_op_mov_reg_T1[ot][reg](); + } else { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_mov_TN_reg[ot][0][reg](); + gen_op_ld_T1_A0[ot](); + gen_op_addl_T0_T1_cc(); + gen_op_st_T0_A0[ot](); + gen_op_mov_reg_T1[ot][reg](); + } + s->cc_op = CC_OP_ADDB + ot; + break; + case 0x1b0: + case 0x1b1: /* cmpxchg Ev, Gv */ + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + modrm = ldub(s->pc++); + reg = (modrm >> 3) & 7; + mod = (modrm >> 6) & 3; + gen_op_mov_TN_reg[ot][1][reg](); + if (mod == 3) { + rm = modrm & 7; + gen_op_mov_TN_reg[ot][0][rm](); + gen_op_cmpxchg_T0_T1_EAX_cc[ot](); + gen_op_mov_reg_T0[ot][rm](); + } else { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_ld_T0_A0[ot](); + gen_op_cmpxchg_T0_T1_EAX_cc[ot](); + gen_op_st_T0_A0[ot](); + } + s->cc_op = CC_OP_SUBB + ot; + break; /**************************/ /* push/pop */ @@ -1748,6 +1813,32 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) else offset_addr = insn_get(s, OT_WORD); gen_op_movl_A0_im(offset_addr); + /* handle override */ + /* XXX: factorize that */ + { + int override, must_add_seg; + override = R_DS; + must_add_seg = s->addseg; + if (s->prefix & (PREFIX_CS | PREFIX_SS | PREFIX_DS | + PREFIX_ES | PREFIX_FS | PREFIX_GS)) { + if (s->prefix & PREFIX_ES) + override = R_ES; + else if (s->prefix & PREFIX_CS) + override = R_CS; + else if (s->prefix & PREFIX_SS) + override = R_SS; + else if (s->prefix & PREFIX_DS) + override = R_DS; + else if (s->prefix & PREFIX_FS) + override = R_FS; + else + override = R_GS; + must_add_seg = 1; + } + if (must_add_seg) { + gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[override].base)); + } + } if ((b & 2) == 0) { gen_op_ld_T0_A0[ot](); gen_op_mov_reg_T0[ot][R_EAX](); @@ -1773,11 +1864,8 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) case 0x91 ... 0x97: /* xchg R, EAX */ ot = dflag ? OT_LONG : OT_WORD; reg = b & 7; - gen_op_mov_TN_reg[ot][0][reg](); - gen_op_mov_TN_reg[ot][1][R_EAX](); - gen_op_mov_reg_T0[ot][R_EAX](); - gen_op_mov_reg_T1[ot][reg](); - break; + rm = R_EAX; + goto do_xchg_reg; case 0x86: case 0x87: /* xchg Ev, Gv */ if ((b & 1) == 0) @@ -1786,12 +1874,21 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); reg = (modrm >> 3) & 7; - - gen_lea_modrm(s, modrm, ®_addr, &offset_addr); - gen_op_mov_TN_reg[ot][0][reg](); - gen_op_ld_T1_A0[ot](); - gen_op_st_T0_A0[ot](); - gen_op_mov_reg_T1[ot][reg](); + mod = (modrm >> 6) & 3; + if (mod == 3) { + rm = modrm & 7; + do_xchg_reg: + gen_op_mov_TN_reg[ot][0][reg](); + gen_op_mov_TN_reg[ot][1][rm](); + gen_op_mov_reg_T0[ot][rm](); + gen_op_mov_reg_T1[ot][reg](); + } else { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_mov_TN_reg[ot][0][reg](); + gen_op_ld_T1_A0[ot](); + gen_op_st_T0_A0[ot](); + gen_op_mov_reg_T1[ot][reg](); + } break; case 0xc4: /* les Gv */ op = R_ES; @@ -2058,8 +2155,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_op_fpop(); break; default: - error("unhandled FPm [op=0x%02x]\n", op); - return -1; + goto illegal_op; } } else { /* register float ops */ @@ -2078,8 +2174,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) case 0: /* fnop */ break; default: - error("unhandled FP GRP d9/2\n"); - return -1; + goto illegal_op; } break; case 0x0c: /* grp d9/4 */ @@ -2098,7 +2193,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_op_fxam_ST0(); break; default: - return -1; + goto illegal_op; } break; case 0x0d: /* grp d9/5 */ @@ -2133,7 +2228,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_op_fldz_ST0(); break; default: - return -1; + goto illegal_op; } } break; @@ -2230,7 +2325,19 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_op_fpop(); break; default: - return -1; + goto illegal_op; + } + break; + case 0x1c: + switch(rm) { + case 2: /* fclex */ + gen_op_fclex(); + break; + case 3: /* fninit */ + gen_op_fninit(); + break; + default: + goto illegal_op; } break; case 0x2a: /* fst sti */ @@ -2258,7 +2365,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_op_fpop(); break; default: - return -1; + goto illegal_op; } break; case 0x3c: /* df/4 */ @@ -2267,13 +2374,11 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_op_fnstsw_EAX(); break; default: - error("unhandled FP %x df/4\n", rm); - return -1; + goto illegal_op; } break; default: - error("unhandled FPr [op=0x%x]\n", op); - return -1; + goto illegal_op; } } break; @@ -2556,7 +2661,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) val = ldub(s->pc++); gen_op_movl_T1_im(val); if (op < 4) - return -1; + goto illegal_op; op -= 4; gen_op_btx_T0_T1_cc[ot - OT_WORD][op](); s->cc_op = CC_OP_SARB + ot; @@ -2684,6 +2789,18 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_op_set_cc_op(s->cc_op); gen_op_salc(); break; + case 0xe0: /* loopnz */ + case 0xe1: /* loopz */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + /* FALL THRU */ + case 0xe2: /* loop */ + case 0xe3: /* jecxz */ + val = (int8_t)insn_get(s, OT_BYTE); + val += (long)s->pc; + gen_op_loop[s->aflag][b & 3](val, (long)s->pc); + s->is_jmp = 1; + break; case 0x1a2: /* rdtsc */ gen_op_rdtsc(); break; @@ -2693,12 +2810,10 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) break; #endif default: - error("unknown opcode 0x%x", b); - return -1; + goto illegal_op; } return (long)s->pc; illegal_op: - error("illegal opcode pc=0x%08Lx", (long)pc_start); return -1; } @@ -2725,9 +2840,11 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, pc_ptr = pc_start; do { ret = disas_insn(dc, pc_ptr); - if (ret == -1) - error("unknown instruction at PC=0x%x B=%02x %02x", - pc_ptr, pc_ptr[0], pc_ptr[1]); + if (ret == -1) { + error("unknown instruction at PC=0x%x B=%02x %02x %02x", + pc_ptr, pc_ptr[0], pc_ptr[1], pc_ptr[2]); + abort(); + } pc_ptr = (void *)ret; } while (!dc->is_jmp && gen_code_ptr < gen_code_end); /* we must store the eflags state if it is not already done */ -- 1.7.9.5