//#define DEBUG_PCALL
+#if 0
+#define raise_exception_err(a, b)\
+do {\
+ fprintf(logfile, "raise_exception line=%d\n", __LINE__);\
+ (raise_exception_err)(a, b);\
+} while (0)
+#endif
+
const uint8_t parity_table[256] = {
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
{
/* NOTE: the register at this point must be saved by hand because
longjmp restore them */
-#ifdef reg_EAX
- env->regs[R_EAX] = EAX;
-#endif
-#ifdef reg_ECX
- env->regs[R_ECX] = ECX;
-#endif
-#ifdef reg_EDX
- env->regs[R_EDX] = EDX;
-#endif
-#ifdef reg_EBX
- env->regs[R_EBX] = EBX;
-#endif
-#ifdef reg_ESP
- env->regs[R_ESP] = ESP;
-#endif
-#ifdef reg_EBP
- env->regs[R_EBP] = EBP;
-#endif
-#ifdef reg_ESI
- env->regs[R_ESI] = ESI;
-#endif
-#ifdef reg_EDI
- env->regs[R_EDI] = EDI;
-#endif
+ regs_to_env();
longjmp(env->jmp_env, 1);
}
{
SegmentCache *dt;
int index;
- uint8_t *ptr;
+ target_ulong ptr;
if (selector & 0x4)
dt = &env->ldt;
return limit;
}
-static inline uint8_t *get_seg_base(uint32_t e1, uint32_t e2)
+static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2)
{
- return (uint8_t *)((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
+ return ((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
}
static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2)
{
selector &= 0xffff;
cpu_x86_load_seg_cache(env, seg, selector,
- (uint8_t *)(selector << 4), 0xffff, 0);
+ (selector << 4), 0xffff, 0);
}
static inline void get_ss_esp_from_tss(uint32_t *ss_ptr,
/* XXX: restore CPU state in registers (PowerPC case) */
static void switch_tss(int tss_selector,
- uint32_t e1, uint32_t e2, int source)
+ uint32_t e1, uint32_t e2, int source,
+ uint32_t next_eip)
{
int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i;
- uint8_t *tss_base;
+ target_ulong tss_base;
uint32_t new_regs[8], new_segs[6];
uint32_t new_eflags, new_eip, new_cr3, new_ldt, new_trap;
uint32_t old_eflags, eflags_mask;
SegmentCache *dt;
int index;
- uint8_t *ptr;
+ target_ulong ptr;
type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
#ifdef DEBUG_PCALL
- if (loglevel)
+ if (loglevel & CPU_LOG_PCALL)
fprintf(logfile, "switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, source);
#endif
/* clear busy bit (it is restartable) */
if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) {
- uint8_t *ptr;
+ target_ulong ptr;
uint32_t e2;
- ptr = env->gdt.base + (env->tr.selector << 3);
+ ptr = env->gdt.base + (env->tr.selector & ~7);
e2 = ldl_kernel(ptr + 4);
e2 &= ~DESC_TSS_BUSY_MASK;
stl_kernel(ptr + 4, e2);
/* save the current state in the old TSS */
if (type & 8) {
/* 32 bit */
- stl_kernel(env->tr.base + 0x20, env->eip);
+ stl_kernel(env->tr.base + 0x20, next_eip);
stl_kernel(env->tr.base + 0x24, old_eflags);
- for(i = 0; i < 8; i++)
- stl_kernel(env->tr.base + (0x28 + i * 4), env->regs[i]);
+ stl_kernel(env->tr.base + (0x28 + 0 * 4), EAX);
+ stl_kernel(env->tr.base + (0x28 + 1 * 4), ECX);
+ stl_kernel(env->tr.base + (0x28 + 2 * 4), EDX);
+ stl_kernel(env->tr.base + (0x28 + 3 * 4), EBX);
+ stl_kernel(env->tr.base + (0x28 + 4 * 4), ESP);
+ stl_kernel(env->tr.base + (0x28 + 5 * 4), EBP);
+ stl_kernel(env->tr.base + (0x28 + 6 * 4), ESI);
+ stl_kernel(env->tr.base + (0x28 + 7 * 4), EDI);
for(i = 0; i < 6; i++)
stw_kernel(env->tr.base + (0x48 + i * 4), env->segs[i].selector);
} else {
/* 16 bit */
- stw_kernel(env->tr.base + 0x0e, new_eip);
+ stw_kernel(env->tr.base + 0x0e, next_eip);
stw_kernel(env->tr.base + 0x10, old_eflags);
- for(i = 0; i < 8; i++)
- stw_kernel(env->tr.base + (0x12 + i * 2), env->regs[i]);
+ stw_kernel(env->tr.base + (0x12 + 0 * 2), EAX);
+ stw_kernel(env->tr.base + (0x12 + 1 * 2), ECX);
+ stw_kernel(env->tr.base + (0x12 + 2 * 2), EDX);
+ stw_kernel(env->tr.base + (0x12 + 3 * 2), EBX);
+ stw_kernel(env->tr.base + (0x12 + 4 * 2), ESP);
+ stw_kernel(env->tr.base + (0x12 + 5 * 2), EBP);
+ stw_kernel(env->tr.base + (0x12 + 6 * 2), ESI);
+ stw_kernel(env->tr.base + (0x12 + 7 * 2), EDI);
for(i = 0; i < 4; i++)
stw_kernel(env->tr.base + (0x22 + i * 4), env->segs[i].selector);
}
/* set busy bit */
if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) {
- uint8_t *ptr;
+ target_ulong ptr;
uint32_t e2;
- ptr = env->gdt.base + (tss_selector << 3);
+ ptr = env->gdt.base + (tss_selector & ~7);
e2 = ldl_kernel(ptr + 4);
e2 |= DESC_TSS_BUSY_MASK;
stl_kernel(ptr + 4, e2);
/* set the new CPU state */
/* from this point, any exception which occurs can give problems */
env->cr[0] |= CR0_TS_MASK;
+ env->hflags |= HF_TS_MASK;
env->tr.selector = tss_selector;
env->tr.base = tss_base;
env->tr.limit = tss_limit;
env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK;
if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) {
- env->cr[3] = new_cr3;
- cpu_x86_update_cr3(env);
+ cpu_x86_update_cr3(env, new_cr3);
}
/* load all registers without an exception, then reload them with
possible exception */
env->eip = new_eip;
eflags_mask = TF_MASK | AC_MASK | ID_MASK |
- IF_MASK | IOPL_MASK | VM_MASK | RF_MASK;
+ IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK;
if (!(type & 8))
eflags_mask &= 0xffff;
load_eflags(new_eflags, eflags_mask);
- for(i = 0; i < 8; i++)
- env->regs[i] = new_regs[i];
+ /* XXX: what to do in 16 bit case ? */
+ EAX = new_regs[0];
+ ECX = new_regs[1];
+ EDX = new_regs[2];
+ EBX = new_regs[3];
+ ESP = new_regs[4];
+ EBP = new_regs[5];
+ ESI = new_regs[6];
+ EDI = new_regs[7];
if (new_eflags & VM_MASK) {
for(i = 0; i < 6; i++)
load_seg_vm(i, new_segs[i]);
cpu_x86_set_cpl(env, new_segs[R_CS] & 3);
/* first just selectors as the rest may trigger exceptions */
for(i = 0; i < 6; i++)
- cpu_x86_load_seg_cache(env, i, new_segs[i], NULL, 0, 0);
+ cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0);
}
env->ldt.selector = new_ldt & ~4;
- env->ldt.base = NULL;
+ env->ldt.base = 0;
env->ldt.limit = 0;
env->ldt.flags = 0;
if (new_ldt & 4)
raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
- dt = &env->gdt;
- index = new_ldt & ~7;
- if ((index + 7) > dt->limit)
- raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
- ptr = dt->base + index;
- e1 = ldl_kernel(ptr);
- e2 = ldl_kernel(ptr + 4);
- if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
- raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
- if (!(e2 & DESC_P_MASK))
- raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
- load_seg_cache_raw_dt(&env->ldt, e1, e2);
+ if ((new_ldt & 0xfffc) != 0) {
+ dt = &env->gdt;
+ index = new_ldt & ~7;
+ if ((index + 7) > dt->limit)
+ raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
+ ptr = dt->base + index;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+ if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
+ raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
+ load_seg_cache_raw_dt(&env->ldt, e1, e2);
+ }
/* load the segments */
if (!(new_eflags & VM_MASK)) {
/* check that EIP is in the CS segment limits */
if (new_eip > env->segs[R_CS].limit) {
+ /* XXX: different exception if CALL ? */
raise_exception_err(EXCP0D_GPF, 0);
}
}
#define POPL(ssp, sp, sp_mask, val)\
{\
- val = ldl_kernel((ssp) + (sp & (sp_mask)));\
+ val = (uint32_t)ldl_kernel((ssp) + (sp & (sp_mask)));\
sp += 4;\
}
unsigned int next_eip, int is_hw)
{
SegmentCache *dt;
- uint8_t *ptr, *ssp;
+ target_ulong ptr, ssp;
int type, dpl, selector, ss_dpl, cpl, sp_mask;
int has_error_code, new_stack, shift;
uint32_t e1, e2, offset, ss, esp, ss_e1, ss_e2;
break;
}
}
+ if (is_int)
+ old_eip = next_eip;
+ else
+ old_eip = env->eip;
dt = &env->idt;
if (intno * 8 + 7 > dt->limit)
/* must do that check here to return the correct error code */
if (!(e2 & DESC_P_MASK))
raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
- switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL);
+ switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip);
if (has_error_code) {
- int mask;
+ int mask, type;
/* push the error code */
- shift = (env->segs[R_CS].flags >> DESC_B_SHIFT) & 1;
+ type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
+ shift = type >> 3;
if (env->segs[R_SS].flags & DESC_B_MASK)
mask = 0xffffffff;
else
mask = 0xffff;
- esp = (env->regs[R_ESP] - (2 << shift)) & mask;
+ esp = (ESP - (2 << shift)) & mask;
ssp = env->segs[R_SS].base + esp;
if (shift)
stl_kernel(ssp, error_code);
else
stw_kernel(ssp, error_code);
- env->regs[R_ESP] = (esp & mask) | (env->regs[R_ESP] & ~mask);
+ ESP = (esp & mask) | (ESP & ~mask);
}
return;
case 6: /* 286 interrupt gate */
sp_mask = get_sp_mask(env->segs[R_SS].flags);
ssp = env->segs[R_SS].base;
esp = ESP;
+ dpl = cpl;
} else {
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
new_stack = 0; /* avoid warning */
sp_mask = 0; /* avoid warning */
- ssp = NULL; /* avoid warning */
+ ssp = 0; /* avoid warning */
esp = 0; /* avoid warning */
}
push_size += 8;
push_size <<= shift;
#endif
- if (is_int)
- old_eip = next_eip;
- else
- old_eip = env->eip;
if (shift == 1) {
if (new_stack) {
if (env->eflags & VM_MASK) {
if (new_stack) {
if (env->eflags & VM_MASK) {
- /* XXX: explain me why W2K hangs if the whole segment cache is
- reset ? */
- env->segs[R_ES].selector = 0;
- env->segs[R_ES].flags = 0;
- env->segs[R_DS].selector = 0;
- env->segs[R_DS].flags = 0;
- env->segs[R_FS].selector = 0;
- env->segs[R_FS].flags = 0;
- env->segs[R_GS].selector = 0;
- env->segs[R_GS].flags = 0;
+ cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0);
+ cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0);
+ cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0);
+ cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0);
}
ss = (ss & ~3) | dpl;
cpu_x86_load_seg_cache(env, R_SS, ss,
env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
}
+#ifdef TARGET_X86_64
+
+#define PUSHQ(sp, val)\
+{\
+ sp -= 8;\
+ stq_kernel(sp, (val));\
+}
+
+#define POPQ(sp, val)\
+{\
+ val = ldq_kernel(sp);\
+ sp += 8;\
+}
+
+static inline target_ulong get_rsp_from_tss(int level)
+{
+ int index;
+
+#if 0
+ printf("TR: base=" TARGET_FMT_lx " limit=%x\n",
+ env->tr.base, env->tr.limit);
+#endif
+
+ if (!(env->tr.flags & DESC_P_MASK))
+ cpu_abort(env, "invalid tss");
+ index = 8 * level + 4;
+ if ((index + 7) > env->tr.limit)
+ raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
+ return ldq_kernel(env->tr.base + index);
+}
+
+/* 64 bit interrupt */
+static void do_interrupt64(int intno, int is_int, int error_code,
+ target_ulong next_eip, int is_hw)
+{
+ SegmentCache *dt;
+ target_ulong ptr;
+ int type, dpl, selector, cpl, ist;
+ int has_error_code, new_stack;
+ uint32_t e1, e2, e3, ss;
+ target_ulong old_eip, esp, offset;
+
+ has_error_code = 0;
+ if (!is_int && !is_hw) {
+ switch(intno) {
+ case 8:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 17:
+ has_error_code = 1;
+ break;
+ }
+ }
+ if (is_int)
+ old_eip = next_eip;
+ else
+ old_eip = env->eip;
+
+ dt = &env->idt;
+ if (intno * 16 + 15 > dt->limit)
+ raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
+ ptr = dt->base + intno * 16;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+ e3 = ldl_kernel(ptr + 8);
+ /* check gate type */
+ type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
+ switch(type) {
+ case 14: /* 386 interrupt gate */
+ case 15: /* 386 trap gate */
+ break;
+ default:
+ raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
+ break;
+ }
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ /* check privledge if software int */
+ if (is_int && dpl < cpl)
+ raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
+ /* check valid bit */
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, intno * 16 + 2);
+ selector = e1 >> 16;
+ offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff);
+ ist = e2 & 7;
+ if ((selector & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, 0);
+
+ if (load_segment(&e1, &e2, selector) != 0)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (dpl > cpl)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+ if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if ((!(e2 & DESC_C_MASK) && dpl < cpl) || ist != 0) {
+ /* to inner priviledge */
+ if (ist != 0)
+ esp = get_rsp_from_tss(ist + 3);
+ else
+ esp = get_rsp_from_tss(dpl);
+ ss = 0;
+ new_stack = 1;
+ } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
+ /* to same priviledge */
+ if (env->eflags & VM_MASK)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ new_stack = 0;
+ esp = ESP & ~0xf; /* align stack */
+ dpl = cpl;
+ } else {
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ new_stack = 0; /* avoid warning */
+ esp = 0; /* avoid warning */
+ }
+
+ PUSHQ(esp, env->segs[R_SS].selector);
+ PUSHQ(esp, ESP);
+ PUSHQ(esp, compute_eflags());
+ PUSHQ(esp, env->segs[R_CS].selector);
+ PUSHQ(esp, old_eip);
+ if (has_error_code) {
+ PUSHQ(esp, error_code);
+ }
+
+ if (new_stack) {
+ ss = 0 | dpl;
+ cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0);
+ }
+ ESP = esp;
+
+ selector = (selector & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_CS, selector,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ cpu_x86_set_cpl(env, dpl);
+ env->eip = offset;
+
+ /* interrupt gate clear IF mask */
+ if ((type & 1) == 0) {
+ env->eflags &= ~IF_MASK;
+ }
+ env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
+}
+#endif
+
+void helper_syscall(int next_eip_addend)
+{
+ int selector;
+
+ if (!(env->efer & MSR_EFER_SCE)) {
+ raise_exception_err(EXCP06_ILLOP, 0);
+ }
+ selector = (env->star >> 32) & 0xffff;
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ ECX = env->eip + next_eip_addend;
+ env->regs[11] = compute_eflags();
+
+ cpu_x86_set_cpl(env, 0);
+ cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_W_MASK | DESC_A_MASK);
+ env->eflags &= ~env->fmask;
+ if (env->hflags & HF_CS64_MASK)
+ env->eip = env->lstar;
+ else
+ env->eip = env->cstar;
+ } else
+#endif
+ {
+ ECX = (uint32_t)(env->eip + next_eip_addend);
+
+ cpu_x86_set_cpl(env, 0);
+ cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_W_MASK | DESC_A_MASK);
+ env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
+ env->eip = (uint32_t)env->star;
+ }
+}
+
+void helper_sysret(int dflag)
+{
+ int cpl, selector;
+
+ if (!(env->efer & MSR_EFER_SCE)) {
+ raise_exception_err(EXCP06_ILLOP, 0);
+ }
+ cpl = env->hflags & HF_CPL_MASK;
+ if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) {
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+ selector = (env->star >> 48) & 0xffff;
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ if (dflag == 2) {
+ cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
+ DESC_L_MASK);
+ env->eip = ECX;
+ } else {
+ cpu_x86_load_seg_cache(env, R_CS, selector | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ env->eip = (uint32_t)ECX;
+ }
+ cpu_x86_load_seg_cache(env, R_SS, selector + 8,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ load_eflags((uint32_t)(env->regs[11]), TF_MASK | AC_MASK | ID_MASK |
+ IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK);
+ cpu_x86_set_cpl(env, 3);
+ } else
+#endif
+ {
+ cpu_x86_load_seg_cache(env, R_CS, selector | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ env->eip = (uint32_t)ECX;
+ cpu_x86_load_seg_cache(env, R_SS, selector + 8,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ env->eflags |= IF_MASK;
+ cpu_x86_set_cpl(env, 3);
+ }
+#ifdef USE_KQEMU
+ if (kqemu_is_ok(env)) {
+ if (env->hflags & HF_LMA_MASK)
+ CC_OP = CC_OP_EFLAGS;
+ env->exception_index = -1;
+ cpu_loop_exit();
+ }
+#endif
+}
+
/* real mode interrupt */
static void do_interrupt_real(int intno, int is_int, int error_code,
unsigned int next_eip)
{
SegmentCache *dt;
- uint8_t *ptr, *ssp;
+ target_ulong ptr, ssp;
int selector;
uint32_t offset, esp;
uint32_t old_cs, old_eip;
ESP = (ESP & ~0xffff) | (esp & 0xffff);
env->eip = offset;
env->segs[R_CS].selector = selector;
- env->segs[R_CS].base = (uint8_t *)(selector << 4);
+ env->segs[R_CS].base = (selector << 4);
env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK);
}
/* fake user mode interrupt */
void do_interrupt_user(int intno, int is_int, int error_code,
- unsigned int next_eip)
+ target_ulong next_eip)
{
SegmentCache *dt;
- uint8_t *ptr;
+ target_ulong ptr;
int dpl, cpl;
uint32_t e2;
}
/*
- * Begin excution of an interruption. is_int is TRUE if coming from
+ * Begin execution of an interruption. is_int is TRUE if coming from
* the int instruction. next_eip is the EIP value AFTER the interrupt
* instruction. It is only relevant if is_int is TRUE.
*/
void do_interrupt(int intno, int is_int, int error_code,
- unsigned int next_eip, int is_hw)
+ target_ulong next_eip, int is_hw)
{
-#if 0
- {
- extern FILE *stdout;
- static int count;
- if ((env->cr[0] && CR0_PE_MASK)) {
- fprintf(stdout, "%d: interrupt: vector=%02x error_code=%04x int=%d CPL=%d CS:EIP=%04x:%08x SS:ESP=%04x:%08x EAX=%08x\n",
+#ifdef DEBUG_PCALL
+ if (loglevel & (CPU_LOG_PCALL | CPU_LOG_INT)) {
+ if ((env->cr[0] & CR0_PE_MASK)) {
+ static int count;
+ fprintf(logfile, "%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:" TARGET_FMT_lx " pc=" TARGET_FMT_lx " SP=%04x:" TARGET_FMT_lx,
count, intno, error_code, is_int,
env->hflags & HF_CPL_MASK,
env->segs[R_CS].selector, EIP,
- env->segs[R_SS].selector, ESP,
- EAX);
- if (0) {
- cpu_x86_dump_state(env, stdout, X86_DUMP_CCOP);
+ (int)env->segs[R_CS].base + EIP,
+ env->segs[R_SS].selector, ESP);
+ if (intno == 0x0e) {
+ fprintf(logfile, " CR2=" TARGET_FMT_lx, env->cr[2]);
+ } else {
+ fprintf(logfile, " EAX=" TARGET_FMT_lx, EAX);
+ }
+ fprintf(logfile, "\n");
#if 0
- {
- int i;
- uint8_t *ptr;
- fprintf(stdout, " code=");
- ptr = env->segs[R_CS].base + env->eip;
- for(i = 0; i < 16; i++) {
- fprintf(stdout, " %02x", ldub(ptr + i));
- }
- fprintf(stdout, "\n");
+ cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP);
+ {
+ int i;
+ uint8_t *ptr;
+ fprintf(logfile, " code=");
+ ptr = env->segs[R_CS].base + env->eip;
+ for(i = 0; i < 16; i++) {
+ fprintf(logfile, " %02x", ldub(ptr + i));
}
-#endif
+ fprintf(logfile, "\n");
}
+#endif
count++;
}
}
#endif
-
-#ifdef DEBUG_PCALL
- if (loglevel) {
- static int count;
- fprintf(logfile, "%d: interrupt: vector=%02x error_code=%04x int=%d\n",
- count, intno, error_code, is_int);
- cpu_x86_dump_state(env, logfile, X86_DUMP_CCOP);
-#if 0
+ if (env->cr[0] & CR0_PE_MASK) {
+#if TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ do_interrupt64(intno, is_int, error_code, next_eip, is_hw);
+ } else
+#endif
{
- int i;
- uint8_t *ptr;
- fprintf(logfile, " code=");
- ptr = env->segs[R_CS].base + env->eip;
- for(i = 0; i < 16; i++) {
- fprintf(logfile, " %02x", ldub(ptr + i));
- }
- fprintf(logfile, "\n");
+ do_interrupt_protected(intno, is_int, error_code, next_eip, is_hw);
}
-#endif
- count++;
- }
-#endif
- if (env->cr[0] & CR0_PE_MASK) {
- do_interrupt_protected(intno, is_int, error_code, next_eip, is_hw);
} else {
do_interrupt_real(intno, is_int, error_code, next_eip);
}
* is_int is TRUE.
*/
void raise_interrupt(int intno, int is_int, int error_code,
- unsigned int next_eip)
+ int next_eip_addend)
{
env->exception_index = intno;
env->error_code = error_code;
env->exception_is_int = is_int;
- env->exception_next_eip = next_eip;
+ env->exception_next_eip = env->eip + next_eip_addend;
cpu_loop_exit();
}
+/* same as raise_exception_err, but do not restore global registers */
+static void raise_exception_err_norestore(int exception_index, int error_code)
+{
+ env->exception_index = exception_index;
+ env->error_code = error_code;
+ env->exception_is_int = 0;
+ env->exception_next_eip = 0;
+ longjmp(env->jmp_env, 1);
+}
+
/* shortcuts to generate exceptions */
-void raise_exception_err(int exception_index, int error_code)
+
+void (raise_exception_err)(int exception_index, int error_code)
{
raise_interrupt(exception_index, 0, error_code, 0);
}
#ifdef BUGGY_GCC_DIV64
/* gcc 2.95.4 on PowerPC does not seem to like using __udivdi3, so we
call it from another function */
-uint32_t div64(uint32_t *q_ptr, uint64_t num, uint32_t den)
+uint32_t div32(uint64_t *q_ptr, uint64_t num, uint32_t den)
{
*q_ptr = num / den;
return num % den;
}
-int32_t idiv64(int32_t *q_ptr, int64_t num, int32_t den)
+int32_t idiv32(int64_t *q_ptr, int64_t num, int32_t den)
{
*q_ptr = num / den;
return num % den;
}
#endif
-void helper_divl_EAX_T0(uint32_t eip)
+void helper_divl_EAX_T0(void)
{
- unsigned int den, q, r;
- uint64_t num;
+ unsigned int den, r;
+ uint64_t num, q;
- num = EAX | ((uint64_t)EDX << 32);
+ num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
den = T0;
if (den == 0) {
- EIP = eip;
raise_exception(EXCP00_DIVZ);
}
#ifdef BUGGY_GCC_DIV64
- r = div64(&q, num, den);
+ r = div32(&q, num, den);
#else
q = (num / den);
r = (num % den);
#endif
- EAX = q;
- EDX = r;
+ if (q > 0xffffffff)
+ raise_exception(EXCP00_DIVZ);
+ EAX = (uint32_t)q;
+ EDX = (uint32_t)r;
}
-void helper_idivl_EAX_T0(uint32_t eip)
+void helper_idivl_EAX_T0(void)
{
- int den, q, r;
- int64_t num;
+ int den, r;
+ int64_t num, q;
- num = EAX | ((uint64_t)EDX << 32);
+ num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
den = T0;
if (den == 0) {
- EIP = eip;
raise_exception(EXCP00_DIVZ);
}
#ifdef BUGGY_GCC_DIV64
- r = idiv64(&q, num, den);
+ r = idiv32(&q, num, den);
#else
q = (num / den);
r = (num % den);
#endif
- EAX = q;
- EDX = r;
+ if (q != (int32_t)q)
+ raise_exception(EXCP00_DIVZ);
+ EAX = (uint32_t)q;
+ EDX = (uint32_t)r;
}
void helper_cmpxchg8b(void)
int eflags;
eflags = cc_table[CC_OP].compute_all();
- d = ldq((uint8_t *)A0);
+ d = ldq(A0);
if (d == (((uint64_t)EDX << 32) | EAX)) {
- stq((uint8_t *)A0, ((uint64_t)ECX << 32) | EBX);
+ stq(A0, ((uint64_t)ECX << 32) | EBX);
eflags |= CC_Z;
} else {
EDX = d >> 32;
CC_SRC = eflags;
}
-#define CPUID_FP87 (1 << 0)
-#define CPUID_VME (1 << 1)
-#define CPUID_DE (1 << 2)
-#define CPUID_PSE (1 << 3)
-#define CPUID_TSC (1 << 4)
-#define CPUID_MSR (1 << 5)
-#define CPUID_PAE (1 << 6)
-#define CPUID_MCE (1 << 7)
-#define CPUID_CX8 (1 << 8)
-#define CPUID_APIC (1 << 9)
-#define CPUID_SEP (1 << 11) /* sysenter/sysexit */
-#define CPUID_MTRR (1 << 12)
-#define CPUID_PGE (1 << 13)
-#define CPUID_MCA (1 << 14)
-#define CPUID_CMOV (1 << 15)
-/* ... */
-#define CPUID_MMX (1 << 23)
-#define CPUID_FXSR (1 << 24)
-#define CPUID_SSE (1 << 25)
-#define CPUID_SSE2 (1 << 26)
-
void helper_cpuid(void)
{
- switch(EAX) {
+ uint32_t index;
+ index = (uint32_t)EAX;
+
+ /* test if maximum index reached */
+ if (index & 0x80000000) {
+ if (index > env->cpuid_xlevel)
+ index = env->cpuid_level;
+ } else {
+ if (index > env->cpuid_level)
+ index = env->cpuid_level;
+ }
+
+ switch(index) {
case 0:
- EAX = 2; /* max EAX index supported */
- EBX = 0x756e6547;
- ECX = 0x6c65746e;
- EDX = 0x49656e69;
+ EAX = env->cpuid_level;
+ EBX = env->cpuid_vendor1;
+ EDX = env->cpuid_vendor2;
+ ECX = env->cpuid_vendor3;
break;
case 1:
- {
- int family, model, stepping;
- /* EAX = 1 info */
-#if 0
- /* pentium 75-200 */
- family = 5;
- model = 2;
- stepping = 11;
-#else
- /* pentium pro */
- family = 6;
- model = 1;
- stepping = 3;
-#endif
- EAX = (family << 8) | (model << 4) | stepping;
- EBX = 0;
- ECX = 0;
- EDX = CPUID_FP87 | CPUID_DE | CPUID_PSE |
- CPUID_TSC | CPUID_MSR | CPUID_MCE |
- CPUID_CX8 | CPUID_PGE | CPUID_CMOV;
- }
+ EAX = env->cpuid_version;
+ EBX = 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */
+ ECX = env->cpuid_ext_features;
+ EDX = env->cpuid_features;
break;
- default:
+ case 2:
/* cache info: needed for Pentium Pro compatibility */
EAX = 0x410601;
EBX = 0;
ECX = 0;
EDX = 0;
break;
+ case 0x80000000:
+ EAX = env->cpuid_xlevel;
+ EBX = env->cpuid_vendor1;
+ EDX = env->cpuid_vendor2;
+ ECX = env->cpuid_vendor3;
+ break;
+ case 0x80000001:
+ EAX = env->cpuid_features;
+ EBX = 0;
+ ECX = 0;
+ EDX = env->cpuid_ext2_features;
+ break;
+ case 0x80000002:
+ case 0x80000003:
+ case 0x80000004:
+ EAX = env->cpuid_model[(index - 0x80000002) * 4 + 0];
+ EBX = env->cpuid_model[(index - 0x80000002) * 4 + 1];
+ ECX = env->cpuid_model[(index - 0x80000002) * 4 + 2];
+ EDX = env->cpuid_model[(index - 0x80000002) * 4 + 3];
+ break;
+ case 0x80000005:
+ /* cache info (L1 cache) */
+ EAX = 0x01ff01ff;
+ EBX = 0x01ff01ff;
+ ECX = 0x40020140;
+ EDX = 0x40020140;
+ break;
+ case 0x80000006:
+ /* cache info (L2 cache) */
+ EAX = 0;
+ EBX = 0x42004200;
+ ECX = 0x02008140;
+ EDX = 0;
+ break;
+ case 0x80000008:
+ /* virtual & phys address size in low 2 bytes. */
+ EAX = 0x00003028;
+ EBX = 0;
+ ECX = 0;
+ EDX = 0;
+ break;
+ default:
+ /* reserved values: zero */
+ EAX = 0;
+ EBX = 0;
+ ECX = 0;
+ EDX = 0;
+ break;
+ }
+}
+
+void helper_enter_level(int level, int data32)
+{
+ target_ulong ssp;
+ uint32_t esp_mask, esp, ebp;
+
+ esp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ ebp = EBP;
+ esp = ESP;
+ if (data32) {
+ /* 32 bit */
+ esp -= 4;
+ while (--level) {
+ esp -= 4;
+ ebp -= 4;
+ stl(ssp + (esp & esp_mask), ldl(ssp + (ebp & esp_mask)));
+ }
+ esp -= 4;
+ stl(ssp + (esp & esp_mask), T1);
+ } else {
+ /* 16 bit */
+ esp -= 2;
+ while (--level) {
+ esp -= 2;
+ ebp -= 2;
+ stw(ssp + (esp & esp_mask), lduw(ssp + (ebp & esp_mask)));
+ }
+ esp -= 2;
+ stw(ssp + (esp & esp_mask), T1);
+ }
+}
+
+#ifdef TARGET_X86_64
+void helper_enter64_level(int level, int data64)
+{
+ target_ulong esp, ebp;
+ ebp = EBP;
+ esp = ESP;
+
+ if (data64) {
+ /* 64 bit */
+ esp -= 8;
+ while (--level) {
+ esp -= 8;
+ ebp -= 8;
+ stq(esp, ldq(ebp));
+ }
+ esp -= 8;
+ stq(esp, T1);
+ } else {
+ /* 16 bit */
+ esp -= 2;
+ while (--level) {
+ esp -= 2;
+ ebp -= 2;
+ stw(esp, lduw(ebp));
+ }
+ esp -= 2;
+ stw(esp, T1);
}
}
+#endif
void helper_lldt_T0(void)
{
int selector;
SegmentCache *dt;
uint32_t e1, e2;
- int index;
- uint8_t *ptr;
+ int index, entry_limit;
+ target_ulong ptr;
selector = T0 & 0xffff;
if ((selector & 0xfffc) == 0) {
/* XXX: NULL selector case: invalid LDT */
- env->ldt.base = NULL;
+ env->ldt.base = 0;
env->ldt.limit = 0;
} else {
if (selector & 0x4)
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
dt = &env->gdt;
index = selector & ~7;
- if ((index + 7) > dt->limit)
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK)
+ entry_limit = 15;
+ else
+#endif
+ entry_limit = 7;
+ if ((index + entry_limit) > dt->limit)
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
ptr = dt->base + index;
e1 = ldl_kernel(ptr);
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
if (!(e2 & DESC_P_MASK))
raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
- load_seg_cache_raw_dt(&env->ldt, e1, e2);
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ uint32_t e3;
+ e3 = ldl_kernel(ptr + 8);
+ load_seg_cache_raw_dt(&env->ldt, e1, e2);
+ env->ldt.base |= (target_ulong)e3 << 32;
+ } else
+#endif
+ {
+ load_seg_cache_raw_dt(&env->ldt, e1, e2);
+ }
}
env->ldt.selector = selector;
}
int selector;
SegmentCache *dt;
uint32_t e1, e2;
- int index, type;
- uint8_t *ptr;
+ int index, type, entry_limit;
+ target_ulong ptr;
selector = T0 & 0xffff;
if ((selector & 0xfffc) == 0) {
- /* NULL selector case: invalid LDT */
- env->tr.base = NULL;
+ /* NULL selector case: invalid TR */
+ env->tr.base = 0;
env->tr.limit = 0;
env->tr.flags = 0;
} else {
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
dt = &env->gdt;
index = selector & ~7;
- if ((index + 7) > dt->limit)
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK)
+ entry_limit = 15;
+ else
+#endif
+ entry_limit = 7;
+ if ((index + entry_limit) > dt->limit)
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
ptr = dt->base + index;
e1 = ldl_kernel(ptr);
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
if (!(e2 & DESC_P_MASK))
raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
- load_seg_cache_raw_dt(&env->tr, e1, e2);
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ uint32_t e3;
+ e3 = ldl_kernel(ptr + 8);
+ load_seg_cache_raw_dt(&env->tr, e1, e2);
+ env->tr.base |= (target_ulong)e3 << 32;
+ } else
+#endif
+ {
+ load_seg_cache_raw_dt(&env->tr, e1, e2);
+ }
e2 |= DESC_TSS_BUSY_MASK;
stl_kernel(ptr + 4, e2);
}
int cpl, dpl, rpl;
SegmentCache *dt;
int index;
- uint8_t *ptr;
+ target_ulong ptr;
selector &= 0xffff;
+ cpl = env->hflags & HF_CPL_MASK;
if ((selector & 0xfffc) == 0) {
/* null selector case */
- if (seg_reg == R_SS)
+ if (seg_reg == R_SS
+#ifdef TARGET_X86_64
+ && (!(env->hflags & HF_CS64_MASK) || cpl == 3)
+#endif
+ )
raise_exception_err(EXCP0D_GPF, 0);
- cpu_x86_load_seg_cache(env, seg_reg, selector, NULL, 0, 0);
+ cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0);
} else {
if (selector & 0x4)
ptr = dt->base + index;
e1 = ldl_kernel(ptr);
e2 = ldl_kernel(ptr + 4);
-
+
if (!(e2 & DESC_S_MASK))
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
rpl = selector & 3;
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
- cpl = env->hflags & HF_CPL_MASK;
if (seg_reg == R_SS) {
/* must be writable segment */
if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
}
/* protected mode jump */
-void helper_ljmp_protected_T0_T1(void)
+void helper_ljmp_protected_T0_T1(int next_eip_addend)
{
- int new_cs, new_eip, gate_cs, type;
+ int new_cs, gate_cs, type;
uint32_t e1, e2, cpl, dpl, rpl, limit;
-
+ target_ulong new_eip, next_eip;
+
new_cs = T0;
new_eip = T1;
if ((new_cs & 0xfffc) == 0)
if (!(e2 & DESC_P_MASK))
raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
limit = get_seg_limit(e1, e2);
- if (new_eip > limit)
+ if (new_eip > limit &&
+ !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK))
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
get_seg_base(e1, e2), limit, e2);
case 5: /* task gate */
if (dpl < cpl || dpl < rpl)
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
- switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP);
+ next_eip = env->eip + next_eip_addend;
+ switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP, next_eip);
break;
case 4: /* 286 call gate */
case 12: /* 386 call gate */
if (!(e2 & DESC_P_MASK))
raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
gate_cs = e1 >> 16;
+ new_eip = (e1 & 0xffff);
+ if (type == 12)
+ new_eip |= (e2 & 0xffff0000);
if (load_segment(&e1, &e2, gate_cs) != 0)
raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) !=
(DESC_S_MASK | DESC_CS_MASK)))
raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
- if (((e2 & DESC_C_MASK) && (dpl > cpl)) ||
+ if (((e2 & DESC_C_MASK) && (dpl > cpl)) ||
(!(e2 & DESC_C_MASK) && (dpl != cpl)))
raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
if (!(e2 & DESC_P_MASK))
raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
- new_eip = (e1 & 0xffff);
- if (type == 12)
- new_eip |= (e2 & 0xffff0000);
limit = get_seg_limit(e1, e2);
if (new_eip > limit)
raise_exception_err(EXCP0D_GPF, 0);
{
int new_cs, new_eip;
uint32_t esp, esp_mask;
- uint8_t *ssp;
+ target_ulong ssp;
new_cs = T0;
new_eip = T1;
ESP = (ESP & ~esp_mask) | (esp & esp_mask);
env->eip = new_eip;
env->segs[R_CS].selector = new_cs;
- env->segs[R_CS].base = (uint8_t *)(new_cs << 4);
+ env->segs[R_CS].base = (new_cs << 4);
}
/* protected mode call */
-void helper_lcall_protected_T0_T1(int shift, int next_eip)
+void helper_lcall_protected_T0_T1(int shift, int next_eip_addend)
{
int new_cs, new_eip, new_stack, i;
uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count;
uint32_t ss, ss_e1, ss_e2, sp, type, ss_dpl, sp_mask;
uint32_t val, limit, old_sp_mask;
- uint8_t *ssp, *old_ssp;
+ target_ulong ssp, old_ssp, next_eip;
new_cs = T0;
new_eip = T1;
+ next_eip = env->eip + next_eip_addend;
#ifdef DEBUG_PCALL
- if (loglevel) {
- fprintf(logfile, "lcall %04x:%08x\n",
- new_cs, new_eip);
- cpu_x86_dump_state(env, logfile, X86_DUMP_CCOP);
+ if (loglevel & CPU_LOG_PCALL) {
+ fprintf(logfile, "lcall %04x:%08x s=%d\n",
+ new_cs, new_eip, shift);
+ cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP);
}
#endif
if ((new_cs & 0xfffc) == 0)
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
cpl = env->hflags & HF_CPL_MASK;
#ifdef DEBUG_PCALL
- if (loglevel) {
+ if (loglevel & CPU_LOG_PCALL) {
fprintf(logfile, "desc=%08x:%08x\n", e1, e2);
}
#endif
if (!(e2 & DESC_P_MASK))
raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
- sp = ESP;
- sp_mask = get_sp_mask(env->segs[R_SS].flags);
- ssp = env->segs[R_SS].base;
- if (shift) {
- PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
- PUSHL(ssp, sp, sp_mask, next_eip);
- } else {
- PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
- PUSHW(ssp, sp, sp_mask, next_eip);
+#ifdef TARGET_X86_64
+ /* XXX: check 16/32 bit cases in long mode */
+ if (shift == 2) {
+ target_ulong rsp;
+ /* 64 bit case */
+ rsp = ESP;
+ PUSHQ(rsp, env->segs[R_CS].selector);
+ PUSHQ(rsp, next_eip);
+ /* from this point, not restartable */
+ ESP = rsp;
+ cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2), e2);
+ EIP = new_eip;
+ } else
+#endif
+ {
+ sp = ESP;
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ if (shift) {
+ PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, sp, sp_mask, next_eip);
+ } else {
+ PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, sp, sp_mask, next_eip);
+ }
+
+ limit = get_seg_limit(e1, e2);
+ if (new_eip > limit)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ /* from this point, not restartable */
+ ESP = (ESP & ~sp_mask) | (sp & sp_mask);
+ cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
+ get_seg_base(e1, e2), limit, e2);
+ EIP = new_eip;
}
-
- limit = get_seg_limit(e1, e2);
- if (new_eip > limit)
- raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
- /* from this point, not restartable */
- ESP = (ESP & ~sp_mask) | (sp & sp_mask);
- cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
- get_seg_base(e1, e2), limit, e2);
- EIP = new_eip;
} else {
/* check gate type */
type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
case 5: /* task gate */
if (dpl < cpl || dpl < rpl)
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
- switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL);
- break;
+ switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL, next_eip);
+ return;
case 4: /* 286 call gate */
case 12: /* 386 call gate */
break;
/* to inner priviledge */
get_ss_esp_from_tss(&ss, &sp, dpl);
#ifdef DEBUG_PCALL
- if (loglevel)
- fprintf(logfile, "ss=%04x sp=%04x param_count=%d ESP=%x\n",
+ if (loglevel & CPU_LOG_PCALL)
+ fprintf(logfile, "new ss:esp=%04x:%08x param_count=%d ESP=" TARGET_FMT_lx "\n",
ss, sp, param_count, ESP);
#endif
if ((ss & 0xfffc) == 0)
ESP = (ESP & ~sp_mask) | (sp & sp_mask);
EIP = offset;
}
+#ifdef USE_KQEMU
+ if (kqemu_is_ok(env)) {
+ env->exception_index = -1;
+ cpu_loop_exit();
+ }
+#endif
}
/* real and vm86 mode iret */
void helper_iret_real(int shift)
{
uint32_t sp, new_cs, new_eip, new_eflags, sp_mask;
- uint8_t *ssp;
+ target_ulong ssp;
int eflags_mask;
sp_mask = 0xffff; /* XXXX: use SS segment size ? */
load_seg_vm(R_CS, new_cs);
env->eip = new_eip;
if (env->eflags & VM_MASK)
- eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK;
+ eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | NT_MASK;
else
- eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | RF_MASK;
+ eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | RF_MASK | NT_MASK;
if (shift == 0)
eflags_mask &= 0xffff;
load_eflags(new_eflags, eflags_mask);
if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
/* data or non conforming code segment */
if (dpl < cpl) {
- cpu_x86_load_seg_cache(env, seg_reg, 0, NULL, 0, 0);
+ cpu_x86_load_seg_cache(env, seg_reg, 0, 0, 0, 0);
}
}
}
/* protected mode iret */
static inline void helper_ret_protected(int shift, int is_iret, int addend)
{
- uint32_t sp, new_cs, new_eip, new_eflags, new_esp, new_ss, sp_mask;
+ uint32_t new_cs, new_eflags, new_ss;
uint32_t new_es, new_ds, new_fs, new_gs;
uint32_t e1, e2, ss_e1, ss_e2;
int cpl, dpl, rpl, eflags_mask, iopl;
- uint8_t *ssp;
+ target_ulong ssp, sp, new_eip, new_esp, sp_mask;
- sp_mask = get_sp_mask(env->segs[R_SS].flags);
+#ifdef TARGET_X86_64
+ if (shift == 2)
+ sp_mask = -1;
+ else
+#endif
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
sp = ESP;
ssp = env->segs[R_SS].base;
+ new_eflags = 0; /* avoid warning */
+#ifdef TARGET_X86_64
+ if (shift == 2) {
+ POPQ(sp, new_eip);
+ POPQ(sp, new_cs);
+ new_cs &= 0xffff;
+ if (is_iret) {
+ POPQ(sp, new_eflags);
+ }
+ } else
+#endif
if (shift == 1) {
/* 32 bits */
POPL(ssp, sp, sp_mask, new_eip);
POPW(ssp, sp, sp_mask, new_eflags);
}
#ifdef DEBUG_PCALL
- if (loglevel) {
- fprintf(logfile, "lret new %04x:%08x addend=0x%x\n",
- new_cs, new_eip, addend);
- cpu_x86_dump_state(env, logfile, X86_DUMP_CCOP);
+ if (loglevel & CPU_LOG_PCALL) {
+ fprintf(logfile, "lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n",
+ new_cs, new_eip, shift, addend);
+ cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP);
}
#endif
if ((new_cs & 0xfffc) == 0)
raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
sp += addend;
- if (rpl == cpl) {
+ if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) ||
+ ((env->hflags & HF_CS64_MASK) && !is_iret))) {
/* return to same priledge level */
cpu_x86_load_seg_cache(env, R_CS, new_cs,
get_seg_base(e1, e2),
e2);
} else {
/* return to different priviledge level */
+#ifdef TARGET_X86_64
+ if (shift == 2) {
+ POPQ(sp, new_esp);
+ POPQ(sp, new_ss);
+ new_ss &= 0xffff;
+ } else
+#endif
if (shift == 1) {
/* 32 bits */
POPL(ssp, sp, sp_mask, new_esp);
POPW(ssp, sp, sp_mask, new_esp);
POPW(ssp, sp, sp_mask, new_ss);
}
-
- if ((new_ss & 3) != rpl)
- raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
- if (load_segment(&ss_e1, &ss_e2, new_ss) != 0)
- raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
- if (!(ss_e2 & DESC_S_MASK) ||
- (ss_e2 & DESC_CS_MASK) ||
- !(ss_e2 & DESC_W_MASK))
- raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
- dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
- if (dpl != rpl)
- raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
- if (!(ss_e2 & DESC_P_MASK))
- raise_exception_err(EXCP0B_NOSEG, new_ss & 0xfffc);
+#ifdef DEBUG_PCALL
+ if (loglevel & CPU_LOG_PCALL) {
+ fprintf(logfile, "new ss:esp=%04x:" TARGET_FMT_lx "\n",
+ new_ss, new_esp);
+ }
+#endif
+ if ((new_ss & 0xfffc) == 0) {
+#ifdef TARGET_X86_64
+ /* NULL ss is allowed in long mode if cpl != 3*/
+ if ((env->hflags & HF_LMA_MASK) && rpl != 3) {
+ cpu_x86_load_seg_cache(env, R_SS, new_ss,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (rpl << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ } else
+#endif
+ {
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+ } else {
+ if ((new_ss & 3) != rpl)
+ raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
+ if (load_segment(&ss_e1, &ss_e2, new_ss) != 0)
+ raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
+ if (!(ss_e2 & DESC_S_MASK) ||
+ (ss_e2 & DESC_CS_MASK) ||
+ !(ss_e2 & DESC_W_MASK))
+ raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
+ dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
+ if (dpl != rpl)
+ raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
+ if (!(ss_e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_ss & 0xfffc);
+ cpu_x86_load_seg_cache(env, R_SS, new_ss,
+ get_seg_base(ss_e1, ss_e2),
+ get_seg_limit(ss_e1, ss_e2),
+ ss_e2);
+ }
cpu_x86_load_seg_cache(env, R_CS, new_cs,
get_seg_base(e1, e2),
get_seg_limit(e1, e2),
e2);
- cpu_x86_load_seg_cache(env, R_SS, new_ss,
- get_seg_base(ss_e1, ss_e2),
- get_seg_limit(ss_e1, ss_e2),
- ss_e2);
cpu_x86_set_cpl(env, rpl);
sp = new_esp;
- /* XXX: change sp_mask according to old segment ? */
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_CS64_MASK)
+ sp_mask = -1;
+ else
+#endif
+ sp_mask = get_sp_mask(ss_e2);
/* validate data segments */
validate_seg(R_ES, cpl);
validate_seg(R_DS, cpl);
validate_seg(R_FS, cpl);
validate_seg(R_GS, cpl);
+
+ sp += addend;
}
ESP = (ESP & ~sp_mask) | (sp & sp_mask);
env->eip = new_eip;
if (is_iret) {
/* NOTE: 'cpl' is the _old_ CPL */
- eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK;
+ eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK;
if (cpl == 0)
eflags_mask |= IOPL_MASK;
iopl = (env->eflags >> IOPL_SHIFT) & 3;
/* modify processor state */
load_eflags(new_eflags, TF_MASK | AC_MASK | ID_MASK |
- IF_MASK | IOPL_MASK | VM_MASK | VIF_MASK | VIP_MASK);
+ IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK | VIP_MASK);
load_seg_vm(R_CS, new_cs & 0xffff);
cpu_x86_set_cpl(env, 3);
load_seg_vm(R_SS, new_ss & 0xffff);
load_seg_vm(R_FS, new_fs & 0xffff);
load_seg_vm(R_GS, new_gs & 0xffff);
- env->eip = new_eip;
+ env->eip = new_eip & 0xffff;
ESP = new_esp;
}
-void helper_iret_protected(int shift)
+void helper_iret_protected(int shift, int next_eip)
{
int tss_selector, type;
uint32_t e1, e2;
/* specific case for TSS */
if (env->eflags & NT_MASK) {
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK)
+ raise_exception_err(EXCP0D_GPF, 0);
+#endif
tss_selector = lduw_kernel(env->tr.base + 0);
if (tss_selector & 4)
raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
/* NOTE: we check both segment and busy TSS */
if (type != 3)
raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
- switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET);
+ switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip);
} else {
helper_ret_protected(shift, 1, 0);
}
+#ifdef USE_KQEMU
+ if (kqemu_is_ok(env)) {
+ CC_OP = CC_OP_EFLAGS;
+ env->exception_index = -1;
+ cpu_loop_exit();
+ }
+#endif
}
void helper_lret_protected(int shift, int addend)
{
helper_ret_protected(shift, 0, addend);
+#ifdef USE_KQEMU
+ if (kqemu_is_ok(env)) {
+ env->exception_index = -1;
+ cpu_loop_exit();
+ }
+#endif
+}
+
+void helper_sysenter(void)
+{
+ if (env->sysenter_cs == 0) {
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+ env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK);
+ cpu_x86_set_cpl(env, 0);
+ cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_W_MASK | DESC_A_MASK);
+ ESP = env->sysenter_esp;
+ EIP = env->sysenter_eip;
+}
+
+void helper_sysexit(void)
+{
+ int cpl;
+
+ cpl = env->hflags & HF_CPL_MASK;
+ if (env->sysenter_cs == 0 || cpl != 0) {
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+ cpu_x86_set_cpl(env, 3);
+ cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ ESP = ECX;
+ EIP = EDX;
+#ifdef USE_KQEMU
+ if (kqemu_is_ok(env)) {
+ env->exception_index = -1;
+ cpu_loop_exit();
+ }
+#endif
}
void helper_movl_crN_T0(int reg)
{
- env->cr[reg] = T0;
+#if !defined(CONFIG_USER_ONLY)
switch(reg) {
case 0:
- cpu_x86_update_cr0(env);
+ cpu_x86_update_cr0(env, T0);
break;
case 3:
- cpu_x86_update_cr3(env);
+ cpu_x86_update_cr3(env, T0);
+ break;
+ case 4:
+ cpu_x86_update_cr4(env, T0);
+ break;
+ case 8:
+ cpu_set_apic_tpr(env, T0);
+ break;
+ default:
+ env->cr[reg] = T0;
break;
}
+#endif
}
/* XXX: do more */
env->dr[reg] = T0;
}
-void helper_invlpg(unsigned int addr)
+void helper_invlpg(target_ulong addr)
{
cpu_x86_flush_tlb(env, addr);
}
-/* rdtsc */
-#ifndef __i386__
-uint64_t emu_time;
-#endif
-
void helper_rdtsc(void)
{
uint64_t val;
-#ifdef __i386__
- asm("rdtsc" : "=A" (val));
-#else
- /* better than nothing: the time increases */
- val = emu_time++;
-#endif
- EAX = val;
- EDX = val >> 32;
+
+ if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
+ raise_exception(EXCP0D_GPF);
+ }
+ val = cpu_get_tsc(env);
+ EAX = (uint32_t)(val);
+ EDX = (uint32_t)(val >> 32);
+}
+
+#if defined(CONFIG_USER_ONLY)
+void helper_wrmsr(void)
+{
}
+void helper_rdmsr(void)
+{
+}
+#else
void helper_wrmsr(void)
{
- switch(ECX) {
+ uint64_t val;
+
+ val = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
+
+ switch((uint32_t)ECX) {
case MSR_IA32_SYSENTER_CS:
- env->sysenter_cs = EAX & 0xffff;
+ env->sysenter_cs = val & 0xffff;
break;
case MSR_IA32_SYSENTER_ESP:
- env->sysenter_esp = EAX;
+ env->sysenter_esp = val;
break;
case MSR_IA32_SYSENTER_EIP:
- env->sysenter_eip = EAX;
+ env->sysenter_eip = val;
+ break;
+ case MSR_IA32_APICBASE:
+ cpu_set_apic_base(env, val);
+ break;
+ case MSR_EFER:
+ {
+ uint64_t update_mask;
+ update_mask = 0;
+ if (env->cpuid_ext2_features & CPUID_EXT2_SYSCALL)
+ update_mask |= MSR_EFER_SCE;
+ if (env->cpuid_ext2_features & CPUID_EXT2_LM)
+ update_mask |= MSR_EFER_LME;
+ if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
+ update_mask |= MSR_EFER_FFXSR;
+ if (env->cpuid_ext2_features & CPUID_EXT2_NX)
+ update_mask |= MSR_EFER_NXE;
+ env->efer = (env->efer & ~update_mask) |
+ (val & update_mask);
+ }
+ break;
+ case MSR_STAR:
+ env->star = val;
+ break;
+ case MSR_PAT:
+ env->pat = val;
+ break;
+#ifdef TARGET_X86_64
+ case MSR_LSTAR:
+ env->lstar = val;
+ break;
+ case MSR_CSTAR:
+ env->cstar = val;
+ break;
+ case MSR_FMASK:
+ env->fmask = val;
+ break;
+ case MSR_FSBASE:
+ env->segs[R_FS].base = val;
break;
+ case MSR_GSBASE:
+ env->segs[R_GS].base = val;
+ break;
+ case MSR_KERNELGSBASE:
+ env->kernelgsbase = val;
+ break;
+#endif
default:
/* XXX: exception ? */
break;
void helper_rdmsr(void)
{
- switch(ECX) {
+ uint64_t val;
+ switch((uint32_t)ECX) {
case MSR_IA32_SYSENTER_CS:
- EAX = env->sysenter_cs;
- EDX = 0;
+ val = env->sysenter_cs;
break;
case MSR_IA32_SYSENTER_ESP:
- EAX = env->sysenter_esp;
- EDX = 0;
+ val = env->sysenter_esp;
break;
case MSR_IA32_SYSENTER_EIP:
- EAX = env->sysenter_eip;
- EDX = 0;
+ val = env->sysenter_eip;
+ break;
+ case MSR_IA32_APICBASE:
+ val = cpu_get_apic_base(env);
+ break;
+ case MSR_EFER:
+ val = env->efer;
+ break;
+ case MSR_STAR:
+ val = env->star;
+ break;
+ case MSR_PAT:
+ val = env->pat;
+ break;
+#ifdef TARGET_X86_64
+ case MSR_LSTAR:
+ val = env->lstar;
break;
+ case MSR_CSTAR:
+ val = env->cstar;
+ break;
+ case MSR_FMASK:
+ val = env->fmask;
+ break;
+ case MSR_FSBASE:
+ val = env->segs[R_FS].base;
+ break;
+ case MSR_GSBASE:
+ val = env->segs[R_GS].base;
+ break;
+ case MSR_KERNELGSBASE:
+ val = env->kernelgsbase;
+ break;
+#endif
default:
/* XXX: exception ? */
+ val = 0;
break;
}
+ EAX = (uint32_t)(val);
+ EDX = (uint32_t)(val >> 32);
}
+#endif
void helper_lsl(void)
{
unsigned int selector, limit;
- uint32_t e1, e2;
+ uint32_t e1, e2, eflags;
int rpl, dpl, cpl, type;
- CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
+ eflags = cc_table[CC_OP].compute_all();
selector = T0 & 0xffff;
if (load_segment(&e1, &e2, selector) != 0)
- return;
+ goto fail;
rpl = selector & 3;
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
cpl = env->hflags & HF_CPL_MASK;
/* conforming */
} else {
if (dpl < cpl || dpl < rpl)
- return;
+ goto fail;
}
} else {
type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
case 11:
break;
default:
- return;
+ goto fail;
}
- if (dpl < cpl || dpl < rpl)
+ if (dpl < cpl || dpl < rpl) {
+ fail:
+ CC_SRC = eflags & ~CC_Z;
return;
+ }
}
limit = get_seg_limit(e1, e2);
T1 = limit;
- CC_SRC |= CC_Z;
+ CC_SRC = eflags | CC_Z;
}
void helper_lar(void)
{
unsigned int selector;
- uint32_t e1, e2;
+ uint32_t e1, e2, eflags;
int rpl, dpl, cpl, type;
- CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
+ eflags = cc_table[CC_OP].compute_all();
selector = T0 & 0xffff;
if ((selector & 0xfffc) == 0)
- return;
+ goto fail;
if (load_segment(&e1, &e2, selector) != 0)
- return;
+ goto fail;
rpl = selector & 3;
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
cpl = env->hflags & HF_CPL_MASK;
/* conforming */
} else {
if (dpl < cpl || dpl < rpl)
- return;
+ goto fail;
}
} else {
type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
case 12:
break;
default:
- return;
+ goto fail;
}
- if (dpl < cpl || dpl < rpl)
+ if (dpl < cpl || dpl < rpl) {
+ fail:
+ CC_SRC = eflags & ~CC_Z;
return;
+ }
}
T1 = e2 & 0x00f0ff00;
- CC_SRC |= CC_Z;
+ CC_SRC = eflags | CC_Z;
}
void helper_verr(void)
{
unsigned int selector;
- uint32_t e1, e2;
+ uint32_t e1, e2, eflags;
int rpl, dpl, cpl;
- CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
+ eflags = cc_table[CC_OP].compute_all();
selector = T0 & 0xffff;
if ((selector & 0xfffc) == 0)
- return;
+ goto fail;
if (load_segment(&e1, &e2, selector) != 0)
- return;
+ goto fail;
if (!(e2 & DESC_S_MASK))
- return;
+ goto fail;
rpl = selector & 3;
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
cpl = env->hflags & HF_CPL_MASK;
if (e2 & DESC_CS_MASK) {
if (!(e2 & DESC_R_MASK))
- return;
+ goto fail;
if (!(e2 & DESC_C_MASK)) {
if (dpl < cpl || dpl < rpl)
- return;
+ goto fail;
}
} else {
- if (dpl < cpl || dpl < rpl)
+ if (dpl < cpl || dpl < rpl) {
+ fail:
+ CC_SRC = eflags & ~CC_Z;
return;
+ }
}
- CC_SRC |= CC_Z;
+ CC_SRC = eflags | CC_Z;
}
void helper_verw(void)
{
unsigned int selector;
- uint32_t e1, e2;
+ uint32_t e1, e2, eflags;
int rpl, dpl, cpl;
- CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
+ eflags = cc_table[CC_OP].compute_all();
selector = T0 & 0xffff;
if ((selector & 0xfffc) == 0)
- return;
+ goto fail;
if (load_segment(&e1, &e2, selector) != 0)
- return;
+ goto fail;
if (!(e2 & DESC_S_MASK))
- return;
+ goto fail;
rpl = selector & 3;
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
cpl = env->hflags & HF_CPL_MASK;
if (e2 & DESC_CS_MASK) {
- return;
+ goto fail;
} else {
if (dpl < cpl || dpl < rpl)
+ goto fail;
+ if (!(e2 & DESC_W_MASK)) {
+ fail:
+ CC_SRC = eflags & ~CC_Z;
return;
- if (!(e2 & DESC_W_MASK))
- return;
+ }
}
- CC_SRC |= CC_Z;
+ CC_SRC = eflags | CC_Z;
}
/* FPU helpers */
{
int new_fpstt;
new_fpstt = (env->fpstt - 1) & 7;
- env->fpregs[new_fpstt] = helper_fldt((uint8_t *)A0);
+ env->fpregs[new_fpstt].d = helper_fldt(A0);
env->fpstt = new_fpstt;
env->fptags[new_fpstt] = 0; /* validate stack entry */
}
void helper_fstt_ST0_A0(void)
{
- helper_fstt(ST0, (uint8_t *)A0);
+ helper_fstt(ST0, A0);
}
-/* BCD ops */
+void fpu_set_exception(int mask)
+{
+ env->fpus |= mask;
+ if (env->fpus & (~env->fpuc & FPUC_EM))
+ env->fpus |= FPUS_SE | FPUS_B;
+}
+
+CPU86_LDouble helper_fdiv(CPU86_LDouble a, CPU86_LDouble b)
+{
+ if (b == 0.0)
+ fpu_set_exception(FPUS_ZE);
+ return a / b;
+}
+
+void fpu_raise_exception(void)
+{
+ if (env->cr[0] & CR0_NE_MASK) {
+ raise_exception(EXCP10_COPR);
+ }
+#if !defined(CONFIG_USER_ONLY)
+ else {
+ cpu_set_ferr(env);
+ }
+#endif
+}
-#define MUL10(iv) ( iv + iv + (iv << 3) )
+/* BCD ops */
void helper_fbld_ST0_A0(void)
{
val = 0;
for(i = 8; i >= 0; i--) {
- v = ldub((uint8_t *)A0 + i);
+ v = ldub(A0 + i);
val = (val * 100) + ((v >> 4) * 10) + (v & 0xf);
}
tmp = val;
- if (ldub((uint8_t *)A0 + 9) & 0x80)
+ if (ldub(A0 + 9) & 0x80)
tmp = -tmp;
fpush();
ST0 = tmp;
void helper_fbst_ST0_A0(void)
{
- CPU86_LDouble tmp;
int v;
- uint8_t *mem_ref, *mem_end;
+ target_ulong mem_ref, mem_end;
int64_t val;
- tmp = rint(ST0);
- val = (int64_t)tmp;
- mem_ref = (uint8_t *)A0;
+ val = floatx_to_int64(ST0, &env->fp_status);
+ mem_ref = A0;
mem_end = mem_ref + 9;
if (val < 0) {
stb(mem_end, 0x80);
void helper_frndint(void)
{
- CPU86_LDouble a;
-
- a = ST0;
-#ifdef __arm__
- switch(env->fpuc & RC_MASK) {
- default:
- case RC_NEAR:
- asm("rndd %0, %1" : "=f" (a) : "f"(a));
- break;
- case RC_DOWN:
- asm("rnddm %0, %1" : "=f" (a) : "f"(a));
- break;
- case RC_UP:
- asm("rnddp %0, %1" : "=f" (a) : "f"(a));
- break;
- case RC_CHOP:
- asm("rnddz %0, %1" : "=f" (a) : "f"(a));
- break;
- }
-#else
- a = rint(a);
-#endif
- ST0 = a;
+ ST0 = floatx_round_to_int(ST0, &env->fp_status);
}
void helper_fscale(void)
{
- CPU86_LDouble fpsrcop, fptemp;
-
- fpsrcop = 2.0;
- fptemp = pow(fpsrcop,ST1);
- ST0 *= fptemp;
+ ST0 = ldexp (ST0, (int)(ST1));
}
void helper_fsin(void)
}
}
-void helper_fstenv(uint8_t *ptr, int data32)
+void helper_fstenv(target_ulong ptr, int data32)
{
int fpus, fptag, exp, i;
uint64_t mant;
if (env->fptags[i]) {
fptag |= 3;
} else {
- tmp.d = env->fpregs[i];
+ tmp.d = env->fpregs[i].d;
exp = EXPD(tmp);
mant = MANTD(tmp);
if (exp == 0 && mant == 0) {
stl(ptr, env->fpuc);
stl(ptr + 4, fpus);
stl(ptr + 8, fptag);
- stl(ptr + 12, 0);
- stl(ptr + 16, 0);
- stl(ptr + 20, 0);
- stl(ptr + 24, 0);
+ stl(ptr + 12, 0); /* fpip */
+ stl(ptr + 16, 0); /* fpcs */
+ stl(ptr + 20, 0); /* fpoo */
+ stl(ptr + 24, 0); /* fpos */
} else {
/* 16 bit */
stw(ptr, env->fpuc);
}
}
-void helper_fldenv(uint8_t *ptr, int data32)
+void helper_fldenv(target_ulong ptr, int data32)
{
int i, fpus, fptag;
}
env->fpstt = (fpus >> 11) & 7;
env->fpus = fpus & ~0x3800;
- for(i = 0;i < 7; i++) {
+ for(i = 0;i < 8; i++) {
env->fptags[i] = ((fptag & 3) == 3);
fptag >>= 2;
}
}
-void helper_fsave(uint8_t *ptr, int data32)
+void helper_fsave(target_ulong ptr, int data32)
{
CPU86_LDouble tmp;
int i;
env->fptags[7] = 1;
}
-void helper_frstor(uint8_t *ptr, int data32)
+void helper_frstor(target_ulong ptr, int data32)
{
CPU86_LDouble tmp;
int i;
}
}
+void helper_fxsave(target_ulong ptr, int data64)
+{
+ int fpus, fptag, i, nb_xmm_regs;
+ CPU86_LDouble tmp;
+ target_ulong addr;
+
+ fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ fptag = 0;
+ for(i = 0; i < 8; i++) {
+ fptag |= (env->fptags[i] << i);
+ }
+ stw(ptr, env->fpuc);
+ stw(ptr + 2, fpus);
+ stw(ptr + 4, fptag ^ 0xff);
+
+ addr = ptr + 0x20;
+ for(i = 0;i < 8; i++) {
+ tmp = ST(i);
+ helper_fstt(tmp, addr);
+ addr += 16;
+ }
+
+ if (env->cr[4] & CR4_OSFXSR_MASK) {
+ /* XXX: finish it */
+ stl(ptr + 0x18, env->mxcsr); /* mxcsr */
+ stl(ptr + 0x1c, 0x0000ffff); /* mxcsr_mask */
+ nb_xmm_regs = 8 << data64;
+ addr = ptr + 0xa0;
+ for(i = 0; i < nb_xmm_regs; i++) {
+ stq(addr, env->xmm_regs[i].XMM_Q(0));
+ stq(addr + 8, env->xmm_regs[i].XMM_Q(1));
+ addr += 16;
+ }
+ }
+}
+
+void helper_fxrstor(target_ulong ptr, int data64)
+{
+ int i, fpus, fptag, nb_xmm_regs;
+ CPU86_LDouble tmp;
+ target_ulong addr;
+
+ env->fpuc = lduw(ptr);
+ fpus = lduw(ptr + 2);
+ fptag = lduw(ptr + 4);
+ env->fpstt = (fpus >> 11) & 7;
+ env->fpus = fpus & ~0x3800;
+ fptag ^= 0xff;
+ for(i = 0;i < 8; i++) {
+ env->fptags[i] = ((fptag >> i) & 1);
+ }
+
+ addr = ptr + 0x20;
+ for(i = 0;i < 8; i++) {
+ tmp = helper_fldt(addr);
+ ST(i) = tmp;
+ addr += 16;
+ }
+
+ if (env->cr[4] & CR4_OSFXSR_MASK) {
+ /* XXX: finish it */
+ env->mxcsr = ldl(ptr + 0x18);
+ //ldl(ptr + 0x1c);
+ nb_xmm_regs = 8 << data64;
+ addr = ptr + 0xa0;
+ for(i = 0; i < nb_xmm_regs; i++) {
+ env->xmm_regs[i].XMM_Q(0) = ldq(addr);
+ env->xmm_regs[i].XMM_Q(1) = ldq(addr + 8);
+ addr += 16;
+ }
+ }
+}
+
+#ifndef USE_X86LDOUBLE
+
+void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f)
+{
+ CPU86_LDoubleU temp;
+ int e;
+
+ temp.d = f;
+ /* mantissa */
+ *pmant = (MANTD(temp) << 11) | (1LL << 63);
+ /* exponent + sign */
+ e = EXPD(temp) - EXPBIAS + 16383;
+ e |= SIGND(temp) >> 16;
+ *pexp = e;
+}
+
+CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper)
+{
+ CPU86_LDoubleU temp;
+ int e;
+ uint64_t ll;
+
+ /* XXX: handle overflow ? */
+ e = (upper & 0x7fff) - 16383 + EXPBIAS; /* exponent */
+ e |= (upper >> 4) & 0x800; /* sign */
+ ll = (mant >> 11) & ((1LL << 52) - 1);
+#ifdef __arm__
+ temp.l.upper = (e << 20) | (ll >> 32);
+ temp.l.lower = ll;
+#else
+ temp.ll = ll | ((uint64_t)e << 52);
+#endif
+ return temp.d;
+}
+
+#else
+
+void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f)
+{
+ CPU86_LDoubleU temp;
+
+ temp.d = f;
+ *pmant = temp.l.lower;
+ *pexp = temp.l.upper;
+}
+
+CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper)
+{
+ CPU86_LDoubleU temp;
+
+ temp.l.upper = upper;
+ temp.l.lower = mant;
+ return temp.d;
+}
+#endif
+
+#ifdef TARGET_X86_64
+
+//#define DEBUG_MULDIV
+
+static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+{
+ *plow += a;
+ /* carry test */
+ if (*plow < a)
+ (*phigh)++;
+ *phigh += b;
+}
+
+static void neg128(uint64_t *plow, uint64_t *phigh)
+{
+ *plow = ~ *plow;
+ *phigh = ~ *phigh;
+ add128(plow, phigh, 1, 0);
+}
+
+static void mul64(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+{
+ uint32_t a0, a1, b0, b1;
+ uint64_t v;
+
+ a0 = a;
+ a1 = a >> 32;
+
+ b0 = b;
+ b1 = b >> 32;
+
+ v = (uint64_t)a0 * (uint64_t)b0;
+ *plow = v;
+ *phigh = 0;
+
+ v = (uint64_t)a0 * (uint64_t)b1;
+ add128(plow, phigh, v << 32, v >> 32);
+
+ v = (uint64_t)a1 * (uint64_t)b0;
+ add128(plow, phigh, v << 32, v >> 32);
+
+ v = (uint64_t)a1 * (uint64_t)b1;
+ *phigh += v;
+#ifdef DEBUG_MULDIV
+ printf("mul: 0x%016llx * 0x%016llx = 0x%016llx%016llx\n",
+ a, b, *phigh, *plow);
+#endif
+}
+
+static void imul64(uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b)
+{
+ int sa, sb;
+ sa = (a < 0);
+ if (sa)
+ a = -a;
+ sb = (b < 0);
+ if (sb)
+ b = -b;
+ mul64(plow, phigh, a, b);
+ if (sa ^ sb) {
+ neg128(plow, phigh);
+ }
+}
+
+/* return TRUE if overflow */
+static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
+{
+ uint64_t q, r, a1, a0;
+ int i, qb, ab;
+
+ a0 = *plow;
+ a1 = *phigh;
+ if (a1 == 0) {
+ q = a0 / b;
+ r = a0 % b;
+ *plow = q;
+ *phigh = r;
+ } else {
+ if (a1 >= b)
+ return 1;
+ /* XXX: use a better algorithm */
+ for(i = 0; i < 64; i++) {
+ ab = a1 >> 63;
+ a1 = (a1 << 1) | (a0 >> 63);
+ if (ab || a1 >= b) {
+ a1 -= b;
+ qb = 1;
+ } else {
+ qb = 0;
+ }
+ a0 = (a0 << 1) | qb;
+ }
+#if defined(DEBUG_MULDIV)
+ printf("div: 0x%016llx%016llx / 0x%016llx: q=0x%016llx r=0x%016llx\n",
+ *phigh, *plow, b, a0, a1);
+#endif
+ *plow = a0;
+ *phigh = a1;
+ }
+ return 0;
+}
+
+/* return TRUE if overflow */
+static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b)
+{
+ int sa, sb;
+ sa = ((int64_t)*phigh < 0);
+ if (sa)
+ neg128(plow, phigh);
+ sb = (b < 0);
+ if (sb)
+ b = -b;
+ if (div64(plow, phigh, b) != 0)
+ return 1;
+ if (sa ^ sb) {
+ if (*plow > (1ULL << 63))
+ return 1;
+ *plow = - *plow;
+ } else {
+ if (*plow >= (1ULL << 63))
+ return 1;
+ }
+ if (sa)
+ *phigh = - *phigh;
+ return 0;
+}
+
+void helper_mulq_EAX_T0(void)
+{
+ uint64_t r0, r1;
+
+ mul64(&r0, &r1, EAX, T0);
+ EAX = r0;
+ EDX = r1;
+ CC_DST = r0;
+ CC_SRC = r1;
+}
+
+void helper_imulq_EAX_T0(void)
+{
+ uint64_t r0, r1;
+
+ imul64(&r0, &r1, EAX, T0);
+ EAX = r0;
+ EDX = r1;
+ CC_DST = r0;
+ CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
+}
+
+void helper_imulq_T0_T1(void)
+{
+ uint64_t r0, r1;
+
+ imul64(&r0, &r1, T0, T1);
+ T0 = r0;
+ CC_DST = r0;
+ CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
+}
+
+void helper_divq_EAX_T0(void)
+{
+ uint64_t r0, r1;
+ if (T0 == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ r0 = EAX;
+ r1 = EDX;
+ if (div64(&r0, &r1, T0))
+ raise_exception(EXCP00_DIVZ);
+ EAX = r0;
+ EDX = r1;
+}
+
+void helper_idivq_EAX_T0(void)
+{
+ uint64_t r0, r1;
+ if (T0 == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ r0 = EAX;
+ r1 = EDX;
+ if (idiv64(&r0, &r1, T0))
+ raise_exception(EXCP00_DIVZ);
+ EAX = r0;
+ EDX = r1;
+}
+
+#endif
+
+float approx_rsqrt(float a)
+{
+ return 1.0 / sqrt(a);
+}
+
+float approx_rcp(float a)
+{
+ return 1.0 / a;
+}
+
+void update_fp_status(void)
+{
+ int rnd_type;
+
+ /* set rounding mode */
+ switch(env->fpuc & RC_MASK) {
+ default:
+ case RC_NEAR:
+ rnd_type = float_round_nearest_even;
+ break;
+ case RC_DOWN:
+ rnd_type = float_round_down;
+ break;
+ case RC_UP:
+ rnd_type = float_round_up;
+ break;
+ case RC_CHOP:
+ rnd_type = float_round_to_zero;
+ break;
+ }
+ set_float_rounding_mode(rnd_type, &env->fp_status);
+#ifdef FLOATX80
+ switch((env->fpuc >> 8) & 3) {
+ case 0:
+ rnd_type = 32;
+ break;
+ case 2:
+ rnd_type = 64;
+ break;
+ case 3:
+ default:
+ rnd_type = 80;
+ break;
+ }
+ set_floatx80_rounding_precision(rnd_type, &env->fp_status);
+#endif
+}
+
#if !defined(CONFIG_USER_ONLY)
#define MMUSUFFIX _mmu
NULL, it means that the function was called in C code (i.e. not
from generated code or from helper.c) */
/* XXX: fix it to restore all registers */
-void tlb_fill(unsigned long addr, int is_write, int is_user, void *retaddr)
+void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr)
{
TranslationBlock *tb;
int ret;
if (tb) {
/* the PC is inside the translated code. It means that we have
a virtual CPU fault */
- cpu_restore_state(tb, env, pc);
+ cpu_restore_state(tb, env, pc, NULL);
}
}
- raise_exception_err(EXCP0E_PAGE, env->error_code);
+ if (retaddr)
+ raise_exception_err(EXCP0E_PAGE, env->error_code);
+ else
+ raise_exception_err_norestore(EXCP0E_PAGE, env->error_code);
}
env = saved_env;
}