--- np2/i386c/ia32/ctrlxfer.c 2004/01/13 16:37:42 1.2 +++ np2/i386c/ia32/ctrlxfer.c 2011/12/20 09:03:28 1.22 @@ -1,5 +1,3 @@ -/* $Id: ctrlxfer.c,v 1.2 2004/01/13 16:37:42 monaka Exp $ */ - /* * Copyright (c) 2003 NONAKA Kimihiro * All rights reserved. @@ -12,8 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -34,22 +30,22 @@ #include "ctrlxfer.h" +/*------------------------------------------------------------------------------ + * JMPfar_pm + */ +static void JMPfar_pm_code_segment(const selector_t *cs_sel, UINT32 new_ip); +static void JMPfar_pm_call_gate(const selector_t *callgate_sel); +static void JMPfar_pm_task_gate(selector_t *taskgate_sel); +static void JMPfar_pm_tss(selector_t *tss_sel); + void -JMPfar_pm(WORD selector, DWORD new_ip) +JMPfar_pm(UINT16 selector, UINT32 new_ip) { selector_t jmp_sel; - selector_t sel2; int rv; - VERBOSE(("JMPfar_pm: EIP = 0x%08x, selector = 0x%04x, new_ip = 0x%08x", CPU_PREV_EIP, selector, new_ip)); - - /* - * IF effective address in the CS, DS, ES, FS, GS, or SS segment is illegal - * OR segment selector in target operand null - * THEN #GP(0); - * FI; - */ - /* XXX */ + VERBOSE(("JMPfar_pm: old EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_PREV_EIP, CPU_SS, CPU_ESP)); + VERBOSE(("JMPfar_pm: selector = 0x%04x, new_ip = 0x%08x", selector, new_ip)); rv = parse_selector(&jmp_sel, selector); if (rv < 0) { @@ -57,53 +53,18 @@ JMPfar_pm(WORD selector, DWORD new_ip) EXCEPTION(GP_EXCEPTION, jmp_sel.idx); } - if (jmp_sel.desc.s) { - /* code segment descriptor */ + if (!SEG_IS_SYSTEM(&jmp_sel.desc)) { VERBOSE(("JMPfar_pm: code or data segment descriptor")); - if (!jmp_sel.desc.u.seg.c) { + /* check segment type */ + if (SEG_IS_DATA(&jmp_sel.desc)) { /* data segment */ VERBOSE(("JMPfar_pm: data segment")); EXCEPTION(GP_EXCEPTION, jmp_sel.idx); } - /* check privilege level */ - if (!jmp_sel.desc.u.seg.ec) { - VERBOSE(("NONCONFORMING-CODE-SEGMENT")); - /* 下巻 p.119 4.8.1.1. */ - if ((jmp_sel.rpl > CPU_STAT_CPL) - || (jmp_sel.desc.dpl != CPU_STAT_CPL)) { - VERBOSE(("JMPfar_pm: RPL(%d) > CPL(%d) or DPL(%d) != CPL(%d)", jmp_sel.rpl, CPU_STAT_CPL, jmp_sel.desc.dpl, CPU_STAT_CPL)); - EXCEPTION(GP_EXCEPTION, jmp_sel.idx); - } - } else { - VERBOSE(("CONFORMING-CODE-SEGMENT")); - /* 下巻 p.120 4.8.1.2. */ - if (jmp_sel.desc.dpl > CPU_STAT_CPL) { - VERBOSE(("JMPfar_pm: DPL(%d) > CPL(%d)", jmp_sel.desc.dpl, CPU_STAT_CPL)); - EXCEPTION(GP_EXCEPTION, jmp_sel.idx); - } - } - - /* not present */ - if (selector_is_not_present(&jmp_sel)) { - VERBOSE(("JMPfar_pm: selector is not present")); - EXCEPTION(NP_EXCEPTION, jmp_sel.idx); - } - - if (!CPU_INST_OP32) { - new_ip &= 0xffff; - } - - /* out of range */ - if (new_ip > jmp_sel.desc.u.seg.limit) { - VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, jmp_sel.desc.u.seg.limit)); - EXCEPTION(GP_EXCEPTION, 0); - } - - VERBOSE(("JMPfar_pm: new CS = %04x, EIP = %08x", jmp_sel.selector, new_ip)); - load_cs(jmp_sel.selector, &jmp_sel.desc, CPU_STAT_CPL); - SET_EIP(new_ip); + /* code segment descriptor */ + JMPfar_pm_code_segment(&jmp_sel, new_ip); } else { /* system descriptor */ VERBOSE(("JMPfar_pm: system descriptor")); @@ -111,159 +72,16 @@ JMPfar_pm(WORD selector, DWORD new_ip) switch (jmp_sel.desc.type) { case CPU_SYSDESC_TYPE_CALL_16: case CPU_SYSDESC_TYPE_CALL_32: - VERBOSE(("CALL-GATE")); - - /* check privilege level */ - if ((jmp_sel.desc.dpl < CPU_STAT_CPL) - || (jmp_sel.desc.dpl < jmp_sel.rpl)) { - VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d) or DPL(%d) < RPL(%d)", jmp_sel.desc.dpl, CPU_STAT_CPL, jmp_sel.desc.dpl, jmp_sel.rpl)); - EXCEPTION(GP_EXCEPTION, jmp_sel.idx); - } - - /* not present */ - if (selector_is_not_present(&jmp_sel)) { - VERBOSE(("JMPfar_pm: selector is not present")); - EXCEPTION(NP_EXCEPTION, jmp_sel.idx); - } - - /* parse call gate selector */ - rv = parse_selector(&sel2, jmp_sel.desc.u.gate.selector); - if (rv < 0) { - VERBOSE(("JMPfar_pm: parse_selector (selector = %04x, rv = %d)", jmp_sel.desc.u.gate.selector, rv)); - EXCEPTION(GP_EXCEPTION, sel2.idx); - } - - /* check code segment descriptor */ - if (!sel2.desc.s || !sel2.desc.u.seg.c) { - VERBOSE(("JMPfar_pm: not code segment (%s, %s)", sel2.desc.s ? "code/data" : "system", sel2.desc.u.seg.c ? "code" : "data")); - EXCEPTION(GP_EXCEPTION, sel2.idx); - } - - /* check privilege level */ - if (!sel2.desc.u.seg.ec) { - /* 下巻 p.119 4.8.1.1. */ - if ((sel2.rpl > CPU_STAT_CPL) - || (sel2.desc.dpl != CPU_STAT_CPL)) { - VERBOSE(("JMPfar_pm: RPL(%d) > CPL(%d) or DPL(%d) != CPL(%d)", sel2.rpl, CPU_STAT_CPL, sel2.desc.dpl, CPU_STAT_CPL)); - EXCEPTION(GP_EXCEPTION, sel2.idx); - } - } else { - /* 下巻 p.120 4.8.1.2. */ - if (sel2.desc.dpl > CPU_STAT_CPL) { - VERBOSE(("JMPfar_pm: DPL(%d) > CPL(%d)", sel2.desc.dpl, CPU_STAT_CPL)); - EXCEPTION(GP_EXCEPTION, sel2.idx); - } - } - - /* not present */ - if (selector_is_not_present(&sel2)) { - VERBOSE(("JMPfar_pm: selector is not present")); - EXCEPTION(NP_EXCEPTION, sel2.idx); - } - - new_ip = jmp_sel.desc.u.gate.offset; - if (jmp_sel.desc.type == CPU_SYSDESC_TYPE_CALL_16) { - new_ip &= 0xffff; - } - - /* out of range */ - if (new_ip > sel2.desc.u.seg.limit) { - VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, sel2.desc.u.seg.limit)); - EXCEPTION(GP_EXCEPTION, 0); - } - - VERBOSE(("JMPfar_pm: new CS = %04x, EIP = %08x", sel2.selector, new_ip)); - load_cs(sel2.selector, &sel2.desc, CPU_STAT_CPL); - SET_EIP(new_ip); + JMPfar_pm_call_gate(&jmp_sel); break; - /* - * 中巻 p.373 JMP 命令 - * - * JMP 命令でタスク・スイッチを実行するときは EFLAGS レジスタに - * ネストされたタスク・フラグ (NT) がセットされず、新しい TSS の - * 以前のタスク・リンク・フィールドに前のタスクの TSS セレクタが - * ロードされないので注意されたい。したがって、前のタスクへの - * リターンは IRET 命令の実行では実現できない。JMP 命令で - * タスク・スイッチを実行するのは、その点で CALL 命令と異なる。 - * すなわち、CALL 命令は NT フラグをセットし、以前の - * タスク・リンク情報をセーブするので、IRET 命令でのコール元 - * タスクへのリターンが可能になる。 - */ case CPU_SYSDESC_TYPE_TASK: - VERBOSE(("TASK-GATE")); - - /* check privilege level */ - if ((jmp_sel.desc.dpl < CPU_STAT_CPL) - || (jmp_sel.desc.dpl < jmp_sel.rpl)) { - VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d) or DPL(%d) < RPL(%d)", jmp_sel.desc.dpl, CPU_STAT_CPL, jmp_sel.desc.dpl, jmp_sel.rpl)); - EXCEPTION(GP_EXCEPTION, jmp_sel.idx); - } - - /* not present */ - if (selector_is_not_present(&jmp_sel)) { - VERBOSE(("JMPfar_pm: selector is not present")); - EXCEPTION(NP_EXCEPTION, jmp_sel.idx); - } - - /* parse call tss selector */ - rv = parse_selector(&sel2, jmp_sel.desc.u.gate.selector); - if (rv < 0 || sel2.ldt) { - VERBOSE(("JMPfar_pm: parse_selector (selector = %04x, rv = %d, %s)", jmp_sel.desc.u.gate.selector, rv, sel2.ldt ? "LDT" : "GDT")); - EXCEPTION(GP_EXCEPTION, sel2.idx); - } - - /* check descriptor type */ - switch (sel2.desc.type) { - case CPU_SYSDESC_TYPE_TSS_16: - case CPU_SYSDESC_TYPE_TSS_32: - break; - - default: - VERBOSE(("JMPfar_pm: invalid descriptor type (type = %d)", sel2.desc.type)); - EXCEPTION(GP_EXCEPTION, sel2.idx); - break; - } - - /* not present */ - if (selector_is_not_present(&sel2)) { - VERBOSE(("JMPfar_pm: selector is not present")); - EXCEPTION(NP_EXCEPTION, sel2.idx); - } - - task_switch(&sel2, TASK_SWITCH_JMP); - - /* out of range */ - if (CPU_EIP > CPU_STAT_CS_LIMIT) { - VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", CPU_EIP, CPU_STAT_CS_LIMIT)); - EXCEPTION(GP_EXCEPTION, 0); - } + JMPfar_pm_task_gate(&jmp_sel); break; case CPU_SYSDESC_TYPE_TSS_16: case CPU_SYSDESC_TYPE_TSS_32: - VERBOSE(("TASK-STATE-SEGMENT")); - - /* check privilege level */ - if ((jmp_sel.desc.dpl < CPU_STAT_CPL) - || (jmp_sel.desc.dpl < jmp_sel.rpl)) { - VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d) or DPL(%d) < RPL(%d)", jmp_sel.desc.dpl, CPU_STAT_CPL, jmp_sel.desc.dpl, jmp_sel.rpl)); - EXCEPTION(TS_EXCEPTION, jmp_sel.idx); - } - - /* not present */ - if (selector_is_not_present(&jmp_sel)) { - VERBOSE(("JMPfar_pm: selector is not present")); - EXCEPTION(NP_EXCEPTION, jmp_sel.idx); - } - - task_switch(&jmp_sel, TASK_SWITCH_JMP); - - /* out of range */ - if (CPU_EIP > CPU_STAT_CS_LIMIT) { - VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", CPU_EIP, CPU_STAT_CS_LIMIT)); - EXCEPTION(GP_EXCEPTION, 0); - } + JMPfar_pm_tss(&jmp_sel); break; case CPU_SYSDESC_TYPE_TSS_BUSY_16: @@ -271,331 +89,292 @@ JMPfar_pm(WORD selector, DWORD new_ip) VERBOSE(("JMPfar_pm: task is busy")); /*FALLTHROUGH*/ default: - VERBOSE(("JMPfar_pm: invalid descriptor type (type = %d)", sel2.desc.type)); + VERBOSE(("JMPfar_pm: invalid descriptor type (type = %d)", jmp_sel.desc.type)); EXCEPTION(GP_EXCEPTION, jmp_sel.idx); break; } } + + VERBOSE(("JMPfar_pm: new EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP)); } -/* - * 4.3.6. 特権レベル間のコール操作 - * - * 1. アクセス権のチェック(特権チェック)を実行する。 - * 2. SS, ESP, CS, EIP の各レジスタの現在地を一時的に内部にセーブする。 - * 3. TSS レジスタに格納されている新しいスタック(すなわち、現在コールされている - * 特権レベル用のスタック)のセグメントレジスタとスタックポインタを - * SS レジスタと ESP レジスタにロードし、新しいスタックに切り替える。 - * 4. コール元プロシージャのスタックに対して一時的にセーブしておいた SS 値と - * ESP 値を、この新しいスタックにプッシュする。 - * 5. コール元プロシージャのスタックからパラメータをコピーする。新しいスタック - * にコピーされるパラメータの数は、コール・ゲート・ディスクリプタ内の値で - * 決まる。 - * 6. コール元プロシージャに対して一時的にセーブしておいた CS 値と EIP 値を、 - * 新しいスタックにプッシュする。 - * 7. 新しいコード・セグメントのセグメント・セレクタと新しい命令ポインタを、 - * コール・ゲートから CS レジスタと EIP レジスタにそれぞれロードする。 - * 8. コールされたプロシージャの実行を新しい特権レベルで開始する。 +/*--- + * JMPfar: code segment */ -void -CALLfar_pm(WORD selector, DWORD new_ip) +static void +JMPfar_pm_code_segment(const selector_t *cs_sel, UINT32 new_ip) { - selector_t call_sel; - selector_t sel2; + + VERBOSE(("JMPfar_pm: CODE-SEGMENT")); + + /* check privilege level */ + if (!SEG_IS_CONFORMING_CODE(&cs_sel->desc)) { + VERBOSE(("JMPfar_pm: NON-CONFORMING-CODE-SEGMENT")); + /* 下巻 p.119 4.8.1.1. */ + if (cs_sel->rpl > CPU_STAT_CPL) { + VERBOSE(("JMPfar_pm: RPL(%d) > CPL(%d)", cs_sel->rpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, cs_sel->idx); + } + if (cs_sel->desc.dpl != CPU_STAT_CPL) { + VERBOSE(("JMPfar_pm: DPL(%d) != CPL(%d)", cs_sel->desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, cs_sel->idx); + } + } else { + VERBOSE(("JMPfar_pm: CONFORMING-CODE-SEGMENT")); + /* 下巻 p.120 4.8.1.2. */ + if (cs_sel->desc.dpl > CPU_STAT_CPL) { + VERBOSE(("JMPfar_pm: DPL(%d) > CPL(%d)", cs_sel->desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, cs_sel->idx); + } + } + + /* not present */ + if (selector_is_not_present(cs_sel)) { + VERBOSE(("JMPfar_pm: code selector is not present")); + EXCEPTION(NP_EXCEPTION, cs_sel->idx); + } + + /* out of range */ + if (new_ip > cs_sel->desc.u.seg.limit) { + VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, cs_sel->desc.u.seg.limit)); + EXCEPTION(GP_EXCEPTION, 0); + } + + load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL); + CPU_EIP = new_ip; +} + +/*--- + * JMPfar: call gate + */ +static void +JMPfar_pm_call_gate(const selector_t *callgate_sel) +{ + selector_t cs_sel; int rv; - VERBOSE(("CALLfar_pm: EIP = 0x%08x, selector = 0x%04x, new_ip = 0x%08x", CPU_PREV_EIP, selector, new_ip)); + VERBOSE(("JMPfar_pm: CALL-GATE")); - rv = parse_selector(&call_sel, selector); - if (rv < 0) { - EXCEPTION(GP_EXCEPTION, call_sel.idx); + /* check privilege level */ + if (callgate_sel->desc.dpl < CPU_STAT_CPL) { + VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d)", callgate_sel->desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, callgate_sel->idx); + } + if (callgate_sel->desc.dpl < callgate_sel->rpl) { + VERBOSE(("JMPfar_pm: DPL(%d) < RPL(%d)", callgate_sel->desc.dpl, callgate_sel->rpl)); + EXCEPTION(GP_EXCEPTION, callgate_sel->idx); } - if (call_sel.desc.s) { - /* code segment descriptor */ - if (!call_sel.desc.u.seg.c) { - /* data segment */ - EXCEPTION(GP_EXCEPTION, call_sel.idx); - } + /* not present */ + if (selector_is_not_present(callgate_sel)) { + VERBOSE(("JMPfar_pm: call gate selector is not present")); + EXCEPTION(NP_EXCEPTION, callgate_sel->idx); + } - /* check privilege level */ - if (!call_sel.desc.u.seg.ec) { - VERBOSE(("NONCONFORMING-CODE-SEGMENT")); + /* parse code segment selector */ + rv = parse_selector(&cs_sel, callgate_sel->desc.u.gate.selector); + if (rv < 0) { + VERBOSE(("JMPfar_pm: parse_selector (selector = %04x, rv = %d)", callgate_sel->desc.u.gate.selector, rv)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } - /* 下巻 p.119 4.8.1.1. */ - if ((call_sel.rpl > CPU_STAT_CPL) - || (call_sel.desc.dpl != CPU_STAT_CPL)) { - EXCEPTION(GP_EXCEPTION, call_sel.idx); - } - } else { - VERBOSE(("CONFORMING-CODE-SEGMENT")); + /* check segment type */ + if (SEG_IS_SYSTEM(&cs_sel.desc)) { + VERBOSE(("JMPfar_pm: code segment is system segment")); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + if (SEG_IS_DATA(&cs_sel.desc)) { + VERBOSE(("JMPfar_pm: code segment is data segment")); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } - /* 下巻 p.120 4.8.1.2. */ - if (call_sel.desc.dpl > CPU_STAT_CPL) { - EXCEPTION(GP_EXCEPTION, call_sel.idx); - } + /* check privilege level */ + if (!SEG_IS_CONFORMING_CODE(&cs_sel.desc)) { + /* 下巻 p.119 4.8.1.1. */ + if (cs_sel.rpl > CPU_STAT_CPL) { + VERBOSE(("JMPfar_pm: RPL(%d) > CPL(%d)", cs_sel.rpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + if (cs_sel.desc.dpl != CPU_STAT_CPL) { + VERBOSE(("JMPfar_pm: DPL(%d) != CPL(%d)", cs_sel.desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); } - - /* not present */ - if (selector_is_not_present(&call_sel)) { - EXCEPTION(NP_EXCEPTION, call_sel.idx); + } else { + /* 下巻 p.120 4.8.1.2. */ + if (cs_sel.desc.dpl > CPU_STAT_CPL) { + VERBOSE(("JMPfar_pm: DPL(%d) > CPL(%d)", cs_sel.desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); } + } - if (CPU_INST_OP32) { - CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8); - /* out of range */ - if (new_ip > call_sel.desc.u.seg.limit) { - EXCEPTION(GP_EXCEPTION, 0); - } + /* not present */ + if (selector_is_not_present(&cs_sel)) { + VERBOSE(("JMPfar_pm: code selector is not present")); + EXCEPTION(NP_EXCEPTION, cs_sel.idx); + } - PUSH0_32(CPU_CS); - PUSH0_32(CPU_EIP); - } else { - CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 4); - /* out of range */ - if (new_ip > call_sel.desc.u.seg.limit) { - EXCEPTION(GP_EXCEPTION, 0); - } + /* out of range */ + if (callgate_sel->desc.u.gate.offset > cs_sel.desc.u.seg.limit) { + VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", callgate_sel->desc.u.gate.offset, cs_sel.desc.u.seg.limit)); + EXCEPTION(GP_EXCEPTION, 0); + } - PUSH0_16(CPU_CS); - PUSH0_16(CPU_IP); - } + load_cs(cs_sel.selector, &cs_sel.desc, CPU_STAT_CPL); + CPU_EIP = callgate_sel->desc.u.gate.offset; +} - if (!call_sel.desc.d) { - new_ip &= 0xffff; - } +/*--- + * JMPfar: task gate + */ +static void +JMPfar_pm_task_gate(selector_t *taskgate_sel) +{ + selector_t tss_sel; + int rv; - load_cs(call_sel.selector, &call_sel.desc, CPU_STAT_CPL); - SET_EIP(new_ip); - } else { - DWORD temp_eip, temp_esp; - WORD temp_cs, temp_ss; + VERBOSE(("JMPfar_pm: TASK-GATE")); - /* system descriptor */ - switch (call_sel.desc.type) { - case CPU_SYSDESC_TYPE_CALL_16: - case CPU_SYSDESC_TYPE_CALL_32: - VERBOSE(("CALL-GATE")); + /* check privilege level */ + if (taskgate_sel->desc.dpl < CPU_STAT_CPL) { + VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d)", taskgate_sel->desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, taskgate_sel->idx); + } + if (taskgate_sel->desc.dpl < taskgate_sel->rpl) { + VERBOSE(("JMPfar_pm: DPL(%d) < RPL(%d)", taskgate_sel->desc.dpl, taskgate_sel->rpl)); + EXCEPTION(GP_EXCEPTION, taskgate_sel->idx); + } - /* check privilege level */ - if ((call_sel.desc.dpl < CPU_STAT_CPL) - || (call_sel.desc.dpl < call_sel.rpl)) { - EXCEPTION(GP_EXCEPTION, call_sel.idx); - } + /* not present */ + if (selector_is_not_present(taskgate_sel)) { + VERBOSE(("JMPfar_pm: selector is not present")); + EXCEPTION(NP_EXCEPTION, taskgate_sel->idx); + } - /* not present */ - if (selector_is_not_present(&call_sel)) { - EXCEPTION(NP_EXCEPTION, call_sel.idx); - } + /* parse tss selector */ + rv = parse_selector(&tss_sel, taskgate_sel->desc.u.gate.selector); + if (rv < 0 || tss_sel.ldt) { + VERBOSE(("JMPfar_pm: parse_selector (selector = %04x, rv = %d, %cDT)", taskgate_sel->desc.u.gate.selector, rv, tss_sel.ldt ? 'L' : 'G')); + EXCEPTION(GP_EXCEPTION, tss_sel.idx); + } - /* parse code segment descriptor */ - rv = parse_selector(&sel2, call_sel.desc.u.gate.selector); - if (rv < 0) { - EXCEPTION(GP_EXCEPTION, sel2.idx); - } + /* check descriptor type */ + switch (tss_sel.desc.type) { + case CPU_SYSDESC_TYPE_TSS_16: + case CPU_SYSDESC_TYPE_TSS_32: + break; + + case CPU_SYSDESC_TYPE_TSS_BUSY_16: + case CPU_SYSDESC_TYPE_TSS_BUSY_32: + VERBOSE(("JMPfar_pm: task is busy")); + /*FALLTHROUGH*/ + default: + VERBOSE(("JMPfar_pm: invalid descriptor type (type = %d)", tss_sel.desc.type)); + EXCEPTION(GP_EXCEPTION, tss_sel.idx); + break; + } - /* check code segment & privilege level */ - if (!sel2.desc.s - || !sel2.desc.u.seg.c - || sel2.desc.dpl > CPU_STAT_CPL) { - EXCEPTION(GP_EXCEPTION, sel2.idx); - } + /* not present */ + if (selector_is_not_present(&tss_sel)) { + VERBOSE(("JMPfar_pm: selector is not present")); + EXCEPTION(NP_EXCEPTION, tss_sel.idx); + } - /* not present */ - if (selector_is_not_present(&sel2)) { - EXCEPTION(NP_EXCEPTION, sel2.idx); - } + task_switch(&tss_sel, TASK_SWITCH_JMP); - /* save register */ - temp_cs = CPU_CS; - temp_ss = CPU_SS; - temp_eip = CPU_EIP; - temp_esp = CPU_ESP; - if (sel2.desc.type == CPU_SYSDESC_TYPE_CALL_16) { - temp_eip &= 0xffff; - temp_esp &= 0xffff; - } + /* out of range */ + if (CPU_EIP > CPU_STAT_CS_LIMIT) { + VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", CPU_EIP, CPU_STAT_CS_LIMIT)); + EXCEPTION(GP_EXCEPTION, 0); + } +} - new_ip = call_sel.desc.u.gate.offset; +/*--- + * JMPfar: TSS + */ +static void +JMPfar_pm_tss(selector_t *tss_sel) +{ - /* out of range */ - if (new_ip > sel2.desc.u.seg.limit) { - EXCEPTION(GP_EXCEPTION, 0); - } + VERBOSE(("JMPfar_pm: TASK-STATE-SEGMENT")); - if (!sel2.desc.u.seg.ec - && (sel2.desc.dpl < CPU_STAT_CPL)) { - DWORD param[32]; // copy param - selector_t ss_sel; - DWORD tss_esp; - WORD tss_ss; - BYTE i; - - VERBOSE(("MORE-PRIVILEGE")); - - get_stack_from_tss(sel2.desc.dpl, &tss_ss, &tss_esp); - - /* parse stack segment descriptor */ - rv = parse_selector(&ss_sel, tss_ss); - if (rv < 0) { - EXCEPTION(TS_EXCEPTION, ss_sel.idx); - } - - /* check privilege level */ - if ((ss_sel.rpl != sel2.desc.dpl) - || (ss_sel.desc.dpl != sel2.desc.dpl) - || !ss_sel.desc.s - || !ss_sel.desc.u.seg.wr) { - EXCEPTION(TS_EXCEPTION, ss_sel.idx); - } - - /* not present */ - if (selector_is_not_present(&ss_sel)) { - EXCEPTION(NP_EXCEPTION, ss_sel.idx); - } - - if (call_sel.desc.type == CPU_SYSDESC_TYPE_CALL_32){ - CHECK_STACK_PUSH(&ss_sel.desc, tss_esp, 16 + sel2.desc.u.gate.count * 4); - - /* dump param */ - for (i = 0; i < sel2.desc.u.gate.count; i++) { - param[i] = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP + i * 4); - } - - load_ss(tss_ss, &ss_sel.desc, ss_sel.desc.dpl); - CPU_ESP = tss_esp; - load_cs(sel2.selector, &sel2.desc, sel2.desc.dpl); - SET_EIP(new_ip); - - PUSH0_32(temp_ss); - PUSH0_32(temp_esp); - - /* restore param */ - for (i = sel2.desc.u.gate.count; i != 0; i--) { - PUSH0_32(param[i - 1]); - } - - PUSH0_32(temp_cs); - PUSH0_32(temp_eip); - } else { - CHECK_STACK_PUSH(&ss_sel.desc, tss_esp, 8 + sel2.desc.u.gate.count * 2); - - new_ip &= 0xffff; - - /* dump param */ - for (i = 0; i < sel2.desc.u.gate.count; i++) { - param[i] = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP + i * 2); - } - - load_ss(tss_ss, &ss_sel.desc, ss_sel.desc.dpl); - CPU_ESP = tss_esp & 0xffff; - load_cs(sel2.selector, &sel2.desc, sel2.desc.dpl); - SET_EIP(new_ip); - - PUSH0_16(temp_ss); - PUSH0_16(temp_esp); - - /* restore param */ - for (i = sel2.desc.u.gate.count; i != 0; i--) { - PUSH0_16(param[i - 1]); - } - - PUSH0_16(temp_cs); - PUSH0_16(temp_eip); - } - } else { - VERBOSE(("SAME-PRIVILEGE")); - - if (call_sel.desc.type == CPU_SYSDESC_TYPE_CALL_32){ - CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8); - - load_cs(sel2.selector, &sel2.desc, CPU_STAT_CPL); - SET_EIP(new_ip); - - PUSH0_32(temp_cs); - PUSH0_32(temp_eip); - } else { - CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 4); - - load_cs(sel2.selector, &sel2.desc, CPU_STAT_CPL); - new_ip &= 0xffff; - SET_EIP(new_ip); - - PUSH0_16(temp_cs); - PUSH0_16(temp_eip); - } - } - break; + /* check privilege level */ + if (tss_sel->desc.dpl < CPU_STAT_CPL) { + VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d)", tss_sel->desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, tss_sel->idx); + } + if (tss_sel->desc.dpl < tss_sel->rpl) { + VERBOSE(("JMPfar_pm: DPL(%d) < RPL(%d)", tss_sel->desc.dpl, tss_sel->rpl)); + EXCEPTION(GP_EXCEPTION, tss_sel->idx); + } - case CPU_SYSDESC_TYPE_TASK: - VERBOSE(("TASK-GATE")); + /* not present */ + if (selector_is_not_present(tss_sel)) { + VERBOSE(("JMPfar_pm: selector is not present")); + EXCEPTION(NP_EXCEPTION, tss_sel->idx); + } - /* check privilege level */ - if ((call_sel.desc.dpl < CPU_STAT_CPL) - || (call_sel.desc.dpl < call_sel.rpl)) { - EXCEPTION(GP_EXCEPTION, call_sel.idx); - } + task_switch(tss_sel, TASK_SWITCH_JMP); - /* not present */ - if (selector_is_not_present(&call_sel)) { - EXCEPTION(NP_EXCEPTION, call_sel.idx); - } + /* out of range */ + if (CPU_EIP > CPU_STAT_CS_LIMIT) { + VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", CPU_EIP, CPU_STAT_CS_LIMIT)); + EXCEPTION(GP_EXCEPTION, 0); + } +} - /* tss descriptor */ - rv = parse_selector(&sel2, call_sel.desc.u.gate.selector); - if (rv < 0 || sel2.ldt) { - EXCEPTION(GP_EXCEPTION, sel2.idx); - } - /* check descriptor type */ - switch (sel2.desc.type) { - case CPU_SYSDESC_TYPE_TSS_16: - case CPU_SYSDESC_TYPE_TSS_32: - break; - - case CPU_SYSDESC_TYPE_TSS_BUSY_16: - case CPU_SYSDESC_TYPE_TSS_BUSY_32: - VERBOSE(("CALLfar_pm: task is busy")); - /*FALLTHROUGH*/ - default: - EXCEPTION(GP_EXCEPTION, sel2.idx); - break; - } +/*------------------------------------------------------------------------------ + * CALLfar_pm + */ +static void CALLfar_pm_code_segment(const selector_t *cs_sel, UINT32 new_ip); +static void CALLfar_pm_call_gate(const selector_t *callgate_sel); +static void CALLfar_pm_task_gate(selector_t *taskgate_sel); +static void CALLfar_pm_tss(selector_t *tss_sel); - /* not present */ - if (selector_is_not_present(&sel2)) { - EXCEPTION(NP_EXCEPTION, sel2.idx); - } +void +CALLfar_pm(UINT16 selector, UINT32 new_ip) +{ + selector_t call_sel; + int rv; - task_switch(&sel2, TASK_SWITCH_CALL); + VERBOSE(("CALLfar_pm: old EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_PREV_EIP, CPU_SS, CPU_ESP)); + VERBOSE(("CALLfar_pm: selector = 0x%04x, new_ip = 0x%08x", selector, new_ip)); - /* out of range */ - if (CPU_EIP > CPU_STAT_CS_LIMIT) { - EXCEPTION(GP_EXCEPTION, 0); - } - break; + rv = parse_selector(&call_sel, selector); + if (rv < 0) { + VERBOSE(("CALLfar_pm: parse_selector (selector = %04x, rv = %d)", selector, rv)); + EXCEPTION(GP_EXCEPTION, call_sel.idx); + } - case CPU_SYSDESC_TYPE_TSS_16: - case CPU_SYSDESC_TYPE_TSS_32: - VERBOSE(("TASK-STATE-SEGMENT")); + if (!SEG_IS_SYSTEM(&call_sel.desc)) { + /* code or data segment descriptor */ + VERBOSE(("CALLfar_pm: code or data segment descriptor")); - /* check privilege level */ - if ((call_sel.desc.dpl < CPU_STAT_CPL) - || (call_sel.desc.dpl < call_sel.rpl)) { - EXCEPTION(GP_EXCEPTION, call_sel.idx); - } + if (SEG_IS_DATA(&call_sel.desc)) { + /* data segment */ + VERBOSE(("CALLfar_pm: data segment")); + EXCEPTION(GP_EXCEPTION, call_sel.idx); + } - /* not present */ - if (selector_is_not_present(&call_sel)) { - EXCEPTION(NP_EXCEPTION, call_sel.idx); - } + /* code segment descriptor */ + CALLfar_pm_code_segment(&call_sel, new_ip); + } else { + /* system descriptor */ + VERBOSE(("CALLfar_pm: system descriptor")); - task_switch(&call_sel, TASK_SWITCH_CALL); + switch (call_sel.desc.type) { + case CPU_SYSDESC_TYPE_CALL_16: + case CPU_SYSDESC_TYPE_CALL_32: + CALLfar_pm_call_gate(&call_sel); + break; - /* out of range */ - if (CPU_EIP > CPU_STAT_CS_LIMIT) { - EXCEPTION(GP_EXCEPTION, 0); - } + case CPU_SYSDESC_TYPE_TASK: + CALLfar_pm_task_gate(&call_sel); + break; + + case CPU_SYSDESC_TYPE_TSS_16: + case CPU_SYSDESC_TYPE_TSS_32: + CALLfar_pm_tss(&call_sel); break; case CPU_SYSDESC_TYPE_TSS_BUSY_16: @@ -603,427 +382,1043 @@ CALLfar_pm(WORD selector, DWORD new_ip) VERBOSE(("CALLfar_pm: task is busy")); /*FALLTHROUGH*/ default: + VERBOSE(("CALLfar_pm: invalid descriptor type (type = %d)", call_sel.desc.type)); EXCEPTION(GP_EXCEPTION, call_sel.idx); break; } } + + VERBOSE(("CALLfar_pm: new EIP = %04x:%08x, new ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP)); } -/* - * 4.3.6. 特権レベル間のリターン操作 - * - * 1. 特権チェックを実行する。 - * 2. CS レジスタと EIP レジスタにコール前の値をリストアする。 - * 3. RET 命令にオプション引き数の n がある場合は、パラメータをスタックから - * 開放するため、n オペランドで指定されたバイト数だけスタック・ポインタを - * インクリメントする。コール・ゲート・ディスクリプタが、スタック間で - * 1 つ以上のパラメータをコピーするよう指定している場合は、RET n 命令を - * 使用して両スタックからパラメータを開放しなければならない。n オペランド - * には、各スタック壌でパラメータが占有するバイト数を指定する。 - * リターン時に、プロセッサは各スタックに対して n だけ ESP をインクリメント - * し、これらのパラメータをスタックから効率よく削除する。 - * 4. SS レジスタと ESP レジスタに、コール前の値をリストアする。これで、 - * コール元プロシージャのスタックへ切り替えられる。 - * 5. RET 命令にオプション引き数の n がある場合は、パラメータをスタックから - * 開放するため、n オペランドで指定されたバイト数だけスタック・ポインタを - * インクリメントする(ステップ 3 の説明を参照)。 - * 6. コール元プロシージャの実行を再開する。 +/*--- + * CALLfar_pm: code segment */ -void -RETfar_pm(DWORD nbytes) +static void +CALLfar_pm_code_segment(const selector_t *cs_sel, UINT32 new_ip) { - selector_t ret_sel; - int rv; - DWORD new_ip; - WORD selector; + UINT32 sp; + + VERBOSE(("CALLfar_pm: CODE-SEGMENT")); - VERBOSE(("RETfar_pm: EIP = 0x%08x, nbytes = %d", CPU_PREV_EIP, nbytes)); + /* check privilege level */ + if (!SEG_IS_CONFORMING_CODE(&cs_sel->desc)) { + VERBOSE(("CALLfar_pm: NON-CONFORMING-CODE-SEGMENT")); + /* 下巻 p.119 4.8.1.1. */ + if (cs_sel->rpl > CPU_STAT_CPL) { + VERBOSE(("CALLfar_pm: RPL(%d) > CPL(%d)", cs_sel->rpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, cs_sel->idx); + } + if (cs_sel->desc.dpl != CPU_STAT_CPL) { + VERBOSE(("CALLfar_pm: DPL(%d) != CPL(%d)", cs_sel->desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, cs_sel->idx); + } + } else { + VERBOSE(("CALLfar_pm: CONFORMING-CODE-SEGMENT")); + /* 下巻 p.120 4.8.1.2. */ + if (cs_sel->desc.dpl > CPU_STAT_CPL) { + VERBOSE(("CALLfar_pm: DPL(%d) > CPL(%d)", cs_sel->desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, cs_sel->idx); + } + } + + /* not present */ + if (selector_is_not_present(cs_sel)) { + VERBOSE(("CALLfar_pm: selector is not present")); + EXCEPTION(NP_EXCEPTION, cs_sel->idx); + } + if (CPU_STAT_SS32) { + sp = CPU_ESP; + } else { + sp = CPU_SP; + } if (CPU_INST_OP32) { - CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8 + nbytes); - if (CPU_STAT_SS32) { - new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP); - selector = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP + 4); - } else { - new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_SP); - selector = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_SP + 4); + SS_PUSH_CHECK(sp, 8); + + /* out of range */ + if (new_ip > cs_sel->desc.u.seg.limit) { + VERBOSE(("CALLfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, cs_sel->desc.u.seg.limit)); + EXCEPTION(GP_EXCEPTION, 0); } + + PUSH0_32(CPU_CS); + PUSH0_32(CPU_EIP); } else { - CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 4 + nbytes); - if (CPU_STAT_SS32) { - new_ip = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP); - selector = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP + 2); - } else { - new_ip = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_SP); - selector = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_SP + 2); + SS_PUSH_CHECK(sp, 4); + + /* out of range */ + if (new_ip > cs_sel->desc.u.seg.limit) { + VERBOSE(("CALLfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, cs_sel->desc.u.seg.limit)); + EXCEPTION(GP_EXCEPTION, 0); } + + PUSH0_16(CPU_CS); + PUSH0_16(CPU_IP); + } + + load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL); + CPU_EIP = new_ip; +} + +/*--- + * CALLfar_pm: call gate + */ +static void CALLfar_pm_call_gate_same_privilege(const selector_t *call_sel, selector_t *cs_sel); +static void CALLfar_pm_call_gate_more_privilege(const selector_t *call_sel, selector_t *cs_sel); + +static void +CALLfar_pm_call_gate(const selector_t *callgate_sel) +{ + selector_t cs_sel; + int rv; + + VERBOSE(("CALLfar_pm: CALL-GATE")); + + /* check privilege level */ + if (callgate_sel->desc.dpl < CPU_STAT_CPL) { + VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", callgate_sel->desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, callgate_sel->idx); + } + if (callgate_sel->desc.dpl < callgate_sel->rpl) { + VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", callgate_sel->desc.dpl, callgate_sel->rpl)); + EXCEPTION(GP_EXCEPTION, callgate_sel->idx); + } + + /* not present */ + if (selector_is_not_present(callgate_sel)) { + VERBOSE(("CALLfar_pm: selector is not present")); + EXCEPTION(NP_EXCEPTION, callgate_sel->idx); } - rv = parse_selector(&ret_sel, selector); + /* parse code segment descriptor */ + rv = parse_selector(&cs_sel, callgate_sel->desc.u.gate.selector); if (rv < 0) { - EXCEPTION(GP_EXCEPTION, ret_sel.idx); + VERBOSE(("CALLfar_pm: parse_selector (selector = %04x, rv = %d)", callgate_sel->desc.u.gate.selector, rv)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); } - /* check code segment descriptor */ - if (!ret_sel.desc.s || !ret_sel.desc.u.seg.c) { - EXCEPTION(GP_EXCEPTION, ret_sel.idx); + /* check segment type */ + if (SEG_IS_SYSTEM(&cs_sel.desc)) { + VERBOSE(("CALLfar_pm: code segment is system segment")); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + if (SEG_IS_DATA(&cs_sel.desc)) { + VERBOSE(("CALLfar_pm: code segment is data segment")); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); } /* check privilege level */ - if (ret_sel.rpl < CPU_STAT_CPL - || (ret_sel.desc.u.seg.ec && (ret_sel.desc.dpl > ret_sel.rpl))) { - EXCEPTION(GP_EXCEPTION, ret_sel.idx); + if (cs_sel.desc.dpl > CPU_STAT_CPL) { + VERBOSE(("CALLfar_pm: DPL(%d) > CPL(%d)", cs_sel.desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); } /* not present */ - if (selector_is_not_present(&ret_sel)) { - EXCEPTION(NP_EXCEPTION, ret_sel.idx); + if (selector_is_not_present(&cs_sel)) { + VERBOSE(("CALLfar_pm: selector is not present")); + EXCEPTION(NP_EXCEPTION, cs_sel.idx); } - if (ret_sel.rpl > CPU_STAT_CPL) { - selector_t ss_sel; - selector_t temp_sel; - DWORD new_sp; - WORD new_ss; - int i; + /* out of range */ + if (callgate_sel->desc.u.gate.offset > cs_sel.desc.u.seg.limit) { + VERBOSE(("CALLfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", callgate_sel->desc.u.gate.offset, cs_sel.desc.u.seg.limit)); + EXCEPTION(GP_EXCEPTION, 0); + } - VERBOSE(("RETURN-OUTER-PRIVILEGE-LEVEL")); + if (!SEG_IS_CONFORMING_CODE(&cs_sel.desc) && (cs_sel.desc.dpl < CPU_STAT_CPL)) { + CALLfar_pm_call_gate_more_privilege(callgate_sel, &cs_sel); + } else { + CALLfar_pm_call_gate_same_privilege(callgate_sel, &cs_sel); + } +} - if (CPU_INST_OP32) { - CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 16 + nbytes); - if (CPU_STAT_SS32) { - new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP + 8 + nbytes); - new_ss = (WORD)cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP + 8 + 4 + nbytes); - } else { - new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, (WORD)(CPU_SP + 8 + nbytes)); - new_ss = (WORD)cpu_vmemoryread_d(CPU_SS_INDEX, (WORD)(CPU_SP + 8 + 4 + nbytes)); - } - } else { - CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8 + nbytes); - if (CPU_STAT_SS32) { - new_sp = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP + 4 + nbytes); - new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP + 4 + 2 + nbytes); - } else { - new_sp = cpu_vmemoryread_w(CPU_SS_INDEX, (WORD)(CPU_SP + 4 + nbytes)); - new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, (WORD)(CPU_SP + 4 + 2 + nbytes)); - } - } +/*--- + * CALLfar_pm: call gate (SAME-PRIVILEGE) + */ +static void +CALLfar_pm_call_gate_same_privilege(const selector_t *callgate_sel, selector_t *cs_sel) +{ + UINT32 sp; - rv = parse_selector(&ss_sel, new_ss); - if (rv < 0) { - EXCEPTION(GP_EXCEPTION, ss_sel.idx); - } + VERBOSE(("CALLfar_pm: SAME-PRIVILEGE")); - /* check stack segment descriptor */ - /* check privilege level */ - if ((ss_sel.rpl != ret_sel.rpl) - || (ss_sel.desc.dpl != ret_sel.rpl) - || !ss_sel.desc.s - || !ss_sel.desc.u.seg.wr) { - EXCEPTION(GP_EXCEPTION, ss_sel.idx); - } + if (CPU_STAT_SS32) { + sp = CPU_ESP; + } else { + sp = CPU_SP; + } + if (callgate_sel->desc.type == CPU_SYSDESC_TYPE_CALL_32) { + SS_PUSH_CHECK(sp, 8); + + PUSH0_32(CPU_CS); + PUSH0_32(CPU_EIP); + } else { + SS_PUSH_CHECK(sp, 4); + + PUSH0_16(CPU_CS); + PUSH0_16(CPU_IP); + } + + load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL); + CPU_EIP = callgate_sel->desc.u.gate.offset; +} + +/*--- + * CALLfar_pm: call gate (MORE-PRIVILEGE) + */ +static void +CALLfar_pm_call_gate_more_privilege(const selector_t *callgate_sel, selector_t *cs_sel) +{ + UINT32 param[32]; /* copy param */ + selector_t ss_sel; + UINT stacksize; + UINT32 old_eip, old_esp; + UINT32 new_esp; + UINT16 old_cs, old_ss; + UINT16 new_ss; + int param_count; + int i; + int rv; + + VERBOSE(("CALLfar_pm: MORE-PRIVILEGE")); + + /* save register */ + old_cs = CPU_CS; + old_ss = CPU_SS; + old_eip = CPU_EIP; + old_esp = CPU_ESP; + if (!CPU_STAT_SS32) { + old_esp &= 0xffff; + } + + /* get stack pointer from TSS */ + get_stack_pointer_from_tss(cs_sel->desc.dpl, &new_ss, &new_esp); + + /* parse stack segment descriptor */ + rv = parse_selector(&ss_sel, new_ss); + if (rv < 0) { + VERBOSE(("CALLfar_pm: parse_selector (selector = %04x, rv = %d)", new_ss, rv)); + EXCEPTION(TS_EXCEPTION, ss_sel.idx); + } + + /* check privilege level */ + if (ss_sel.rpl != cs_sel->desc.dpl) { + VERBOSE(("CALLfar_pm: selector RPL[SS](%d) != DPL[CS](%d)", ss_sel.rpl, cs_sel->desc.dpl)); + EXCEPTION(TS_EXCEPTION, ss_sel.idx); + } + if (ss_sel.desc.dpl != cs_sel->desc.dpl) { + VERBOSE(("CALLfar_pm: descriptor DPL[SS](%d) != DPL[CS](%d)", ss_sel.desc.dpl, cs_sel->desc.dpl)); + EXCEPTION(TS_EXCEPTION, ss_sel.idx); + } + + /* stack segment must be writable data segment. */ + if (SEG_IS_SYSTEM(&ss_sel.desc)) { + VERBOSE(("CALLfar_pm: stack segment is system segment")); + EXCEPTION(TS_EXCEPTION, ss_sel.idx); + } + if (SEG_IS_CODE(&ss_sel.desc)) { + VERBOSE(("CALLfar_pm: stack segment is code segment")); + EXCEPTION(TS_EXCEPTION, ss_sel.idx); + } + if (!SEG_IS_WRITABLE_DATA(&ss_sel.desc)) { + VERBOSE(("CALLfar_pm: stack segment is read-only data segment")); + EXCEPTION(TS_EXCEPTION, ss_sel.idx); + } + + /* not present */ + if (selector_is_not_present(&ss_sel)) { + VERBOSE(("CALLfar_pm: stack segment selector is not present")); + EXCEPTION(SS_EXCEPTION, ss_sel.idx); + } + + param_count = callgate_sel->desc.u.gate.count; + VERBOSE(("CALLfar_pm: param_count = %d", param_count)); + + /* check stack size */ + if (cs_sel->desc.d) { + stacksize = 16; + } else { + stacksize = 8; + } + if (callgate_sel->desc.type == CPU_SYSDESC_TYPE_CALL_32) { + stacksize += param_count * 4; + } else { + stacksize += param_count * 2; + } + cpu_stack_push_check(ss_sel.idx, &ss_sel.desc, new_esp, stacksize); + + if (callgate_sel->desc.type == CPU_SYSDESC_TYPE_CALL_32) { + /* dump param */ + for (i = 0; i < param_count; i++) { + param[i] = cpu_vmemoryread_d(CPU_SS_INDEX, old_esp + i * 4); + VERBOSE(("CALLfar_pm: get param[%d] = %08x", i, param[i])); + } + + load_ss(ss_sel.selector, &ss_sel.desc, ss_sel.desc.dpl); + if (CPU_STAT_SS32) { + CPU_ESP = new_esp; + } else { + CPU_SP = (UINT16)new_esp; + } + + load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->desc.dpl); + CPU_EIP = callgate_sel->desc.u.gate.offset; + + PUSH0_32(old_ss); + PUSH0_32(old_esp); + + /* restore param */ + for (i = param_count; i > 0; i--) { + PUSH0_32(param[i - 1]); + VERBOSE(("CALLfar_pm: set param[%d] = %08x", i - 1, param[i - 1])); + } + + PUSH0_32(old_cs); + PUSH0_32(old_eip); + } else { + /* dump param */ + for (i = 0; i < param_count; i++) { + param[i] = cpu_vmemoryread_w(CPU_SS_INDEX, old_esp + i * 2); + VERBOSE(("CALLfar_pm: get param[%d] = %04x", i, param[i])); + } + + load_ss(ss_sel.selector, &ss_sel.desc, ss_sel.desc.dpl); + if (CPU_STAT_SS32) { + CPU_ESP = new_esp; + } else { + CPU_SP = (UINT16)new_esp; + } + + load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->desc.dpl); + CPU_EIP = callgate_sel->desc.u.gate.offset; + + PUSH0_16(old_ss); + PUSH0_16(old_esp); + + /* restore param */ + for (i = param_count; i > 0; i--) { + PUSH0_16(param[i - 1]); + VERBOSE(("CALLfar_pm: set param[%d] = %04x", i - 1, param[i - 1])); + } + + PUSH0_16(old_cs); + PUSH0_16(old_eip); + } +} + +/*--- + * CALLfar_pm: task gate + */ +static void +CALLfar_pm_task_gate(selector_t *taskgate_sel) +{ + selector_t tss_sel; + int rv; + + VERBOSE(("CALLfar_pm: TASK-GATE")); + + /* check privilege level */ + if (taskgate_sel->desc.dpl < CPU_STAT_CPL) { + VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", taskgate_sel->desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, taskgate_sel->idx); + } + if (taskgate_sel->desc.dpl < taskgate_sel->rpl) { + VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", taskgate_sel->desc.dpl, taskgate_sel->rpl)); + EXCEPTION(GP_EXCEPTION, taskgate_sel->idx); + } + + /* not present */ + if (selector_is_not_present(taskgate_sel)) { + VERBOSE(("CALLfar_pm: selector is not present")); + EXCEPTION(NP_EXCEPTION, taskgate_sel->idx); + } + + /* tss descriptor */ + rv = parse_selector(&tss_sel, taskgate_sel->desc.u.gate.selector); + if (rv < 0 || tss_sel.ldt) { + VERBOSE(("CALLfar_pm: parse_selector (selector = %04x, rv = %d, %cDT)", tss_sel.selector, rv, tss_sel.ldt ? 'L' : 'G')); + EXCEPTION(GP_EXCEPTION, tss_sel.idx); + } + + /* check descriptor type */ + switch (tss_sel.desc.type) { + case CPU_SYSDESC_TYPE_TSS_16: + case CPU_SYSDESC_TYPE_TSS_32: + break; + + case CPU_SYSDESC_TYPE_TSS_BUSY_16: + case CPU_SYSDESC_TYPE_TSS_BUSY_32: + VERBOSE(("CALLfar_pm: task is busy")); + /*FALLTHROUGH*/ + default: + VERBOSE(("CALLfar_pm: invalid descriptor type (type = %d)", tss_sel.desc.type)); + EXCEPTION(GP_EXCEPTION, tss_sel.idx); + break; + } + + /* not present */ + if (selector_is_not_present(&tss_sel)) { + VERBOSE(("CALLfar_pm: TSS selector is not present")); + EXCEPTION(NP_EXCEPTION, tss_sel.idx); + } + + task_switch(&tss_sel, TASK_SWITCH_CALL); + + /* out of range */ + if (CPU_EIP > CPU_STAT_CS_LIMIT) { + VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", CPU_EIP, CPU_STAT_CS_LIMIT)); + EXCEPTION(GP_EXCEPTION, 0); + } +} + +/*--- + * CALLfar_pm: TSS + */ +static void +CALLfar_pm_tss(selector_t *tss_sel) +{ + + VERBOSE(("TASK-STATE-SEGMENT")); + + /* check privilege level */ + if (tss_sel->desc.dpl < CPU_STAT_CPL) { + VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", tss_sel->desc.dpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, tss_sel->idx); + } + if (tss_sel->desc.dpl < tss_sel->rpl) { + VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", tss_sel->desc.dpl, tss_sel->rpl)); + EXCEPTION(GP_EXCEPTION, tss_sel->idx); + } + + /* not present */ + if (selector_is_not_present(tss_sel)) { + VERBOSE(("CALLfar_pm: TSS selector is not present")); + EXCEPTION(NP_EXCEPTION, tss_sel->idx); + } + + task_switch(tss_sel, TASK_SWITCH_CALL); + + /* out of range */ + if (CPU_EIP > CPU_STAT_CS_LIMIT) { + VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", CPU_EIP, CPU_STAT_CS_LIMIT)); + EXCEPTION(GP_EXCEPTION, 0); + } +} + + +/*------------------------------------------------------------------------------ + * RETfar_pm + */ + +void +RETfar_pm(UINT nbytes) +{ + selector_t cs_sel, ss_sel, temp_sel; + descriptor_t *sdp; + UINT32 sp; + UINT32 new_ip, new_sp; + UINT16 new_cs, new_ss; + 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)); + + if (CPU_STAT_SS32) { + sp = CPU_ESP; + } else { + sp = CPU_SP; + } + if (CPU_INST_OP32) { + SS_POP_CHECK(sp, nbytes + 8); + new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, sp); + new_cs = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4); + } else { + SS_POP_CHECK(sp, nbytes + 4); + new_ip = cpu_vmemoryread_w(CPU_SS_INDEX, sp); + new_cs = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 2); + } + + rv = parse_selector(&cs_sel, new_cs); + if (rv < 0) { + VERBOSE(("RETfar_pm: parse_selector (selector = %04x, rv = %d)", cs_sel.selector, rv)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + + /* check segment type */ + if (SEG_IS_SYSTEM(&cs_sel.desc)) { + VERBOSE(("RETfar_pm: return to system segment")); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + if (SEG_IS_DATA(&cs_sel.desc)) { + VERBOSE(("RETfar_pm: return to data segment")); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + + /* check privilege level */ + if (cs_sel.rpl < CPU_STAT_CPL) { + VERBOSE(("RETfar_pm: RPL(%d) < CPL(%d)", cs_sel.rpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + if (!SEG_IS_CONFORMING_CODE(&cs_sel.desc) && (cs_sel.desc.dpl > cs_sel.rpl)) { + VERBOSE(("RETfar_pm: NON-COMFORMING-CODE-SEGMENT and DPL(%d) > RPL(%d)", cs_sel.desc.dpl, cs_sel.rpl)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + + /* not present */ + if (selector_is_not_present(&cs_sel)) { + VERBOSE(("RETfar_pm: returned code segment is not present")); + EXCEPTION(NP_EXCEPTION, cs_sel.idx); + } + + if (cs_sel.rpl == CPU_STAT_CPL) { + VERBOSE(("RETfar_pm: RETURN-TO-SAME-PRIVILEGE-LEVEL")); + + /* check code segment limit */ + if (new_ip > cs_sel.desc.u.seg.limit) { + VERBOSE(("RETfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, cs_sel.desc.u.seg.limit)); + EXCEPTION(GP_EXCEPTION, 0); + } + + VERBOSE(("RETfar_pm: new_ip = %08x, new_cs = %04x", new_ip, cs_sel.selector)); + + if (CPU_INST_OP32) { + nbytes += 8; + } else { + nbytes += 4; + } + if (CPU_STAT_SS32) { + CPU_ESP += nbytes; + } else { + CPU_SP += (UINT16)nbytes; + } + + load_cs(cs_sel.selector, &cs_sel.desc, CPU_STAT_CPL); + CPU_EIP = new_ip; + } else { + VERBOSE(("RETfar_pm: RETURN-OUTER-PRIVILEGE-LEVEL")); + + if (CPU_INST_OP32) { + SS_POP_CHECK(sp, 8 + 8 + nbytes); + new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 8 + nbytes); + new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 8 + nbytes + 4); + } else { + SS_POP_CHECK(sp, 4 + 4 + nbytes); + new_sp = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4 + nbytes); + new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4 + nbytes + 2); + } + + rv = parse_selector(&ss_sel, new_ss); + if (rv < 0) { + VERBOSE(("RETfar_pm: parse_selector (selector = %04x, rv = %d)", ss_sel.selector, rv)); + EXCEPTION(GP_EXCEPTION, (rv == -2) ? 0 : ss_sel.idx); + } + + /* stack segment must be writable data segment. */ + if (SEG_IS_SYSTEM(&ss_sel.desc)) { + VERBOSE(("RETfar_pm: stack segment is system segment")); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + if (SEG_IS_CODE(&ss_sel.desc)) { + VERBOSE(("RETfar_pm: stack segment is code segment")); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + if (!SEG_IS_WRITABLE_DATA(&ss_sel.desc)) { + VERBOSE(("RETfar_pm: stack segment is read-only data segment")); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + + /* check privilege level */ + if (ss_sel.rpl != cs_sel.rpl) { + VERBOSE(("RETfar_pm: selector RPL[SS](%d) != RPL[CS](%d)", ss_sel.rpl, cs_sel.rpl)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + if (ss_sel.desc.dpl != cs_sel.rpl) { + VERBOSE(("RETfar_pm: descriptor DPL[SS](%d) != RPL[CS](%d)", ss_sel.desc.dpl, cs_sel.rpl)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } /* not present */ if (selector_is_not_present(&ss_sel)) { + VERBOSE(("RETfar_pm: stack segment is not present")); EXCEPTION(SS_EXCEPTION, ss_sel.idx); } /* check code segment limit */ - if (new_ip > ret_sel.desc.u.seg.limit) { + if (new_ip > cs_sel.desc.u.seg.limit) { + VERBOSE(("RETfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, cs_sel.desc.u.seg.limit)); EXCEPTION(GP_EXCEPTION, 0); } - /* set new register */ - load_cs(ret_sel.selector, &ret_sel.desc, ret_sel.rpl); - SET_EIP(new_ip); - load_ss(ss_sel.selector, &ss_sel.desc, ret_sel.rpl); - CPU_ESP = new_sp; + VERBOSE(("RETfar_pm: new_ip = %08x, new_cs = %04x", new_ip, cs_sel.selector)); + VERBOSE(("RETfar_pm: new_sp = %08x, new_ss = %04x", new_sp, ss_sel.selector)); + + load_ss(ss_sel.selector, &ss_sel.desc, cs_sel.rpl); + if (CPU_STAT_SS32) { + CPU_ESP = new_sp + nbytes; + } else { + CPU_SP = (UINT16)(new_sp + nbytes); + } + + load_cs(cs_sel.selector, &cs_sel.desc, cs_sel.rpl); + CPU_EIP = new_ip; /* check segment register */ for (i = 0; i < CPU_SEGREG_NUM; i++) { - if (i == CPU_CS_INDEX || i == CPU_SS_INDEX) + if (i == CPU_SS_INDEX || i == CPU_CS_INDEX) continue; + sdp = &CPU_STAT_SREG(i); + if ((SEG_IS_DATA(sdp) || !SEG_IS_CONFORMING_CODE(sdp)) + && (CPU_STAT_CPL > sdp->dpl)) { + /* current segment descriptor is invalid */ + CPU_REGS_SREG(i) = 0; + segdesc_clear(sdp); + continue; + } + + /* Reload segment descriptor */ 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; + segdesc_clear(sdp); continue; } - /* check privilege level */ - if (!temp_sel.desc.s - || (temp_sel.desc.u.seg.c && temp_sel.desc.u.seg.wr) - || ((!temp_sel.desc.u.seg.c || !temp_sel.desc.u.seg.ec) - && ((temp_sel.desc.dpl < CPU_STAT_CPL) || (temp_sel.desc.dpl < temp_sel.rpl)))) { + /* + * - system segment + * - execute-only code segment + * - data or conforming code segment && CPL > DPL + */ + if (SEG_IS_SYSTEM(&temp_sel.desc) + || (SEG_IS_CODE(&temp_sel.desc) + && !SEG_IS_READABLE_CODE(&temp_sel.desc)) + || ((SEG_IS_DATA(&temp_sel.desc) + || !SEG_IS_CONFORMING_CODE(&temp_sel.desc)) + && (CPU_STAT_CPL > temp_sel.desc.dpl))) { + /* segment descriptor is invalid */ CPU_REGS_SREG(i) = 0; - CPU_STAT_SREG(i).valid = 0; - continue; + segdesc_clear(sdp); } } - CPU_ESP += nbytes; - } else { - VERBOSE(("RETURN-TO-SAME-PRIVILEGE-LEVEL")); + } + + VERBOSE(("RETfar_pm: new EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP)); +} + +/*------------------------------------------------------------------------------ + * IRET_pm + */ +static void IRET_pm_nested_task(void); +static void IRET_pm_protected_mode_return(UINT16 new_cs, UINT32 new_ip, UINT32 new_flags); +static void IRET_pm_protected_mode_return_same_privilege(const selector_t *cs_sel, UINT32 new_ip, UINT32 new_flags); +static void IRET_pm_protected_mode_return_outer_privilege(const selector_t *cs_sel, UINT32 new_ip, UINT32 new_flags); +static void IRET_pm_return_to_vm86(UINT16 new_cs, UINT32 new_ip, UINT32 new_flags); +static void IRET_pm_return_from_vm86(UINT16 new_cs, UINT32 new_ip, UINT32 new_flags); + +void +IRET_pm(void) +{ + UINT32 sp; + UINT32 new_ip, new_flags; + UINT16 new_cs; + + VERBOSE(("IRET_pm: old EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_PREV_EIP, CPU_SS, CPU_ESP)); + + if (!(CPU_EFLAG & VM_FLAG) && (CPU_EFLAG & NT_FLAG)) { + /* TASK-RETURN: PE=1, VM=0, NT=1 */ + IRET_pm_nested_task(); + } else { + if (CPU_STAT_SS32) { + sp = CPU_ESP; + } else { + sp = CPU_SP; + } if (CPU_INST_OP32) { - if (new_ip > ret_sel.desc.u.seg.limit) { - EXCEPTION(GP_EXCEPTION, 0); - } - POP0_32(CPU_EIP); - POP0_32(CPU_CS); + SS_POP_CHECK(sp, 12); + new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, sp); + new_cs = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4); + new_flags = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 8); } else { - new_ip &= 0xffff; - if (new_ip > ret_sel.desc.u.seg.limit) { - EXCEPTION(GP_EXCEPTION, 0); - } - POP0_16(CPU_EIP); - POP0_16(CPU_CS); + SS_POP_CHECK(sp, 6); + new_ip = cpu_vmemoryread_w(CPU_SS_INDEX, sp); + new_cs = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 2); + new_flags = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4); + } + + VERBOSE(("IRET_pm: new_ip = %08x, new_cs = %04x, new_eflags = %08x", new_ip, new_cs, new_flags)); + + if (CPU_EFLAG & VM_FLAG) { + /* RETURN-FROM-VIRTUAL-8086-MODE */ + IRET_pm_return_from_vm86(new_cs, new_ip, new_flags); + } else if ((new_flags & VM_FLAG) && CPU_STAT_CPL == 0) { + /* RETURN-TO-VIRTUAL-8086-MODE */ + IRET_pm_return_to_vm86(new_cs, new_ip, new_flags); + } else { + /* PROTECTED-MODE-RETURN */ + IRET_pm_protected_mode_return(new_cs, new_ip, new_flags); } - load_cs(ret_sel.selector, &ret_sel.desc, CPU_STAT_CPL); - SET_EIP(new_ip); - CPU_ESP += nbytes; } + + VERBOSE(("IRET_pm: new EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP)); } -void -IRET_pm() +/*--- + * IRET_pm: NT_FLAG + */ +static void +IRET_pm_nested_task(void) { - selector_t iret_sel; + selector_t tss_sel; + UINT16 new_tss; int rv; - DWORD new_ip, new_flags; - WORD new_cs; - VERBOSE(("IRET_pm: EIP = 0x%08x", CPU_PREV_EIP)); + VERBOSE(("IRET_pm: TASK-RETURN: PE=1, VM=0, NT=1")); - if (CPU_STAT_VM86) { - /* RETURN-FROM-VIRTUAL-8086-MODE */ - VERBOSE(("Virtual-8086 mode: PE=1, VM=1")); - VERBOSE(("Processor is in virtual-8086 mode when IRET is executed and stays in virtual-8086 mode")); - - if (CPU_STAT_IOPL == CPU_IOPL3) { - VERBOSE(("Virtual mode: PE=1, VM=1, IOPL=3")); - if (CPU_INST_OP32) { - CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 12); - POP0_32(new_ip); - POP0_32(new_cs); - POP0_32(new_flags); - - set_eflags(new_flags, I_FLAG|RF_FLAG); - } else { - CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 6); - POP0_16(new_ip); - POP0_16(new_cs); - POP0_16(new_flags); + new_tss = get_backlink_selector_from_tss(); - set_flags(new_flags, I_FLAG); - } - CPU_SET_SEGREG(CPU_CS_INDEX, new_cs); - SET_EIP(new_ip); - return; - } - VERBOSE(("trap to virtual-8086 monitor: PE=1, VM=1, IOPL<3")); + 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')); + EXCEPTION(GP_EXCEPTION, tss_sel.idx); + } + + /* check system segment */ + if (!SEG_IS_SYSTEM(&tss_sel.desc)) { + VERBOSE(("IRET_pm: task segment is %s segment", tss_sel.desc.u.seg.c ? "code" : "data")); + EXCEPTION(GP_EXCEPTION, tss_sel.idx); + } + + switch (tss_sel.desc.type) { + case CPU_SYSDESC_TYPE_TSS_BUSY_16: + case CPU_SYSDESC_TYPE_TSS_BUSY_32: + break; + + case CPU_SYSDESC_TYPE_TSS_16: + case CPU_SYSDESC_TYPE_TSS_32: + VERBOSE(("IRET_pm: task is not busy")); + /*FALLTHROUGH*/ + default: + VERBOSE(("IRET_pm: invalid descriptor type (type = %d)", tss_sel.desc.type)); + EXCEPTION(GP_EXCEPTION, tss_sel.idx); + break; + } + + /* not present */ + if (selector_is_not_present(&tss_sel)) { + VERBOSE(("IRET_pm: tss segment is not present")); + EXCEPTION(NP_EXCEPTION, tss_sel.idx); + } + + task_switch(&tss_sel, TASK_SWITCH_IRET); + + /* out of range */ + if (CPU_EIP > CPU_STAT_CS_LIMIT) { + VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", CPU_EIP, CPU_STAT_CS_LIMIT)); EXCEPTION(GP_EXCEPTION, 0); - } else if (CPU_EFLAG & NT_FLAG) { - VERBOSE(("TASK-RETURN: PE=1, VM=0, NT=1")); + } +} - new_cs = get_link_selector_from_tss(); - rv = parse_selector(&iret_sel, new_cs); - if (rv < 0 || iret_sel.ldt) { - EXCEPTION(GP_EXCEPTION, iret_sel.idx); - } +/*--- + * IRET_pm: PROTECTED-MODE-RETURN + */ +static void +IRET_pm_protected_mode_return(UINT16 new_cs, UINT32 new_ip, UINT32 new_flags) +{ + selector_t cs_sel; + int rv; - /* check system segment */ - if (iret_sel.desc.s) { - EXCEPTION(GP_EXCEPTION, iret_sel.idx); - } - switch (iret_sel.desc.type) { - case CPU_SYSDESC_TYPE_TSS_BUSY_16: - case CPU_SYSDESC_TYPE_TSS_BUSY_32: - break; + /* PROTECTED-MODE-RETURN */ + VERBOSE(("IRET_pm: PE=1, VM=0 in flags image")); - case CPU_SYSDESC_TYPE_TSS_16: - case CPU_SYSDESC_TYPE_TSS_32: - VERBOSE(("IRET_pm: task is not busy")); - /*FALLTHROUGH*/ - default: - EXCEPTION(GP_EXCEPTION, iret_sel.idx); - break; - } + rv = parse_selector(&cs_sel, new_cs); + if (rv < 0) { + VERBOSE(("IRET_pm: parse_selector (selector = %04x, rv = %d)", cs_sel.selector, rv)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } - /* not present */ - if (selector_is_not_present(&iret_sel)) { - EXCEPTION(NP_EXCEPTION, iret_sel.idx); - } + /* check code segment descriptor */ + if (SEG_IS_SYSTEM(&cs_sel.desc)) { + VERBOSE(("IRET_pm: return code segment is system segment")); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + if (SEG_IS_DATA(&cs_sel.desc)) { + VERBOSE(("IRET_pm: return code segment is data segment")); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } - task_switch(&iret_sel, TASK_SWITCH_IRET); + /* check privilege level */ + if (cs_sel.rpl < CPU_STAT_CPL) { + VERBOSE(("IRET_pm: RPL(%d) < CPL(%d)", cs_sel.rpl, CPU_STAT_CPL)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } + if (SEG_IS_CONFORMING_CODE(&cs_sel.desc) && (cs_sel.desc.dpl > cs_sel.rpl)) { + VERBOSE(("IRET_pm: CONFORMING-CODE-SEGMENT and DPL(%d) > RPL(%d)", cs_sel.desc.dpl, cs_sel.rpl)); + EXCEPTION(GP_EXCEPTION, cs_sel.idx); + } - /* out of range */ - if (CPU_EIP > CPU_STAT_CS_LIMIT) { - EXCEPTION(GP_EXCEPTION, 0); + /* not present */ + if (selector_is_not_present(&cs_sel)) { + VERBOSE(("IRET_pm: code segment is not present")); + EXCEPTION(NP_EXCEPTION, cs_sel.idx); + } + + if (cs_sel.rpl > CPU_STAT_CPL) { + IRET_pm_protected_mode_return_outer_privilege(&cs_sel, new_ip, new_flags); + } else { + IRET_pm_protected_mode_return_same_privilege(&cs_sel, new_ip, new_flags); + } +} + +/*--- + * IRET_pm: SAME-PRIVILEGE + */ +static void +IRET_pm_protected_mode_return_same_privilege(const selector_t *cs_sel, UINT32 new_ip, UINT32 new_flags) +{ + UINT32 mask; + UINT stacksize; + + VERBOSE(("IRET_pm: RETURN-TO-SAME-PRIVILEGE-LEVEL")); + + /* check code segment limit */ + if (new_ip > cs_sel->desc.u.seg.limit) { + VERBOSE(("IRET_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, cs_sel->desc.u.seg.limit)); + EXCEPTION(GP_EXCEPTION, 0); + } + + mask = 0; + if (CPU_INST_OP32) + mask |= RF_FLAG; + if (CPU_STAT_CPL <= CPU_STAT_IOPL) + mask |= I_FLAG; + if (CPU_STAT_CPL == 0) { + mask |= IOPL_FLAG; + if (CPU_INST_OP32) { + mask |= VM_FLAG|VIF_FLAG|VIP_FLAG; } + } + + if (CPU_INST_OP32) { + stacksize = 12; } else { + stacksize = 6; + } + + /* set new register */ + load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL); + CPU_EIP = new_ip; + + if (CPU_STAT_SS32) { + CPU_ESP += stacksize; + } else { + CPU_SP += (UINT16)stacksize; + } + + set_eflags(new_flags, mask); +} + +/*--- + * IRET_pm: OUTER-PRIVILEGE + */ +static void +IRET_pm_protected_mode_return_outer_privilege(const selector_t *cs_sel, UINT32 new_ip, UINT32 new_flags) +{ + descriptor_t *sdp; + selector_t ss_sel; + UINT32 mask; + UINT32 sp; + UINT32 new_sp; + UINT16 new_ss; + int rv; + int i; + + VERBOSE(("IRET_pm: RETURN-OUTER-PRIVILEGE-LEVEL")); + + if (CPU_STAT_SS32) { + sp = CPU_ESP; + } else { + sp = CPU_SP; + } + if (CPU_INST_OP32) { + SS_POP_CHECK(sp, 20); + new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 12); + new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 16); + } else { + SS_POP_CHECK(sp, 10); + new_sp = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 6); + new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 8); + } + VERBOSE(("IRET_pm: new_sp = 0x%08x, new_ss = 0x%04x", new_sp, new_ss)); + + rv = parse_selector(&ss_sel, new_ss); + if (rv < 0) { + VERBOSE(("IRET_pm: parse_selector (selector = %04x, rv = %d)", ss_sel.selector, rv)); + EXCEPTION(GP_EXCEPTION, (rv == -2) ? 0 : ss_sel.idx); + } + + /* check privilege level */ + if (ss_sel.rpl != cs_sel->rpl) { + VERBOSE(("IRET_pm: selector RPL[SS](%d) != RPL[CS](%d)", ss_sel.rpl, cs_sel->rpl)); + EXCEPTION(GP_EXCEPTION, ss_sel.idx); + } +#if 0 + if (ss_sel.desc.dpl != cs_sel->rpl) { + VERBOSE(("IRET_pm: segment DPL[SS](%d) != RPL[CS](%d)", ss_sel.desc.dpl, cs_sel->rpl)); + EXCEPTION(GP_EXCEPTION, ss_sel.idx); + } + if (ss_sel.desc.rpl != cs_sel->rpl) { + VERBOSE(("IRET_pm: segment RPL[SS](%d) != RPL[CS](%d)", ss_sel.desc.rpl, cs_sel->rpl)); + EXCEPTION(GP_EXCEPTION, ss_sel.idx); + } +#endif + + /* stack segment must be writable data segment. */ + if (SEG_IS_SYSTEM(&ss_sel.desc)) { + VERBOSE(("IRET_pm: stack segment is system segment")); + EXCEPTION(GP_EXCEPTION, ss_sel.idx); + } + if (SEG_IS_CODE(&ss_sel.desc)) { + VERBOSE(("IRET_pm: stack segment is code segment")); + EXCEPTION(GP_EXCEPTION, ss_sel.idx); + } + if (!SEG_IS_WRITABLE_DATA(&ss_sel.desc)) { + VERBOSE(("IRET_pm: stack segment is read-only data segment")); + EXCEPTION(GP_EXCEPTION, ss_sel.idx); + } + + /* not present */ + if (selector_is_not_present(&ss_sel)) { + VERBOSE(("IRET_pm: stack segment is not present")); + EXCEPTION(SS_EXCEPTION, ss_sel.idx); + } + + /* check code segment limit */ + if (new_ip > cs_sel->desc.u.seg.limit) { + VERBOSE(("IRET_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, cs_sel->desc.u.seg.limit)); + EXCEPTION(GP_EXCEPTION, 0); + } + + mask = 0; + if (CPU_INST_OP32) + mask |= RF_FLAG; + if (CPU_STAT_CPL <= CPU_STAT_IOPL) + mask |= I_FLAG; + if (CPU_STAT_CPL == 0) { + mask |= IOPL_FLAG; if (CPU_INST_OP32) { - CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 12); - POP0_32(new_ip); - POP0_32(new_cs); - POP0_32(new_flags); - } else { - CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 6); - POP0_16(new_ip); - POP0_16(new_cs); - POP0_16(new_flags); + mask |= VM_FLAG|VIF_FLAG|VIP_FLAG; } + } - if ((CPU_STAT_CPL == 0) && (new_flags & VM_FLAG)) { - /* RETURN-TO-VIRTUAL-8086-MODE */ - descriptor_t sd; - DWORD new_sp; - WORD segsel[CPU_SEGREG_NUM]; - int i; - - VERBOSE(("Interrupt procedure was in virtual-8086 mode: PE=1, VM=1 in flags image")); - - CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 24); - - segsel[CPU_CS_INDEX] = new_cs; - POP0_32(new_sp); - POP0_32(segsel[CPU_SS_INDEX]); - POP0_32(segsel[CPU_ES_INDEX]); - POP0_32(segsel[CPU_DS_INDEX]); - POP0_32(segsel[CPU_FS_INDEX]); - POP0_32(segsel[CPU_GS_INDEX]); - - for (i = 0; i < CPU_SEGREG_NUM; i++) { - CPU_REGS_SREG(i) = segsel[i]; - sd.u.seg.limit = 0xffff; - CPU_SET_SEGDESC_DEFAULT(&sd, i, segsel[i]); - sd.dpl = 3; - CPU_STAT_SREG(i) = sd; - } - CPU_ESP = new_sp; - SET_EIP(new_ip & 0xffff); + /* set new register */ + load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->rpl); + CPU_EIP = new_ip; - set_eflags(new_flags, IOPL_FLAG|RF_FLAG); - } else { - DWORD mask; + load_ss(ss_sel.selector, &ss_sel.desc, cs_sel->rpl); + if (CPU_STAT_SS32) { + CPU_ESP = new_sp; + } else { + CPU_SP = (UINT16)new_sp; + } - /* PROTECTED-MODE-RETURN */ - VERBOSE(("PE=1, VM=0 in flags image")); + set_eflags(new_flags, mask); - rv = parse_selector(&iret_sel, new_cs); - if (rv < 0) { - EXCEPTION(GP_EXCEPTION, iret_sel.idx); + /* check segment register */ + for (i = 0; i < CPU_SEGREG_NUM; i++) { + if ((i != CPU_CS_INDEX) && (i != CPU_SS_INDEX)) { + sdp = &CPU_STAT_SREG(i); + if ((SEG_IS_DATA(sdp) || !SEG_IS_CONFORMING_CODE(sdp)) + && (sdp->dpl < CPU_STAT_CPL)) { + /* segment register is invalid */ + CPU_REGS_SREG(i) = 0; + segdesc_clear(sdp); } + } + } +} - /* check code segment descriptor */ - if (!iret_sel.desc.s || !iret_sel.desc.u.seg.c) { - EXCEPTION(GP_EXCEPTION, iret_sel.idx); - } +/*--- + * IRET_pm: new_flags & VM_FLAG + */ +static void +IRET_pm_return_to_vm86(UINT16 new_cs, UINT32 new_ip, UINT32 new_flags) +{ + UINT16 segsel[CPU_SEGREG_NUM]; + UINT32 sp; + UINT32 new_sp; + int i; - /* check privilege level */ - if ((iret_sel.rpl < CPU_STAT_CPL) - || (iret_sel.desc.u.seg.ec && (iret_sel.desc.dpl > iret_sel.rpl))) { - EXCEPTION(GP_EXCEPTION, iret_sel.idx); - } + VERBOSE(("IRET_pm: Interrupt procedure was in virtual-8086 mode: PE=1, VM=1 in flags image")); - /* not present */ - if (selector_is_not_present(&iret_sel)) { - EXCEPTION(NP_EXCEPTION, iret_sel.idx); - } + if (!CPU_INST_OP32) { + ia32_panic("IRET_pm: 16bit mode"); + } - if (iret_sel.rpl > CPU_STAT_CPL) { - /* RETURN-OUTER-PRIVILEGE-LEVEL */ - selector_t ss_sel; - WORD new_sp, new_ss; - - if (CPU_INST_OP32) { - CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8); - if (CPU_STAT_SS32) { - new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP); - new_ss = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP + 4); - } else { - new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_SP); - new_ss = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_SP + 4); - } - } else { - CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 4); - if (CPU_STAT_SS32) { - new_sp = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP); - new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP + 2); - } else { - new_sp = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_SP); - new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_SP + 2); - } - } - - rv = parse_selector(&ss_sel, new_ss); - if (rv < 0) { - EXCEPTION(GP_EXCEPTION, ss_sel.idx); - } - - /* check stack segment descriptor */ - /* check privilege level */ - if ((ss_sel.rpl != iret_sel.rpl) - || (ss_sel.desc.dpl != iret_sel.rpl) - || !ss_sel.desc.s - || !ss_sel.desc.u.seg.wr) { - EXCEPTION(GP_EXCEPTION, ss_sel.idx); - } - - /* not present */ - if (selector_is_not_present(&ss_sel)) { - EXCEPTION(SS_EXCEPTION, ss_sel.idx); - } - } else { - VERBOSE(("RETURN-TO-SAME-PRIVILEGE-LEVEL")); - } + if (CPU_STAT_SS32) { + sp = CPU_ESP; + } else { + sp = CPU_SP; + } + SS_POP_CHECK(sp, 36); - /* check code segment limit */ - if (new_ip > iret_sel.desc.u.seg.limit) { - EXCEPTION(GP_EXCEPTION, 0); - } + if (new_ip > 0xffff) { + EXCEPTION(GP_EXCEPTION, 0); + } - /* set new register */ - load_cs(iret_sel.selector, &iret_sel.desc, iret_sel.rpl); - SET_EIP(new_ip); - - mask = 0; - if (CPU_INST_OP32) - mask |= RF_FLAG; - if (CPU_STAT_CPL <= CPU_STAT_IOPL) - mask |= I_FLAG; - if (CPU_STAT_CPL == 0) { - mask |= IOPL_FLAG; - if (CPU_INST_OP32) - mask |= VM_FLAG|VIF_FLAG|VIP_FLAG; - } - set_eflags(new_flags, mask); + new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 12); + segsel[CPU_SS_INDEX] = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 16); + segsel[CPU_ES_INDEX] = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 20); + segsel[CPU_DS_INDEX] = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 24); + segsel[CPU_FS_INDEX] = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 28); + segsel[CPU_GS_INDEX] = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 32); + segsel[CPU_CS_INDEX] = new_cs; - if (iret_sel.rpl > CPU_STAT_CPL) { - selector_t temp_sel; - int i; - - /* RETURN-OUTER-PRIVILEGE-LEVEL */ - /* 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) { - CPU_REGS_SREG(i) = 0; - CPU_STAT_SREG(i).valid = 0; - continue; - } - - if ((!temp_sel.desc.u.seg.c || !temp_sel.desc.u.seg.ec) - && (CPU_STAT_CPL > temp_sel.desc.dpl)) { - CPU_REGS_SREG(i) = 0; - CPU_STAT_SREG(i).valid = 0; - continue; - } - } - } + for (i = 0; i < CPU_SEGREG_NUM; i++) { + segdesc_init(i, segsel[i], &CPU_STAT_SREG(i)); + } + + CPU_ESP = new_sp; + CPU_EIP = new_ip; + + /* to VM86 mode */ + set_eflags(new_flags, IOPL_FLAG|I_FLAG|VM_FLAG|RF_FLAG); +} + +/*--- + * IRET_pm: VM_FLAG + */ +static void +IRET_pm_return_from_vm86(UINT16 new_cs, UINT32 new_ip, UINT32 new_flags) +{ + UINT stacksize; + + VERBOSE(("IRET_pm: virtual-8086 mode: VM=1")); + + if (CPU_STAT_IOPL == CPU_IOPL3) { + VERBOSE(("IRET_pm: virtual-8086 mode: IOPL=3")); + if (CPU_INST_OP32) { + stacksize = 12; + } else { + stacksize = 6; + } + if (CPU_STAT_SS32) { + CPU_ESP += stacksize; + } else { + CPU_SP += stacksize; } + + LOAD_SEGREG(CPU_CS_INDEX, new_cs); + if (new_ip > CPU_STAT_CS_LIMIT) { + EXCEPTION(GP_EXCEPTION, 0); + } + CPU_EIP = new_ip; + + set_eflags(new_flags, I_FLAG|RF_FLAG); + return; } + VERBOSE(("IRET_pm: trap to virtual-8086 monitor: VM=1, IOPL<3")); + EXCEPTION(GP_EXCEPTION, 0); }