| version 1.4, 2004/01/26 15:23:21 | version 1.8, 2004/02/05 16:43:44 | 
| Line 34 | Line 34 | 
 | #include "ctrlxfer.h" | #include "ctrlxfer.h" | 
 |  |  | 
 |  |  | 
 | static void check_segreg(void); |  | 
 |  |  | 
 | /*------------------------------------------------------------------------------ | /*------------------------------------------------------------------------------ | 
 | * JMPfar_pm | * JMPfar_pm | 
 | */ | */ | 
| Line 562  static void | Line 560  static void | 
 | CALLfar_pm_call_gate_same_privilege(selector_t *callgate_sel, selector_t *cs_sel) | CALLfar_pm_call_gate_same_privilege(selector_t *callgate_sel, selector_t *cs_sel) | 
 | { | { | 
 | DWORD sp; | DWORD sp; | 
| DWORD old_eip; | DWORD new_ip; | 
| WORD old_cs; |  | 
 |  |  | 
 | VERBOSE(("CALLfar_pm: SAME-PRIVILEGE")); | VERBOSE(("CALLfar_pm: SAME-PRIVILEGE")); | 
 |  |  | 
 | /* save register */ |  | 
 | old_cs = CPU_CS; |  | 
 | old_eip = CPU_EIP; |  | 
 |  |  | 
 | if (CPU_STAT_SS32) { | if (CPU_STAT_SS32) { | 
 | sp = CPU_ESP; | sp = CPU_ESP; | 
 | } else { | } else { | 
 | sp = CPU_SP; | sp = CPU_SP; | 
 | } | } | 
 |  | new_ip = callgate_sel->desc.u.gate.offset; | 
 |  |  | 
 | if (callgate_sel->desc.type == CPU_SYSDESC_TYPE_CALL_32) { | if (callgate_sel->desc.type == CPU_SYSDESC_TYPE_CALL_32) { | 
 | CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 8); | CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 8); | 
 |  |  | 
| load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL); | PUSH0_32(CPU_CS); | 
| SET_EIP(callgate_sel->desc.u.gate.offset); | PUSH0_32(CPU_EIP); | 
 |  |  | 
| PUSH0_32(old_cs); | load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL); | 
| PUSH0_32(old_eip); | SET_EIP(new_ip); | 
 | } else { | } else { | 
 | CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 4); | CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 4); | 
 |  |  | 
| load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL); | PUSH0_16(CPU_CS); | 
| SET_EIP(callgate_sel->desc.u.gate.offset); | PUSH0_16(CPU_IP); | 
 |  |  | 
| PUSH0_16(old_cs); | load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL); | 
| PUSH0_16(old_eip); | SET_EIP(new_ip); | 
 | } | } | 
 | } | } | 
 |  |  | 
| Line 680  CALLfar_pm_call_gate_more_privilege(sele | Line 674  CALLfar_pm_call_gate_more_privilege(sele | 
 | } | } | 
 |  |  | 
 | load_ss(ss_sel.selector, &ss_sel.desc, ss_sel.desc.dpl); | load_ss(ss_sel.selector, &ss_sel.desc, ss_sel.desc.dpl); | 
| CPU_ESP = tss_esp; | if (CPU_STAT_SS32) { | 
|  | CPU_ESP = tss_esp; | 
|  | } else { | 
|  | CPU_SP = (WORD)tss_esp; | 
|  | } | 
 |  |  | 
 | load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->desc.dpl); | load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->desc.dpl); | 
 | SET_EIP(callgate_sel->desc.u.gate.offset); | SET_EIP(callgate_sel->desc.u.gate.offset); | 
| Line 706  CALLfar_pm_call_gate_more_privilege(sele | Line 704  CALLfar_pm_call_gate_more_privilege(sele | 
 | } | } | 
 |  |  | 
 | load_ss(ss_sel.selector, &ss_sel.desc, ss_sel.desc.dpl); | load_ss(ss_sel.selector, &ss_sel.desc, ss_sel.desc.dpl); | 
| CPU_ESP = tss_esp; | if (CPU_STAT_SS32) { | 
|  | CPU_ESP = tss_esp; | 
|  | } else { | 
|  | CPU_SP = (WORD)tss_esp; | 
|  | } | 
 |  |  | 
 | load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->desc.dpl); | load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->desc.dpl); | 
 | SET_EIP(callgate_sel->desc.u.gate.offset); | SET_EIP(callgate_sel->desc.u.gate.offset); | 
| Line 840  CALLfar_pm_tss(selector_t *tss_sel) | Line 842  CALLfar_pm_tss(selector_t *tss_sel) | 
 | void | void | 
 | RETfar_pm(DWORD nbytes) | RETfar_pm(DWORD nbytes) | 
 | { | { | 
| selector_t ret_sel, ss_sel; | selector_t ret_sel, ss_sel, temp_sel; | 
 | DWORD sp; | DWORD sp; | 
 | DWORD new_ip, new_sp; | DWORD new_ip, new_sp; | 
 | WORD new_cs, new_ss; | WORD new_cs, new_ss; | 
 | int rv; | int rv; | 
 |  | int i; | 
 |  |  | 
 | VERBOSE(("RETfar_pm: old EIP = %04x:%08x, ESP = %04x:%08x, nbytes = %d", CPU_CS, CPU_PREV_EIP, CPU_SS, CPU_ESP, nbytes)); | VERBOSE(("RETfar_pm: old EIP = %04x:%08x, ESP = %04x:%08x, nbytes = %d", CPU_CS, CPU_PREV_EIP, CPU_SS, CPU_ESP, nbytes)); | 
 |  |  | 
| Line 981  RETfar_pm(DWORD nbytes) | Line 984  RETfar_pm(DWORD nbytes) | 
 | SET_EIP(new_ip); | SET_EIP(new_ip); | 
 |  |  | 
 | load_ss(ss_sel.selector, &ss_sel.desc, ret_sel.rpl); | load_ss(ss_sel.selector, &ss_sel.desc, ret_sel.rpl); | 
| CPU_ESP = new_sp + nbytes; | if (CPU_STAT_SS32) { | 
|  | CPU_ESP = new_sp + nbytes; | 
|  | } else { | 
|  | CPU_SP = new_sp + nbytes; | 
|  | } | 
 |  |  | 
 | /* check segment register */ | /* check segment register */ | 
| check_segreg(); | for (i = 0; i < CPU_SEGREG_NUM; i++) { | 
|  | descriptor_t *dp; | 
|  | BOOL valid; | 
|  |  | 
|  | dp = &CPU_STAT_SREG(i); | 
|  | if ((!dp->u.seg.c || !dp->u.seg.ec) | 
|  | && (CPU_STAT_SREG(i).dpl < CPU_STAT_CPL)) { | 
|  | /* segment register is invalid */ | 
|  | CPU_REGS_SREG(i) = 0; | 
|  | CPU_STAT_SREG_CLEAR(i); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | rv = parse_selector(&temp_sel, CPU_REGS_SREG(i)); | 
|  | if (rv < 0) { | 
|  | /* segment register is invalid */ | 
|  | CPU_REGS_SREG(i) = 0; | 
|  | CPU_STAT_SREG_CLEAR(i); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | valid = TRUE; | 
|  | if (!temp_sel.desc.s) { | 
|  | /* system segment */ | 
|  | valid = FALSE; | 
|  | } | 
|  | if (temp_sel.desc.u.seg.c && !temp_sel.desc.u.seg.wr) { | 
|  | /* execute-only code segment */ | 
|  | valid = FALSE; | 
|  | } | 
|  | if (!temp_sel.desc.u.seg.c || !temp_sel.desc.u.seg.ec) { | 
|  | if (CPU_STAT_CPL > temp_sel.desc.dpl) { | 
|  | valid = FALSE; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!valid) { | 
|  | /* segment register is invalid */ | 
|  | CPU_REGS_SREG(i) = 0; | 
|  | CPU_STAT_SREG(i).valid = 0; | 
|  | } | 
|  | } | 
 | } | } | 
 |  |  | 
 | VERBOSE(("RETfar_pm: new EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP)); | VERBOSE(("RETfar_pm: new EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP)); | 
| Line 994  RETfar_pm(DWORD nbytes) | Line 1042  RETfar_pm(DWORD nbytes) | 
 | /*------------------------------------------------------------------------------ | /*------------------------------------------------------------------------------ | 
 | * IRET_pm | * IRET_pm | 
 | */ | */ | 
 |  |  | 
 | #undef  IA32_RETURN_FROM_VM86 |  | 
 |  |  | 
 | static void IRET_pm_nested_task(void); | static void IRET_pm_nested_task(void); | 
 | static void IRET_pm_return_to_vm86(DWORD new_ip, DWORD new_cs, DWORD new_flags); | static void IRET_pm_return_to_vm86(DWORD new_ip, DWORD new_cs, DWORD new_flags); | 
 | #if defined(IA32_RETURN_FROM_VM86) |  | 
 | static void IRET_pm_return_from_vm86(DWORD new_ip, DWORD new_cs, DWORD new_flags); | static void IRET_pm_return_from_vm86(DWORD new_ip, DWORD new_cs, DWORD new_flags); | 
 | #endif  /* IA32_RETURN_FROM_VM86 */ |  | 
 |  |  | 
 | void | void | 
 | IRET_pm(void) | IRET_pm(void) | 
 | { | { | 
 | selector_t iret_sel, ss_sel; | selector_t iret_sel, ss_sel; | 
 |  | descriptor_t *dp; | 
 | DWORD sp; | DWORD sp; | 
 | DWORD stacksize;        /* for RETURN-TO-SAME-PRIVILEGE-LEVEL */ | DWORD stacksize;        /* for RETURN-TO-SAME-PRIVILEGE-LEVEL */ | 
 | DWORD mask = 0; | DWORD mask = 0; | 
| Line 1014  IRET_pm(void) | Line 1058  IRET_pm(void) | 
 | WORD new_cs, new_ss; | WORD new_cs, new_ss; | 
 | int old_cpl; | int old_cpl; | 
 | int rv; | int rv; | 
 |  | int i; | 
 |  |  | 
 | VERBOSE(("IRET_pm: old EIP = %04x:%08x, old ESP = %04x:%08x", CPU_CS, CPU_PREV_EIP, CPU_SS, CPU_ESP)); | VERBOSE(("IRET_pm: old EIP = %04x:%08x, old ESP = %04x:%08x", CPU_CS, CPU_PREV_EIP, CPU_SS, CPU_ESP)); | 
 |  |  | 
| if (CPU_EFLAG & NT_FLAG) { | if (!(CPU_EFLAG & VM_FLAG) && (CPU_EFLAG & NT_FLAG)) { | 
 | /* TASK-RETURN: PE=1, VM=0, NT=1 */ | /* TASK-RETURN: PE=1, VM=0, NT=1 */ | 
 | IRET_pm_nested_task(); | IRET_pm_nested_task(); | 
 | VERBOSE(("IRET_pm: new EIP = %04x:%08x, new ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP)); | VERBOSE(("IRET_pm: new EIP = %04x:%08x, new ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP)); | 
| Line 1043  IRET_pm(void) | Line 1088  IRET_pm(void) | 
 | } | } | 
 | VERBOSE(("IRET_pm: new_ip = %08x, new_cs = %04x, new_eflags = %08x", new_ip, new_cs, new_flags)); | VERBOSE(("IRET_pm: new_ip = %08x, new_cs = %04x, new_eflags = %08x", new_ip, new_cs, new_flags)); | 
 |  |  | 
 | #ifdef  IA32_RETURN_FROM_VM86 |  | 
 | if (CPU_EFLAG & VM_FLAG) { | if (CPU_EFLAG & VM_FLAG) { | 
 | /* RETURN-FROM-VIRTUAL-8086-MODE */ | /* RETURN-FROM-VIRTUAL-8086-MODE */ | 
 | IRET_pm_return_from_vm86(new_ip, new_cs, new_flags); | IRET_pm_return_from_vm86(new_ip, new_cs, new_flags); | 
| Line 1051  IRET_pm(void) | Line 1095  IRET_pm(void) | 
 | CPU_STAT_NERROR = 0; | CPU_STAT_NERROR = 0; | 
 | return; | return; | 
 | } | } | 
 | #endif  /* IA32_RETURN_FROM_VM86 */ |  | 
 |  |  | 
 | if (new_flags & VM_FLAG) { | if (new_flags & VM_FLAG) { | 
 | /* RETURN-TO-VIRTUAL-8086-MODE */ | /* RETURN-TO-VIRTUAL-8086-MODE */ | 
| Line 1168  IRET_pm(void) | Line 1211  IRET_pm(void) | 
 | EXCEPTION(GP_EXCEPTION, 0); | EXCEPTION(GP_EXCEPTION, 0); | 
 | } | } | 
 |  |  | 
 | /* set new register */ |  | 
 | old_cpl = CPU_STAT_CPL; |  | 
 | load_cs(iret_sel.selector, &iret_sel.desc, iret_sel.rpl); |  | 
 | SET_EIP(new_ip); |  | 
 |  |  | 
 | /* set new eflags */ |  | 
 | mask = 0; | mask = 0; | 
 | if (CPU_INST_OP32) | if (CPU_INST_OP32) | 
 | mask |= RF_FLAG; | mask |= RF_FLAG; | 
| Line 1185  IRET_pm(void) | Line 1222  IRET_pm(void) | 
 | mask |= VM_FLAG|VIF_FLAG|VIP_FLAG; | mask |= VM_FLAG|VIF_FLAG|VIP_FLAG; | 
 | } | } | 
 | } | } | 
 |  |  | 
 |  | /* set new register */ | 
 |  | old_cpl = CPU_STAT_CPL; | 
 |  | load_cs(iret_sel.selector, &iret_sel.desc, iret_sel.rpl); | 
 |  | SET_EIP(new_ip); | 
 | set_eflags(new_flags, mask); | set_eflags(new_flags, mask); | 
 |  |  | 
 | if (iret_sel.rpl > old_cpl) { | if (iret_sel.rpl > old_cpl) { | 
 | /* RETURN-OUTER-PRIVILEGE-LEVEL */ | /* RETURN-OUTER-PRIVILEGE-LEVEL */ | 
 |  |  | 
 | load_ss(ss_sel.selector, &ss_sel.desc, iret_sel.rpl); | load_ss(ss_sel.selector, &ss_sel.desc, iret_sel.rpl); | 
| CPU_ESP = new_sp; | if (CPU_STAT_SS32) { | 
|  | CPU_ESP = new_sp; | 
|  | } else { | 
|  | CPU_SP = new_sp; | 
|  | } | 
 |  |  | 
 | /* check segment register */ | /* check segment register */ | 
| check_segreg(); | for (i = 0; i < CPU_SEGREG_NUM; i++) { | 
|  | if ((i != CPU_CS_INDEX) && (i != CPU_SS_INDEX)) { | 
|  | dp = &CPU_STAT_SREG(i); | 
|  | if ((!dp->u.seg.c || !dp->u.seg.ec) | 
|  | && (CPU_STAT_SREG(i).dpl < CPU_STAT_CPL)) { | 
|  | /* segment register is invalid */ | 
|  | CPU_REGS_SREG(i) = 0; | 
|  | CPU_STAT_SREG_CLEAR(i); | 
|  | continue; | 
|  | } | 
|  | } | 
|  | } | 
 | } else { | } else { | 
 | /* RETURN-TO-SAME-PRIVILEGE-LEVEL */ | /* RETURN-TO-SAME-PRIVILEGE-LEVEL */ | 
 | if (CPU_STAT_SS32) { | if (CPU_STAT_SS32) { | 
| Line 1213  IRET_pm(void) | Line 1271  IRET_pm(void) | 
 | static void | static void | 
 | IRET_pm_nested_task(void) | IRET_pm_nested_task(void) | 
 | { | { | 
| selector_t iret_sel; | selector_t tss_sel; | 
 | int rv; | int rv; | 
| WORD new_cs; | WORD new_tss; | 
 |  |  | 
 | VERBOSE(("IRET_pm: TASK-RETURN: PE=1, VM=0, NT=1")); | VERBOSE(("IRET_pm: TASK-RETURN: PE=1, VM=0, NT=1")); | 
 |  |  | 
| if (CPU_STAT_VM86) { | new_tss = get_link_selector_from_tss(); | 
| ia32_panic("IRET_pm: VM86"); | rv = parse_selector(&tss_sel, new_tss); | 
| } | if (rv < 0 || tss_sel.ldt) { | 
|  | VERBOSE(("IRET_pm: parse_selector (selector = %04x, rv = %d, %cDT)", tss_sel.selector, rv, tss_sel.ldt ? 'L' : 'G')); | 
| new_cs = get_link_selector_from_tss(); | EXCEPTION(GP_EXCEPTION, tss_sel.idx); | 
| rv = parse_selector(&iret_sel, new_cs); |  | 
| if (rv < 0 || iret_sel.ldt) { |  | 
| VERBOSE(("IRET_pm: parse_selector (selector = %04x, rv = %d)", iret_sel.selector, rv)); |  | 
| EXCEPTION(GP_EXCEPTION, iret_sel.idx); |  | 
 | } | } | 
 |  |  | 
 | /* check system segment */ | /* check system segment */ | 
| if (iret_sel.desc.s) { | if (tss_sel.desc.s) { | 
| VERBOSE(("IRET_pm: task segment is %d segment", iret_sel.desc.u.seg.c ? "code" : "data")); | VERBOSE(("IRET_pm: task segment is %d segment", tss_sel.desc.u.seg.c ? "code" : "data")); | 
| EXCEPTION(GP_EXCEPTION, iret_sel.idx); | EXCEPTION(GP_EXCEPTION, tss_sel.idx); | 
 | } | } | 
 |  |  | 
| switch (iret_sel.desc.type) { | switch (tss_sel.desc.type) { | 
 | case CPU_SYSDESC_TYPE_TSS_BUSY_16: | case CPU_SYSDESC_TYPE_TSS_BUSY_16: | 
 | case CPU_SYSDESC_TYPE_TSS_BUSY_32: | case CPU_SYSDESC_TYPE_TSS_BUSY_32: | 
 | break; | break; | 
| Line 1246  IRET_pm_nested_task(void) | Line 1300  IRET_pm_nested_task(void) | 
 | VERBOSE(("IRET_pm: task is not busy")); | VERBOSE(("IRET_pm: task is not busy")); | 
 | /*FALLTHROUGH*/ | /*FALLTHROUGH*/ | 
 | default: | default: | 
| VERBOSE(("IRET_pm: invalid descriptor type (type = %d)", iret_sel.desc.type)); | VERBOSE(("IRET_pm: invalid descriptor type (type = %d)", tss_sel.desc.type)); | 
| EXCEPTION(GP_EXCEPTION, iret_sel.idx); | EXCEPTION(GP_EXCEPTION, tss_sel.idx); | 
 | break; | break; | 
 | } | } | 
 |  |  | 
 | /* not present */ | /* not present */ | 
| if (selector_is_not_present(&iret_sel)) { | if (selector_is_not_present(&tss_sel)) { | 
 | VERBOSE(("IRET_pm: tss segment is not present")); | VERBOSE(("IRET_pm: tss segment is not present")); | 
| EXCEPTION(NP_EXCEPTION, iret_sel.idx); | EXCEPTION(NP_EXCEPTION, tss_sel.idx); | 
 | } | } | 
 |  |  | 
| task_switch(&iret_sel, TASK_SWITCH_IRET); | task_switch(&tss_sel, TASK_SWITCH_IRET); | 
 | } | } | 
 |  |  | 
 | /*--- | /*--- | 
| Line 1305  IRET_pm_return_to_vm86(DWORD new_ip, DWO | Line 1359  IRET_pm_return_to_vm86(DWORD new_ip, DWO | 
 | SET_EIP(new_ip); | SET_EIP(new_ip); | 
 | } | } | 
 |  |  | 
 | #ifdef  IA32_RETURN_FROM_VM86 |  | 
 | /*--- | /*--- | 
 | * IRET_pm: VM_FLAG | * IRET_pm: VM_FLAG | 
 | */ | */ | 
| Line 1338  IRET_pm_return_from_vm86(DWORD new_ip, D | Line 1391  IRET_pm_return_from_vm86(DWORD new_ip, D | 
 | VERBOSE(("IRET_pm: trap to virtual-8086 monitor: VM=1, IOPL<3")); | VERBOSE(("IRET_pm: trap to virtual-8086 monitor: VM=1, IOPL<3")); | 
 | EXCEPTION(GP_EXCEPTION, 0); | EXCEPTION(GP_EXCEPTION, 0); | 
 | } | } | 
 | #endif  /* IA32_RETURN_FROM_VM86 */ |  | 
 |  |  | 
 |  |  | 
 | /*----- |  | 
 | * Misc. |  | 
 | */ |  | 
 | static void |  | 
 | check_segreg(void) |  | 
 | { |  | 
 | selector_t temp_sel; |  | 
 | BOOL valid; |  | 
 | int rv; |  | 
 | int i; |  | 
 |  |  | 
 | /* check segment register */ |  | 
 | for (i = 0; i < CPU_SEGREG_NUM; i++) { |  | 
 | if (i == CPU_CS_INDEX || i == CPU_SS_INDEX) |  | 
 | continue; |  | 
 |  |  | 
 | rv = parse_selector(&temp_sel, CPU_REGS_SREG(i)); |  | 
 | if (rv < 0) { |  | 
 | /* segment register is invalid */ |  | 
 | CPU_REGS_SREG(i) = 0; |  | 
 | CPU_STAT_SREG(i).valid = 0; |  | 
 | continue; |  | 
 | } |  | 
 |  |  | 
 | valid = TRUE; |  | 
 | if (!temp_sel.desc.s) { |  | 
 | /* system segment */ |  | 
 | valid = FALSE; |  | 
 | } |  | 
 | if (temp_sel.desc.u.seg.c && !temp_sel.desc.u.seg.wr) { |  | 
 | /* execute-only code segment */ |  | 
 | valid = FALSE; |  | 
 | } |  | 
 | if (!temp_sel.desc.u.seg.c || !temp_sel.desc.u.seg.ec) { |  | 
 | if (CPU_STAT_CPL > temp_sel.desc.dpl) { |  | 
 | valid = FALSE; |  | 
 | } |  | 
 | } |  | 
 |  |  | 
 | if (!valid) { |  | 
 | /* segment register is invalid */ |  | 
 | CPU_REGS_SREG(i) = 0; |  | 
 | CPU_STAT_SREG(i).valid = 0; |  | 
 | } |  | 
 | } |  | 
 | } |  |