📄 helper.c
字号:
/* * i386 helpers * * Copyright (c) 2003 Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include "exec.h"//#define DEBUG_PCALL#if 0#define raise_exception_err(a, b)\do {\ if (logfile)\ fprintf(logfile, "raise_exception line=%d\n", __LINE__);\ (raise_exception_err)(a, b);\} while (0)#endifconst 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, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,};/* modulo 17 table */const uint8_t rclw_table[32] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,};/* modulo 9 table */const uint8_t rclb_table[32] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4,};const CPU86_LDouble f15rk[7] ={ 0.00000000000000000000L, 1.00000000000000000000L, 3.14159265358979323851L, /*pi*/ 0.30102999566398119523L, /*lg2*/ 0.69314718055994530943L, /*ln2*/ 1.44269504088896340739L, /*l2e*/ 3.32192809488736234781L, /*l2t*/}; /* thread support */spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;void cpu_lock(void){ spin_lock(&global_cpu_lock);}void cpu_unlock(void){ spin_unlock(&global_cpu_lock);}void cpu_loop_exit(void){ /* NOTE: the register at this point must be saved by hand because longjmp restore them */ regs_to_env(); longjmp(env->jmp_env, 1);}/* return non zero if error */static inline int load_segment(uint32_t *e1_ptr, uint32_t *e2_ptr, int selector){ SegmentCache *dt; int index; target_ulong ptr; if (selector & 0x4) dt = &env->ldt; else dt = &env->gdt; index = selector & ~7; if ((index + 7) > dt->limit) return -1; ptr = dt->base + index; *e1_ptr = ldl_kernel(ptr); *e2_ptr = ldl_kernel(ptr + 4); return 0;} static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2){ unsigned int limit; limit = (e1 & 0xffff) | (e2 & 0x000f0000); if (e2 & DESC_G_MASK) limit = (limit << 12) | 0xfff; return limit;}static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2){ return ((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));}static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2){ sc->base = get_seg_base(e1, e2); sc->limit = get_seg_limit(e1, e2); sc->flags = e2;}/* init the segment cache in vm86 mode. */static inline void load_seg_vm(int seg, int selector){ selector &= 0xffff; cpu_x86_load_seg_cache(env, seg, selector, (selector << 4), 0xffff, 0);}static inline void get_ss_esp_from_tss(uint32_t *ss_ptr, uint32_t *esp_ptr, int dpl){ int type, index, shift; #if 0 { int i; printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit); for(i=0;i<env->tr.limit;i++) { printf("%02x ", env->tr.base[i]); if ((i & 7) == 7) printf("\n"); } printf("\n"); }#endif if (!(env->tr.flags & DESC_P_MASK)) cpu_abort(env, "invalid tss"); type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; if ((type & 7) != 1) cpu_abort(env, "invalid tss type"); shift = type >> 3; index = (dpl * 4 + 2) << shift; if (index + (4 << shift) - 1 > env->tr.limit) raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc); if (shift == 0) { *esp_ptr = lduw_kernel(env->tr.base + index); *ss_ptr = lduw_kernel(env->tr.base + index + 2); } else { *esp_ptr = ldl_kernel(env->tr.base + index); *ss_ptr = lduw_kernel(env->tr.base + index + 4); }}/* XXX: merge with load_seg() */static void tss_load_seg(int seg_reg, int selector){ uint32_t e1, e2; int rpl, dpl, cpl; if ((selector & 0xfffc) != 0) { if (load_segment(&e1, &e2, selector) != 0) raise_exception_err(EXCP0A_TSS, selector & 0xfffc); if (!(e2 & DESC_S_MASK)) raise_exception_err(EXCP0A_TSS, selector & 0xfffc); rpl = selector & 3; dpl = (e2 >> DESC_DPL_SHIFT) & 3; cpl = env->hflags & HF_CPL_MASK; if (seg_reg == R_CS) { if (!(e2 & DESC_CS_MASK)) raise_exception_err(EXCP0A_TSS, selector & 0xfffc); /* XXX: is it correct ? */ if (dpl != rpl) raise_exception_err(EXCP0A_TSS, selector & 0xfffc); if ((e2 & DESC_C_MASK) && dpl > rpl) raise_exception_err(EXCP0A_TSS, selector & 0xfffc); } else if (seg_reg == R_SS) { /* SS must be writable data */ if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) raise_exception_err(EXCP0A_TSS, selector & 0xfffc); if (dpl != cpl || dpl != rpl) raise_exception_err(EXCP0A_TSS, selector & 0xfffc); } else { /* not readable code */ if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK)) raise_exception_err(EXCP0A_TSS, selector & 0xfffc); /* if data or non conforming code, checks the rights */ if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) { if (dpl < cpl || dpl < rpl) raise_exception_err(EXCP0A_TSS, selector & 0xfffc); } } if (!(e2 & DESC_P_MASK)) raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); cpu_x86_load_seg_cache(env, seg_reg, selector, get_seg_base(e1, e2), get_seg_limit(e1, e2), e2); } else { if (seg_reg == R_SS || seg_reg == R_CS) raise_exception_err(EXCP0A_TSS, selector & 0xfffc); }}#define SWITCH_TSS_JMP 0#define SWITCH_TSS_IRET 1#define SWITCH_TSS_CALL 2/* 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 next_eip){ int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i; 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; target_ulong ptr; type = (e2 >> DESC_TYPE_SHIFT) & 0xf;#ifdef DEBUG_PCALL if (loglevel & CPU_LOG_PCALL) fprintf(logfile, "switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, source);#endif /* if task gate, we read the TSS segment and we load it */ if (type == 5) { if (!(e2 & DESC_P_MASK)) raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc); tss_selector = e1 >> 16; if (tss_selector & 4) raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); if (load_segment(&e1, &e2, tss_selector) != 0) raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc); if (e2 & DESC_S_MASK) raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc); type = (e2 >> DESC_TYPE_SHIFT) & 0xf; if ((type & 7) != 1) raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc); } if (!(e2 & DESC_P_MASK)) raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc); if (type & 8) tss_limit_max = 103; else tss_limit_max = 43; tss_limit = get_seg_limit(e1, e2); tss_base = get_seg_base(e1, e2); if ((tss_selector & 4) != 0 || tss_limit < tss_limit_max) raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; if (old_type & 8) old_tss_limit_max = 103; else old_tss_limit_max = 43; /* read all the registers from the new TSS */ if (type & 8) { /* 32 bit */ new_cr3 = ldl_kernel(tss_base + 0x1c); new_eip = ldl_kernel(tss_base + 0x20); new_eflags = ldl_kernel(tss_base + 0x24); for(i = 0; i < 8; i++) new_regs[i] = ldl_kernel(tss_base + (0x28 + i * 4)); for(i = 0; i < 6; i++) new_segs[i] = lduw_kernel(tss_base + (0x48 + i * 4)); new_ldt = lduw_kernel(tss_base + 0x60); new_trap = ldl_kernel(tss_base + 0x64); } else { /* 16 bit */ new_cr3 = 0; new_eip = lduw_kernel(tss_base + 0x0e); new_eflags = lduw_kernel(tss_base + 0x10); for(i = 0; i < 8; i++) new_regs[i] = lduw_kernel(tss_base + (0x12 + i * 2)) | 0xffff0000; for(i = 0; i < 4; i++) new_segs[i] = lduw_kernel(tss_base + (0x22 + i * 4)); new_ldt = lduw_kernel(tss_base + 0x2a); new_segs[R_FS] = 0; new_segs[R_GS] = 0; new_trap = 0; } /* NOTE: we must avoid memory exceptions during the task switch, so we make dummy accesses before */ /* XXX: it can still fail in some cases, so a bigger hack is necessary to valid the TLB after having done the accesses */ v1 = ldub_kernel(env->tr.base); v2 = ldub_kernel(env->tr.base + old_tss_limit_max); stb_kernel(env->tr.base, v1); stb_kernel(env->tr.base + old_tss_limit_max, v2); /* clear busy bit (it is restartable) */ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { target_ulong ptr; uint32_t e2; ptr = env->gdt.base + (env->tr.selector & ~7); e2 = ldl_kernel(ptr + 4); e2 &= ~DESC_TSS_BUSY_MASK; stl_kernel(ptr + 4, e2); } old_eflags = compute_eflags(); if (source == SWITCH_TSS_IRET) old_eflags &= ~NT_MASK; /* save the current state in the old TSS */ if (type & 8) { /* 32 bit */ stl_kernel(env->tr.base + 0x20, next_eip); stl_kernel(env->tr.base + 0x24, old_eflags); 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, next_eip); stw_kernel(env->tr.base + 0x10, old_eflags); 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); } /* now if an exception occurs, it will occurs in the next task context */ if (source == SWITCH_TSS_CALL) { stw_kernel(tss_base, env->tr.selector); new_eflags |= NT_MASK; } /* set busy bit */ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) { target_ulong ptr; uint32_t e2; 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -