|
|
| version 1.2, 2004/01/13 16:37:42 | version 1.11, 2004/02/13 14:50:17 |
|---|---|
| Line 34 | Line 34 |
| #include "ctrlxfer.h" | #include "ctrlxfer.h" |
| /*------------------------------------------------------------------------------ | |
| * JMPfar_pm | |
| */ | |
| static void JMPfar_pm_code_segment(selector_t *jmp_sel, DWORD new_ip); | |
| static void JMPfar_pm_call_gate(selector_t *jmp_sel); | |
| static void JMPfar_pm_task_gate(selector_t *jmp_sel); | |
| static void JMPfar_pm_tss(selector_t *jmp_sel); | |
| void | void |
| JMPfar_pm(WORD selector, DWORD new_ip) | JMPfar_pm(WORD selector, DWORD new_ip) |
| { | { |
| selector_t jmp_sel; | selector_t jmp_sel; |
| selector_t sel2; | |
| int rv; | int rv; |
| VERBOSE(("JMPfar_pm: EIP = 0x%08x, selector = 0x%04x, new_ip = 0x%08x", CPU_PREV_EIP, selector, new_ip)); | 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)); | |
| /* | |
| * 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 */ | |
| rv = parse_selector(&jmp_sel, selector); | rv = parse_selector(&jmp_sel, selector); |
| if (rv < 0) { | if (rv < 0) { |
| Line 58 JMPfar_pm(WORD selector, DWORD new_ip) | Line 58 JMPfar_pm(WORD selector, DWORD new_ip) |
| } | } |
| if (jmp_sel.desc.s) { | if (jmp_sel.desc.s) { |
| /* code segment descriptor */ | |
| VERBOSE(("JMPfar_pm: code or data segment descriptor")); | VERBOSE(("JMPfar_pm: code or data segment descriptor")); |
| /* check segment type */ | |
| if (!jmp_sel.desc.u.seg.c) { | if (!jmp_sel.desc.u.seg.c) { |
| /* data segment */ | /* data segment */ |
| VERBOSE(("JMPfar_pm: data segment")); | VERBOSE(("JMPfar_pm: data segment")); |
| EXCEPTION(GP_EXCEPTION, jmp_sel.idx); | EXCEPTION(GP_EXCEPTION, jmp_sel.idx); |
| } | } |
| /* check privilege level */ | /* code segment descriptor */ |
| if (!jmp_sel.desc.u.seg.ec) { | JMPfar_pm_code_segment(&jmp_sel, new_ip); |
| 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); | |
| } else { | } else { |
| /* system descriptor */ | /* system descriptor */ |
| VERBOSE(("JMPfar_pm: system descriptor")); | VERBOSE(("JMPfar_pm: system descriptor")); |
| Line 111 JMPfar_pm(WORD selector, DWORD new_ip) | Line 76 JMPfar_pm(WORD selector, DWORD new_ip) |
| switch (jmp_sel.desc.type) { | switch (jmp_sel.desc.type) { |
| case CPU_SYSDESC_TYPE_CALL_16: | case CPU_SYSDESC_TYPE_CALL_16: |
| case CPU_SYSDESC_TYPE_CALL_32: | case CPU_SYSDESC_TYPE_CALL_32: |
| VERBOSE(("CALL-GATE")); | JMPfar_pm_call_gate(&jmp_sel); |
| /* 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); | |
| break; | break; |
| /* | |
| * 中巻 p.373 JMP 命令 | |
| * | |
| * JMP 命令でタスク・スイッチを実行するときは EFLAGS レジスタに | |
| * ネストされたタスク・フラグ (NT) がセットされず、新しい TSS の | |
| * 以前のタスク・リンク・フィールドに前のタスクの TSS セレクタが | |
| * ロードされないので注意されたい。したがって、前のタスクへの | |
| * リターンは IRET 命令の実行では実現できない。JMP 命令で | |
| * タスク・スイッチを実行するのは、その点で CALL 命令と異なる。 | |
| * すなわち、CALL 命令は NT フラグをセットし、以前の | |
| * タスク・リンク情報をセーブするので、IRET 命令でのコール元 | |
| * タスクへのリターンが可能になる。 | |
| */ | |
| case CPU_SYSDESC_TYPE_TASK: | case CPU_SYSDESC_TYPE_TASK: |
| VERBOSE(("TASK-GATE")); | JMPfar_pm_task_gate(&jmp_sel); |
| /* 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); | |
| } | |
| break; | break; |
| case CPU_SYSDESC_TYPE_TSS_16: | case CPU_SYSDESC_TYPE_TSS_16: |
| case CPU_SYSDESC_TYPE_TSS_32: | case CPU_SYSDESC_TYPE_TSS_32: |
| VERBOSE(("TASK-STATE-SEGMENT")); | JMPfar_pm_tss(&jmp_sel); |
| /* 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); | |
| } | |
| break; | break; |
| case CPU_SYSDESC_TYPE_TSS_BUSY_16: | case CPU_SYSDESC_TYPE_TSS_BUSY_16: |
| Line 271 JMPfar_pm(WORD selector, DWORD new_ip) | Line 93 JMPfar_pm(WORD selector, DWORD new_ip) |
| VERBOSE(("JMPfar_pm: task is busy")); | VERBOSE(("JMPfar_pm: task is busy")); |
| /*FALLTHROUGH*/ | /*FALLTHROUGH*/ |
| default: | 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); | EXCEPTION(GP_EXCEPTION, jmp_sel.idx); |
| break; | break; |
| } | } |
| } | } |
| VERBOSE(("JMPfar_pm: new EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP)); | |
| } | } |
| /* | /*--- |
| * 4.3.6. 特権レベル間のコール操作 | * JMPfar: code segment |
| * | |
| * 1. アクセス権のチェック(特権チェック)を実行する。 | |
| * 2. SS, ESP, CS, EIP の各レジスタの現在地を一時的に内部にセーブする。 | |
| * 3. TSS レジスタに格納されている新しいスタック(すなわち、現在コールされている | |
| * 特権レベル用のスタック)のセグメントレジスタとスタックポインタを | |
| * SS レジスタと ESP レジスタにロードし、新しいスタックに切り替える。 | |
| * 4. コール元プロシージャのスタックに対して一時的にセーブしておいた SS 値と | |
| * ESP 値を、この新しいスタックにプッシュする。 | |
| * 5. コール元プロシージャのスタックからパラメータをコピーする。新しいスタック | |
| * にコピーされるパラメータの数は、コール・ゲート・ディスクリプタ内の値で | |
| * 決まる。 | |
| * 6. コール元プロシージャに対して一時的にセーブしておいた CS 値と EIP 値を、 | |
| * 新しいスタックにプッシュする。 | |
| * 7. 新しいコード・セグメントのセグメント・セレクタと新しい命令ポインタを、 | |
| * コール・ゲートから CS レジスタと EIP レジスタにそれぞれロードする。 | |
| * 8. コールされたプロシージャの実行を新しい特権レベルで開始する。 | |
| */ | */ |
| void | static void |
| CALLfar_pm(WORD selector, DWORD new_ip) | JMPfar_pm_code_segment(selector_t *cs_sel, DWORD new_ip) |
| { | { |
| selector_t call_sel; | |
| selector_t sel2; | |
| int rv; | |
| VERBOSE(("CALLfar_pm: EIP = 0x%08x, selector = 0x%04x, new_ip = 0x%08x", CPU_PREV_EIP, selector, new_ip)); | VERBOSE(("JMPfar_pm: CODE-SEGMENT")); |
| rv = parse_selector(&call_sel, selector); | /* check privilege level */ |
| if (rv < 0) { | if (!cs_sel->desc.u.seg.ec) { |
| EXCEPTION(GP_EXCEPTION, call_sel.idx); | 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); | |
| } | |
| } | } |
| if (call_sel.desc.s) { | /* not present */ |
| /* code segment descriptor */ | if (selector_is_not_present(cs_sel)) { |
| if (!call_sel.desc.u.seg.c) { | VERBOSE(("JMPfar_pm: selector is not present")); |
| /* data segment */ | EXCEPTION(NP_EXCEPTION, cs_sel->idx); |
| EXCEPTION(GP_EXCEPTION, call_sel.idx); | } |
| } | |
| /* check privilege level */ | /* out of range */ |
| if (!call_sel.desc.u.seg.ec) { | if (new_ip > cs_sel->desc.u.seg.limit) { |
| VERBOSE(("NONCONFORMING-CODE-SEGMENT")); | 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); | |
| } | |
| /* 下巻 p.119 4.8.1.1. */ | load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL); |
| if ((call_sel.rpl > CPU_STAT_CPL) | SET_EIP(new_ip); |
| || (call_sel.desc.dpl != CPU_STAT_CPL)) { | } |
| EXCEPTION(GP_EXCEPTION, call_sel.idx); | |
| } | |
| } else { | |
| VERBOSE(("CONFORMING-CODE-SEGMENT")); | |
| /* 下巻 p.120 4.8.1.2. */ | /*--- |
| if (call_sel.desc.dpl > CPU_STAT_CPL) { | * JMPfar: call gate |
| EXCEPTION(GP_EXCEPTION, call_sel.idx); | */ |
| } | static void |
| } | JMPfar_pm_call_gate(selector_t *callgate_sel) |
| { | |
| selector_t cs_sel; | |
| int rv; | |
| /* not present */ | VERBOSE(("JMPfar_pm: CALL-GATE")); |
| if (selector_is_not_present(&call_sel)) { | |
| EXCEPTION(NP_EXCEPTION, call_sel.idx); | |
| } | |
| if (CPU_INST_OP32) { | /* check privilege level */ |
| CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8); | if (callgate_sel->desc.dpl < CPU_STAT_CPL) { |
| /* out of range */ | VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d)", callgate_sel->desc.dpl, CPU_STAT_CPL)); |
| if (new_ip > call_sel.desc.u.seg.limit) { | EXCEPTION(GP_EXCEPTION, callgate_sel->idx); |
| EXCEPTION(GP_EXCEPTION, 0); | } |
| } | 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); | |
| } | |
| PUSH0_32(CPU_CS); | /* not present */ |
| PUSH0_32(CPU_EIP); | if (selector_is_not_present(callgate_sel)) { |
| } else { | VERBOSE(("JMPfar_pm: selector is not present")); |
| CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 4); | EXCEPTION(NP_EXCEPTION, callgate_sel->idx); |
| /* out of range */ | } |
| if (new_ip > call_sel.desc.u.seg.limit) { | |
| EXCEPTION(GP_EXCEPTION, 0); | |
| } | |
| PUSH0_16(CPU_CS); | /* parse code segment selector */ |
| PUSH0_16(CPU_IP); | 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); | |
| } | |
| if (!call_sel.desc.d) { | /* check segment type */ |
| new_ip &= 0xffff; | if (!cs_sel.desc.s) { |
| } | VERBOSE(("JMPfar_pm: code segment is system segment")); |
| EXCEPTION(GP_EXCEPTION, cs_sel.idx); | |
| } | |
| if (!cs_sel.desc.u.seg.c) { | |
| VERBOSE(("JMPfar_pm: code segment is data segment")); | |
| EXCEPTION(GP_EXCEPTION, cs_sel.idx); | |
| } | |
| load_cs(call_sel.selector, &call_sel.desc, CPU_STAT_CPL); | /* check privilege level */ |
| SET_EIP(new_ip); | if (!cs_sel.desc.u.seg.ec) { |
| /* 下巻 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 { | } else { |
| DWORD temp_eip, temp_esp; | /* 下巻 p.120 4.8.1.2. */ |
| WORD temp_cs, temp_ss; | if (cs_sel.desc.dpl > CPU_STAT_CPL) { |
| VERBOSE(("JMPfar_pm: DPL(%d) > CPL(%d)", cs_sel.desc.dpl, CPU_STAT_CPL)); | |
| /* system descriptor */ | EXCEPTION(GP_EXCEPTION, cs_sel.idx); |
| switch (call_sel.desc.type) { | } |
| case CPU_SYSDESC_TYPE_CALL_16: | } |
| case CPU_SYSDESC_TYPE_CALL_32: | |
| VERBOSE(("CALL-GATE")); | |
| /* check privilege level */ | /* not present */ |
| if ((call_sel.desc.dpl < CPU_STAT_CPL) | if (selector_is_not_present(&cs_sel)) { |
| || (call_sel.desc.dpl < call_sel.rpl)) { | VERBOSE(("JMPfar_pm: selector is not present")); |
| EXCEPTION(GP_EXCEPTION, call_sel.idx); | EXCEPTION(NP_EXCEPTION, cs_sel.idx); |
| } | } |
| /* not present */ | /* out of range */ |
| if (selector_is_not_present(&call_sel)) { | if (callgate_sel->desc.u.gate.offset > cs_sel.desc.u.seg.limit) { |
| EXCEPTION(NP_EXCEPTION, call_sel.idx); | 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); |
| } | |
| /* parse code segment descriptor */ | load_cs(cs_sel.selector, &cs_sel.desc, CPU_STAT_CPL); |
| rv = parse_selector(&sel2, call_sel.desc.u.gate.selector); | SET_EIP(callgate_sel->desc.u.gate.offset); |
| if (rv < 0) { | } |
| EXCEPTION(GP_EXCEPTION, sel2.idx); | |
| } | |
| /* check code segment & privilege level */ | /*--- |
| if (!sel2.desc.s | * JMPfar: task gate |
| || !sel2.desc.u.seg.c | */ |
| || sel2.desc.dpl > CPU_STAT_CPL) { | static void |
| EXCEPTION(GP_EXCEPTION, sel2.idx); | JMPfar_pm_task_gate(selector_t *taskgate_sel) |
| } | { |
| selector_t tss_sel; | |
| int rv; | |
| /* not present */ | VERBOSE(("JMPfar_pm: TASK-GATE")); |
| if (selector_is_not_present(&sel2)) { | |
| EXCEPTION(NP_EXCEPTION, sel2.idx); | |
| } | |
| /* save register */ | /* check privilege level */ |
| temp_cs = CPU_CS; | if (taskgate_sel->desc.dpl < CPU_STAT_CPL) { |
| temp_ss = CPU_SS; | VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d)", taskgate_sel->desc.dpl, CPU_STAT_CPL)); |
| temp_eip = CPU_EIP; | EXCEPTION(GP_EXCEPTION, taskgate_sel->idx); |
| temp_esp = CPU_ESP; | } |
| if (sel2.desc.type == CPU_SYSDESC_TYPE_CALL_16) { | if (taskgate_sel->desc.dpl < taskgate_sel->rpl) { |
| temp_eip &= 0xffff; | VERBOSE(("JMPfar_pm: DPL(%d) < RPL(%d)", taskgate_sel->desc.dpl, taskgate_sel->rpl)); |
| temp_esp &= 0xffff; | EXCEPTION(GP_EXCEPTION, taskgate_sel->idx); |
| } | } |
| new_ip = call_sel.desc.u.gate.offset; | /* not present */ |
| if (selector_is_not_present(taskgate_sel)) { | |
| VERBOSE(("JMPfar_pm: selector is not present")); | |
| EXCEPTION(NP_EXCEPTION, taskgate_sel->idx); | |
| } | |
| /* out of range */ | /* parse tss selector */ |
| if (new_ip > sel2.desc.u.seg.limit) { | rv = parse_selector(&tss_sel, taskgate_sel->desc.u.gate.selector); |
| EXCEPTION(GP_EXCEPTION, 0); | 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); | |
| } | |
| if (!sel2.desc.u.seg.ec | /* check descriptor type */ |
| && (sel2.desc.dpl < CPU_STAT_CPL)) { | switch (tss_sel.desc.type) { |
| DWORD param[32]; // copy param | case CPU_SYSDESC_TYPE_TSS_16: |
| selector_t ss_sel; | case CPU_SYSDESC_TYPE_TSS_32: |
| DWORD tss_esp; | break; |
| WORD tss_ss; | |
| BYTE i; | case CPU_SYSDESC_TYPE_TSS_BUSY_16: |
| case CPU_SYSDESC_TYPE_TSS_BUSY_32: | |
| VERBOSE(("MORE-PRIVILEGE")); | VERBOSE(("JMPfar_pm: task is busy")); |
| /*FALLTHROUGH*/ | |
| get_stack_from_tss(sel2.desc.dpl, &tss_ss, &tss_esp); | default: |
| VERBOSE(("JMPfar_pm: invalid descriptor type (type = %d)", tss_sel.desc.type)); | |
| /* parse stack segment descriptor */ | EXCEPTION(GP_EXCEPTION, tss_sel.idx); |
| rv = parse_selector(&ss_sel, tss_ss); | break; |
| if (rv < 0) { | } |
| EXCEPTION(TS_EXCEPTION, ss_sel.idx); | |
| } | |
| /* check privilege level */ | /* not present */ |
| if ((ss_sel.rpl != sel2.desc.dpl) | if (selector_is_not_present(&tss_sel)) { |
| || (ss_sel.desc.dpl != sel2.desc.dpl) | VERBOSE(("JMPfar_pm: selector is not present")); |
| || !ss_sel.desc.s | EXCEPTION(NP_EXCEPTION, tss_sel.idx); |
| || !ss_sel.desc.u.seg.wr) { | } |
| EXCEPTION(TS_EXCEPTION, ss_sel.idx); | |
| } | |
| /* not present */ | task_switch(&tss_sel, TASK_SWITCH_JMP); |
| 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); | * JMPfar: TSS |
| */ | |
| static void | |
| JMPfar_pm_tss(selector_t *tss_sel) | |
| { | |
| /* dump param */ | VERBOSE(("JMPfar_pm: TASK-STATE-SEGMENT")); |
| 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); | /* check privilege level */ |
| PUSH0_16(temp_eip); | if (tss_sel->desc.dpl < CPU_STAT_CPL) { |
| } | VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d)", tss_sel->desc.dpl, CPU_STAT_CPL)); |
| } else { | EXCEPTION(GP_EXCEPTION, tss_sel->idx); |
| VERBOSE(("SAME-PRIVILEGE")); | } |
| 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); | |
| } | |
| if (call_sel.desc.type == CPU_SYSDESC_TYPE_CALL_32){ | /* not present */ |
| CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8); | if (selector_is_not_present(tss_sel)) { |
| VERBOSE(("JMPfar_pm: selector is not present")); | |
| EXCEPTION(NP_EXCEPTION, tss_sel->idx); | |
| } | |
| load_cs(sel2.selector, &sel2.desc, CPU_STAT_CPL); | task_switch(tss_sel, TASK_SWITCH_JMP); |
| 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); | * CALLfar_pm |
| } | */ |
| } | static void CALLfar_pm_code_segment(selector_t *call_sel, DWORD new_ip); |
| break; | static void CALLfar_pm_call_gate(selector_t *call_sel); |
| static void CALLfar_pm_task_gate(selector_t *call_sel); | |
| static void CALLfar_pm_tss(selector_t *call_sel); | |
| case CPU_SYSDESC_TYPE_TASK: | void |
| VERBOSE(("TASK-GATE")); | CALLfar_pm(WORD selector, DWORD new_ip) |
| { | |
| selector_t call_sel; | |
| int rv; | |
| /* check privilege level */ | VERBOSE(("CALLfar_pm: old EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_PREV_EIP, CPU_SS, CPU_ESP)); |
| if ((call_sel.desc.dpl < CPU_STAT_CPL) | VERBOSE(("CALLfar_pm: selector = 0x%04x, new_ip = 0x%08x", selector, new_ip)); |
| || (call_sel.desc.dpl < call_sel.rpl)) { | |
| EXCEPTION(GP_EXCEPTION, call_sel.idx); | |
| } | |
| /* not present */ | rv = parse_selector(&call_sel, selector); |
| if (selector_is_not_present(&call_sel)) { | if (rv < 0) { |
| EXCEPTION(NP_EXCEPTION, call_sel.idx); | VERBOSE(("CALLfar_pm: parse_selector (selector = %04x, rv = %d)", selector, rv)); |
| } | EXCEPTION(GP_EXCEPTION, call_sel.idx); |
| } | |
| /* tss descriptor */ | if (call_sel.desc.s) { |
| rv = parse_selector(&sel2, call_sel.desc.u.gate.selector); | /* code or data segment descriptor */ |
| if (rv < 0 || sel2.ldt) { | VERBOSE(("CALLfar_pm: code or data segment descriptor")); |
| EXCEPTION(GP_EXCEPTION, sel2.idx); | |
| } | |
| /* check descriptor type */ | if (!call_sel.desc.u.seg.c) { |
| switch (sel2.desc.type) { | /* data segment */ |
| case CPU_SYSDESC_TYPE_TSS_16: | VERBOSE(("CALLfar_pm: data segment")); |
| case CPU_SYSDESC_TYPE_TSS_32: | EXCEPTION(GP_EXCEPTION, call_sel.idx); |
| 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; | |
| } | |
| /* not present */ | /* code segment descriptor */ |
| if (selector_is_not_present(&sel2)) { | CALLfar_pm_code_segment(&call_sel, new_ip); |
| EXCEPTION(NP_EXCEPTION, sel2.idx); | } else { |
| } | /* system descriptor */ |
| VERBOSE(("CALLfar_pm: system descriptor")); | |
| task_switch(&sel2, 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 */ | case CPU_SYSDESC_TYPE_TASK: |
| if (CPU_EIP > CPU_STAT_CS_LIMIT) { | CALLfar_pm_task_gate(&call_sel); |
| EXCEPTION(GP_EXCEPTION, 0); | |
| } | |
| break; | break; |
| case CPU_SYSDESC_TYPE_TSS_16: | case CPU_SYSDESC_TYPE_TSS_16: |
| case CPU_SYSDESC_TYPE_TSS_32: | case CPU_SYSDESC_TYPE_TSS_32: |
| VERBOSE(("TASK-STATE-SEGMENT")); | CALLfar_pm_tss(&call_sel); |
| /* 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(&call_sel)) { | |
| EXCEPTION(NP_EXCEPTION, call_sel.idx); | |
| } | |
| task_switch(&call_sel, TASK_SWITCH_CALL); | |
| /* out of range */ | |
| if (CPU_EIP > CPU_STAT_CS_LIMIT) { | |
| EXCEPTION(GP_EXCEPTION, 0); | |
| } | |
| break; | break; |
| case CPU_SYSDESC_TYPE_TSS_BUSY_16: | case CPU_SYSDESC_TYPE_TSS_BUSY_16: |
| Line 603 CALLfar_pm(WORD selector, DWORD new_ip) | Line 374 CALLfar_pm(WORD selector, DWORD new_ip) |
| VERBOSE(("CALLfar_pm: task is busy")); | VERBOSE(("CALLfar_pm: task is busy")); |
| /*FALLTHROUGH*/ | /*FALLTHROUGH*/ |
| default: | default: |
| VERBOSE(("CALLfar_pm: invalid descriptor type (type = %d)", call_sel.desc.type)); | |
| EXCEPTION(GP_EXCEPTION, call_sel.idx); | EXCEPTION(GP_EXCEPTION, call_sel.idx); |
| break; | break; |
| } | } |
| } | } |
| VERBOSE(("CALLfar_pm: new EIP = %04x:%08x, new ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP)); | |
| } | } |
| /* | /*--- |
| * 4.3.6. 特権レベル間のリターン操作 | * CALLfar_pm: code segment |
| * | |
| * 1. 特権チェックを実行する。 | |
| * 2. CS レジスタと EIP レジスタにコール前の値をリストアする。 | |
| * 3. RET 命令にオプション引き数の n がある場合は、パラメータをスタックから | |
| * 開放するため、n オペランドで指定されたバイト数だけスタック・ポインタを | |
| * インクリメントする。コール・ゲート・ディスクリプタが、スタック間で | |
| * 1 つ以上のパラメータをコピーするよう指定している場合は、RET n 命令を | |
| * 使用して両スタックからパラメータを開放しなければならない。n オペランド | |
| * には、各スタック壌でパラメータが占有するバイト数を指定する。 | |
| * リターン時に、プロセッサは各スタックに対して n だけ ESP をインクリメント | |
| * し、これらのパラメータをスタックから効率よく削除する。 | |
| * 4. SS レジスタと ESP レジスタに、コール前の値をリストアする。これで、 | |
| * コール元プロシージャのスタックへ切り替えられる。 | |
| * 5. RET 命令にオプション引き数の n がある場合は、パラメータをスタックから | |
| * 開放するため、n オペランドで指定されたバイト数だけスタック・ポインタを | |
| * インクリメントする(ステップ 3 の説明を参照)。 | |
| * 6. コール元プロシージャの実行を再開する。 | |
| */ | */ |
| void | static void |
| RETfar_pm(DWORD nbytes) | CALLfar_pm_code_segment(selector_t *cs_sel, DWORD new_ip) |
| { | { |
| selector_t ret_sel; | DWORD sp; |
| int rv; | |
| DWORD new_ip; | |
| WORD selector; | |
| VERBOSE(("RETfar_pm: EIP = 0x%08x, nbytes = %d", CPU_PREV_EIP, nbytes)); | VERBOSE(("CALLfar_pm: CODE-SEGMENT")); |
| if (CPU_INST_OP32) { | /* check privilege level */ |
| CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8 + nbytes); | if (!cs_sel->desc.u.seg.ec) { |
| if (CPU_STAT_SS32) { | VERBOSE(("CALLfar_pm: NON-CONFORMING-CODE-SEGMENT")); |
| new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP); | /* 下巻 p.119 4.8.1.1. */ |
| selector = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP + 4); | if (cs_sel->rpl > CPU_STAT_CPL) { |
| } else { | VERBOSE(("CALLfar_pm: RPL(%d) > CPL(%d)", cs_sel->rpl, CPU_STAT_CPL)); |
| new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_SP); | EXCEPTION(GP_EXCEPTION, cs_sel->idx); |
| selector = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_SP + 4); | } |
| 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 { | } else { |
| CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 4 + nbytes); | VERBOSE(("CALLfar_pm: CONFORMING-CODE-SEGMENT")); |
| if (CPU_STAT_SS32) { | /* 下巻 p.120 4.8.1.2. */ |
| new_ip = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP); | if (cs_sel->desc.dpl > CPU_STAT_CPL) { |
| selector = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP + 2); | VERBOSE(("CALLfar_pm: DPL(%d) > CPL(%d)", cs_sel->desc.dpl, CPU_STAT_CPL)); |
| } else { | EXCEPTION(GP_EXCEPTION, cs_sel->idx); |
| new_ip = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_SP); | |
| selector = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_SP + 2); | |
| } | } |
| } | } |
| rv = parse_selector(&ret_sel, selector); | /* not present */ |
| if (rv < 0) { | if (selector_is_not_present(cs_sel)) { |
| EXCEPTION(GP_EXCEPTION, ret_sel.idx); | VERBOSE(("CALLfar_pm: selector is not present")); |
| EXCEPTION(NP_EXCEPTION, cs_sel->idx); | |
| } | } |
| /* check code segment descriptor */ | if (CPU_STAT_SS32) { |
| if (!ret_sel.desc.s || !ret_sel.desc.u.seg.c) { | sp = CPU_ESP; |
| EXCEPTION(GP_EXCEPTION, ret_sel.idx); | } else { |
| sp = CPU_SP; | |
| } | |
| if (CPU_INST_OP32) { | |
| CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), 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_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), 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); | |
| SET_EIP(new_ip); | |
| } | |
| /*--- | |
| * CALLfar_pm: call gate | |
| */ | |
| static void CALLfar_pm_call_gate_same_privilege(selector_t *call_sel, selector_t *cs_sel); | |
| static void CALLfar_pm_call_gate_more_privilege(selector_t *call_sel, selector_t *cs_sel); | |
| static void | |
| CALLfar_pm_call_gate(selector_t *callgate_sel) | |
| { | |
| selector_t cs_sel; | |
| int rv; | |
| VERBOSE(("CALLfar_pm: CALL-GATE")); | |
| /* check privilege level */ | /* check privilege level */ |
| if (ret_sel.rpl < CPU_STAT_CPL | if (callgate_sel->desc.dpl < CPU_STAT_CPL) { |
| || (ret_sel.desc.u.seg.ec && (ret_sel.desc.dpl > ret_sel.rpl))) { | VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", callgate_sel->desc.dpl, CPU_STAT_CPL)); |
| EXCEPTION(GP_EXCEPTION, ret_sel.idx); | 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 */ | /* not present */ |
| if (selector_is_not_present(&ret_sel)) { | if (selector_is_not_present(callgate_sel)) { |
| EXCEPTION(NP_EXCEPTION, ret_sel.idx); | VERBOSE(("CALLfar_pm: selector is not present")); |
| EXCEPTION(NP_EXCEPTION, callgate_sel->idx); | |
| } | } |
| if (ret_sel.rpl > CPU_STAT_CPL) { | /* parse code segment descriptor */ |
| selector_t ss_sel; | rv = parse_selector(&cs_sel, callgate_sel->desc.u.gate.selector); |
| selector_t temp_sel; | if (rv < 0) { |
| DWORD new_sp; | VERBOSE(("CALLfar_pm: parse_selector (selector = %04x, rv = %d)", callgate_sel->desc.u.gate.selector, rv)); |
| WORD new_ss; | EXCEPTION(GP_EXCEPTION, cs_sel.idx); |
| int i; | } |
| VERBOSE(("RETURN-OUTER-PRIVILEGE-LEVEL")); | /* check segment type */ |
| if (!cs_sel.desc.s) { | |
| VERBOSE(("CALLfar_pm: code segment is system segment")); | |
| EXCEPTION(GP_EXCEPTION, cs_sel.idx); | |
| } | |
| if (!cs_sel.desc.u.seg.c) { | |
| VERBOSE(("CALLfar_pm: code segment is data segment")); | |
| EXCEPTION(GP_EXCEPTION, cs_sel.idx); | |
| } | |
| if (CPU_INST_OP32) { | /* check privilege level */ |
| CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 16 + nbytes); | if (cs_sel.desc.dpl > CPU_STAT_CPL) { |
| if (CPU_STAT_SS32) { | VERBOSE(("CALLfar_pm: DPL(%d) > CPL(%d)", cs_sel.desc.dpl, CPU_STAT_CPL)); |
| new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP + 8 + nbytes); | EXCEPTION(GP_EXCEPTION, cs_sel.idx); |
| 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)); | |
| } | |
| } | |
| rv = parse_selector(&ss_sel, new_ss); | /* not present */ |
| if (rv < 0) { | if (selector_is_not_present(&cs_sel)) { |
| EXCEPTION(GP_EXCEPTION, ss_sel.idx); | VERBOSE(("CALLfar_pm: selector is not present")); |
| } | EXCEPTION(NP_EXCEPTION, cs_sel.idx); |
| } | |
| /* check stack segment descriptor */ | /* out of range */ |
| /* check privilege level */ | if (callgate_sel->desc.u.gate.offset > cs_sel.desc.u.seg.limit) { |
| if ((ss_sel.rpl != ret_sel.rpl) | 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)); |
| || (ss_sel.desc.dpl != ret_sel.rpl) | EXCEPTION(GP_EXCEPTION, 0); |
| || !ss_sel.desc.s | } |
| || !ss_sel.desc.u.seg.wr) { | |
| EXCEPTION(GP_EXCEPTION, ss_sel.idx); | if (!cs_sel.desc.u.seg.ec && (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); | |
| } | |
| } | |
| /*--- | |
| * CALLfar_pm: call gate (SAME-PRIVILEGE) | |
| */ | |
| static void | |
| CALLfar_pm_call_gate_same_privilege(selector_t *callgate_sel, selector_t *cs_sel) | |
| { | |
| DWORD sp; | |
| VERBOSE(("CALLfar_pm: SAME-PRIVILEGE")); | |
| if (CPU_STAT_SS32) { | |
| sp = CPU_ESP; | |
| } else { | |
| sp = CPU_SP; | |
| } | |
| if (callgate_sel->desc.type == CPU_SYSDESC_TYPE_CALL_32) { | |
| CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 8); | |
| PUSH0_32(CPU_CS); | |
| PUSH0_32(CPU_EIP); | |
| load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL); | |
| SET_EIP(callgate_sel->desc.u.gate.offset); | |
| } else { | |
| CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 4); | |
| PUSH0_16(CPU_CS); | |
| PUSH0_16(CPU_IP); | |
| load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL); | |
| SET_EIP(callgate_sel->desc.u.gate.offset); | |
| } | |
| } | |
| /*--- | |
| * CALLfar_pm: call gate (MORE-PRIVILEGE) | |
| */ | |
| static void | |
| CALLfar_pm_call_gate_more_privilege(selector_t *callgate_sel, selector_t *cs_sel) | |
| { | |
| DWORD param[32]; /* copy param */ | |
| selector_t ss_sel; | |
| DWORD old_eip, old_esp; | |
| DWORD new_esp; | |
| WORD old_cs, old_ss; | |
| WORD 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: 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: 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 (!ss_sel.desc.s) { | |
| VERBOSE(("CALLfar_pm: stack segment is system segment")); | |
| EXCEPTION(TS_EXCEPTION, ss_sel.idx); | |
| } | |
| if (ss_sel.desc.u.seg.c) { | |
| VERBOSE(("CALLfar_pm: stack segment is code segment")); | |
| EXCEPTION(TS_EXCEPTION, ss_sel.idx); | |
| } | |
| if (!ss_sel.desc.u.seg.wr) { | |
| 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)); | |
| if (callgate_sel->desc.type == CPU_SYSDESC_TYPE_CALL_32) { | |
| CHECK_STACK_PUSH(&ss_sel.desc, new_esp, 16 + param_count * 4); | |
| /* 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 = new_esp; | |
| } | |
| load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->desc.dpl); | |
| SET_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 { | |
| CHECK_STACK_PUSH(&ss_sel.desc, new_esp, 8 + param_count * 2); | |
| /* 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 = new_esp; | |
| } | |
| load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->desc.dpl); | |
| SET_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); | |
| } | |
| /*--- | |
| * 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); | |
| } | |
| /*------------------------------------------------------------------------------ | |
| * RETfar_pm | |
| */ | |
| void | |
| RETfar_pm(DWORD nbytes) | |
| { | |
| selector_t cs_sel, ss_sel, temp_sel; | |
| DWORD sp; | |
| DWORD new_ip, new_sp; | |
| WORD 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) { | |
| CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, nbytes + 8); | |
| new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, sp); | |
| new_cs = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4); | |
| } else { | |
| CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), 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, %s)", cs_sel.selector, rv)); | |
| EXCEPTION(GP_EXCEPTION, cs_sel.idx); | |
| } | |
| /* check segment type */ | |
| if (!cs_sel.desc.s) { | |
| VERBOSE(("RETfar_pm: return to system segment")); | |
| EXCEPTION(GP_EXCEPTION, cs_sel.idx); | |
| } | |
| if (!cs_sel.desc.u.seg.c) { | |
| 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 (!cs_sel.desc.u.seg.ec && (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 += nbytes; | |
| } | |
| load_cs(cs_sel.selector, &cs_sel.desc, CPU_STAT_CPL); | |
| SET_EIP(new_ip); | |
| } else { | |
| VERBOSE(("RETfar_pm: RETURN-OUTER-PRIVILEGE-LEVEL")); | |
| if (CPU_INST_OP32) { | |
| CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), 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 { | |
| CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), 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, %s)", ss_sel.selector, rv)); | |
| EXCEPTION(GP_EXCEPTION, ss_sel.idx); | |
| } | |
| /* check stack segment descriptor */ | |
| if (!ss_sel.desc.s) { | |
| VERBOSE(("RETfar_pm: stack segment is system segment")); | |
| EXCEPTION(GP_EXCEPTION, cs_sel.idx); | |
| } | |
| if (ss_sel.desc.u.seg.c) { | |
| VERBOSE(("RETfar_pm: stack segment is code segment")); | |
| EXCEPTION(GP_EXCEPTION, cs_sel.idx); | |
| } | |
| if (!ss_sel.desc.u.seg.wr) { | |
| 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: 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: DPL[SS](%d) != RPL[CS](%d)", ss_sel.desc.dpl, cs_sel.rpl)); | |
| EXCEPTION(GP_EXCEPTION, cs_sel.idx); | |
| } | |
| /* not present */ | /* not present */ |
| if (selector_is_not_present(&ss_sel)) { | if (selector_is_not_present(&ss_sel)) { |
| VERBOSE(("RETfar_pm: stack segment is not present")); | |
| EXCEPTION(SS_EXCEPTION, ss_sel.idx); | EXCEPTION(SS_EXCEPTION, ss_sel.idx); |
| } | } |
| /* check code segment limit */ | /* 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); | EXCEPTION(GP_EXCEPTION, 0); |
| } | } |
| /* set new register */ | VERBOSE(("RETfar_pm: new_ip = %08x, new_cs = %04x", new_ip, cs_sel.selector)); |
| load_cs(ret_sel.selector, &ret_sel.desc, ret_sel.rpl); | VERBOSE(("RETfar_pm: new_sp = %08x, new_ss = %04x", new_sp, ss_sel.selector)); |
| load_cs(cs_sel.selector, &cs_sel.desc, cs_sel.rpl); | |
| SET_EIP(new_ip); | SET_EIP(new_ip); |
| load_ss(ss_sel.selector, &ss_sel.desc, ret_sel.rpl); | |
| CPU_ESP = new_sp; | load_ss(ss_sel.selector, &ss_sel.desc, cs_sel.rpl); |
| if (CPU_STAT_SS32) { | |
| CPU_ESP = new_sp + nbytes; | |
| } else { | |
| CPU_SP = new_sp + nbytes; | |
| } | |
| /* check segment register */ | /* check segment register */ |
| for (i = 0; i < CPU_SEGREG_NUM; i++) { | for (i = 0; i < CPU_SEGREG_NUM; i++) { |
| if (i == CPU_CS_INDEX || i == CPU_SS_INDEX) | 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; | continue; |
| } | |
| rv = parse_selector(&temp_sel, CPU_REGS_SREG(i)); | rv = parse_selector(&temp_sel, CPU_REGS_SREG(i)); |
| if (rv < 0) { | if (rv < 0) { |
| /* segment register is invalid */ | |
| CPU_REGS_SREG(i) = 0; | CPU_REGS_SREG(i) = 0; |
| CPU_STAT_SREG(i).valid = 0; | CPU_STAT_SREG_CLEAR(i); |
| continue; | continue; |
| } | } |
| /* check privilege level */ | valid = TRUE; |
| if (!temp_sel.desc.s | if (!temp_sel.desc.s) { |
| || (temp_sel.desc.u.seg.c && temp_sel.desc.u.seg.wr) | /* system segment */ |
| || ((!temp_sel.desc.u.seg.c || !temp_sel.desc.u.seg.ec) | valid = FALSE; |
| && ((temp_sel.desc.dpl < CPU_STAT_CPL) || (temp_sel.desc.dpl < temp_sel.rpl)))) { | } |
| 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_REGS_SREG(i) = 0; |
| CPU_STAT_SREG(i).valid = 0; | CPU_STAT_SREG(i).valid = 0; |
| continue; | |
| } | } |
| } | } |
| 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(DWORD new_cs, DWORD new_ip, DWORD new_flags); | |
| static void IRET_pm_protected_mode_return_same_privilege(selector_t *cs_sel, DWORD new_ip, DWORD new_flags); | |
| static void IRET_pm_protected_mode_return_outer_privilege(selector_t *cs_sel, DWORD new_ip, DWORD new_flags); | |
| static void IRET_pm_return_to_vm86(DWORD new_cs, DWORD new_ip, DWORD new_flags); | |
| static void IRET_pm_return_from_vm86(DWORD new_cs, DWORD new_ip, DWORD new_flags); | |
| void | |
| IRET_pm(void) | |
| { | |
| DWORD sp; | |
| DWORD new_ip, new_flags; | |
| WORD new_cs; | |
| int rv; | |
| 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 (CPU_INST_OP32) { |
| if (new_ip > ret_sel.desc.u.seg.limit) { | CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 12); |
| EXCEPTION(GP_EXCEPTION, 0); | new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, sp); |
| } | new_cs = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4); |
| POP0_32(CPU_EIP); | new_flags = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 8); |
| POP0_32(CPU_CS); | |
| } else { | } else { |
| new_ip &= 0xffff; | CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 6); |
| if (new_ip > ret_sel.desc.u.seg.limit) { | new_ip = cpu_vmemoryread_w(CPU_SS_INDEX, sp); |
| EXCEPTION(GP_EXCEPTION, 0); | new_cs = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 2); |
| } | new_flags = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4); |
| POP0_16(CPU_EIP); | } |
| POP0_16(CPU_CS); | |
| 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) { | |
| /* 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; |
| int rv; | int rv; |
| DWORD new_ip, new_flags; | WORD new_tss; |
| 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) { | new_tss = get_backlink_selector_from_tss(); |
| /* 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); | |
| set_flags(new_flags, I_FLAG); | rv = parse_selector(&tss_sel, new_tss); |
| } | if (rv < 0 || tss_sel.ldt) { |
| CPU_SET_SEGREG(CPU_CS_INDEX, new_cs); | VERBOSE(("IRET_pm: parse_selector (selector = %04x, rv = %d, %cDT)", tss_sel.selector, rv, tss_sel.ldt ? 'L' : 'G')); |
| SET_EIP(new_ip); | EXCEPTION(GP_EXCEPTION, tss_sel.idx); |
| return; | } |
| } | |
| VERBOSE(("trap to virtual-8086 monitor: PE=1, VM=1, IOPL<3")); | /* check system segment */ |
| if (tss_sel.desc.s) { | |
| VERBOSE(("IRET_pm: task segment is %d 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); | |
| } | |
| /*--- | |
| * IRET_pm: PROTECTED-MODE-RETURN | |
| */ | |
| static void | |
| IRET_pm_protected_mode_return(DWORD new_cs, DWORD new_ip, DWORD new_flags) | |
| { | |
| selector_t cs_sel; | |
| int rv; | |
| /* PROTECTED-MODE-RETURN */ | |
| VERBOSE(("IRET_pm: PE=1, VM=0 in flags image")); | |
| 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); | |
| } | |
| /* check code segment descriptor */ | |
| if (!cs_sel.desc.s) { | |
| VERBOSE(("IRET_pm: return code segment is system segment")); | |
| EXCEPTION(GP_EXCEPTION, cs_sel.idx); | |
| } | |
| if (!cs_sel.desc.u.seg.c) { | |
| VERBOSE(("IRET_pm: return code segment is data segment")); | |
| EXCEPTION(GP_EXCEPTION, cs_sel.idx); | |
| } | |
| /* 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 (cs_sel.desc.u.seg.ec && (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); | |
| } | |
| /* 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(selector_t *cs_sel, DWORD new_ip, DWORD new_flags) | |
| { | |
| DWORD mask; | |
| DWORD 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); | 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(); | mask = 0; |
| rv = parse_selector(&iret_sel, new_cs); | if (CPU_INST_OP32) |
| if (rv < 0 || iret_sel.ldt) { | mask |= RF_FLAG; |
| EXCEPTION(GP_EXCEPTION, iret_sel.idx); | 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; | |
| } | |
| /* check system segment */ | /* set new register */ |
| if (iret_sel.desc.s) { | load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL); |
| EXCEPTION(GP_EXCEPTION, iret_sel.idx); | SET_EIP(new_ip); |
| } | |
| switch (iret_sel.desc.type) { | |
| case CPU_SYSDESC_TYPE_TSS_BUSY_16: | |
| case CPU_SYSDESC_TYPE_TSS_BUSY_32: | |
| break; | |
| case CPU_SYSDESC_TYPE_TSS_16: | set_eflags(new_flags, mask); |
| case CPU_SYSDESC_TYPE_TSS_32: | |
| VERBOSE(("IRET_pm: task is not busy")); | |
| /*FALLTHROUGH*/ | |
| default: | |
| EXCEPTION(GP_EXCEPTION, iret_sel.idx); | |
| break; | |
| } | |
| /* not present */ | if (CPU_STAT_SS32) { |
| if (selector_is_not_present(&iret_sel)) { | CPU_ESP += stacksize; |
| EXCEPTION(NP_EXCEPTION, iret_sel.idx); | } else { |
| } | CPU_SP += stacksize; |
| } | |
| } | |
| task_switch(&iret_sel, TASK_SWITCH_IRET); | /*--- |
| * IRET_pm: OUTER-PRIVILEGE | |
| */ | |
| static void | |
| IRET_pm_protected_mode_return_outer_privilege(selector_t *cs_sel, DWORD new_ip, DWORD new_flags) | |
| { | |
| descriptor_t *dp; | |
| selector_t ss_sel; | |
| DWORD mask; | |
| DWORD sp; | |
| DWORD new_sp; | |
| WORD new_ss; | |
| int rv; | |
| int i; | |
| /* out of range */ | VERBOSE(("IRET_pm: RETURN-OUTER-PRIVILEGE-LEVEL")); |
| if (CPU_EIP > CPU_STAT_CS_LIMIT) { | |
| EXCEPTION(GP_EXCEPTION, 0); | if (CPU_STAT_SS32) { |
| } | sp = CPU_ESP; |
| } else { | } else { |
| sp = CPU_SP; | |
| } | |
| if (CPU_INST_OP32) { | |
| CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 20); | |
| new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 12); | |
| new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 16); | |
| } else { | |
| CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), 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, ss_sel.idx); | |
| } | |
| /* check privilege level */ | |
| if (ss_sel.rpl != cs_sel->rpl) { | |
| VERBOSE(("IRET_pm: RPL[SS](%d) != RPL[CS](%d)", ss_sel.rpl, cs_sel->rpl)); | |
| EXCEPTION(GP_EXCEPTION, ss_sel.idx); | |
| } | |
| if (ss_sel.desc.dpl != cs_sel->rpl) { | |
| VERBOSE(("IRET_pm: DPL[SS](%d) != RPL[CS](%d)", ss_sel.desc.dpl, cs_sel->rpl)); | |
| EXCEPTION(GP_EXCEPTION, ss_sel.idx); | |
| } | |
| /* check stack segment descriptor */ | |
| if (!ss_sel.desc.s) { | |
| VERBOSE(("IRET_pm: stack segment is system segment")); | |
| EXCEPTION(GP_EXCEPTION, ss_sel.idx); | |
| } | |
| if (ss_sel.desc.u.seg.c) { | |
| VERBOSE(("IRET_pm: stack segment is code segment")); | |
| EXCEPTION(GP_EXCEPTION, ss_sel.idx); | |
| } | |
| if (!ss_sel.desc.u.seg.wr) { | |
| 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) { | if (CPU_INST_OP32) { |
| CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 12); | mask |= VM_FLAG|VIF_FLAG|VIP_FLAG; |
| 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); | |
| } | } |
| } | |
| if ((CPU_STAT_CPL == 0) && (new_flags & VM_FLAG)) { | /* set new register */ |
| /* RETURN-TO-VIRTUAL-8086-MODE */ | load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->rpl); |
| descriptor_t sd; | SET_EIP(new_ip); |
| 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_eflags(new_flags, IOPL_FLAG|RF_FLAG); | set_eflags(new_flags, mask); |
| } else { | |
| DWORD mask; | |
| /* PROTECTED-MODE-RETURN */ | load_ss(ss_sel.selector, &ss_sel.desc, cs_sel->rpl); |
| VERBOSE(("PE=1, VM=0 in flags image")); | if (CPU_STAT_SS32) { |
| CPU_ESP = new_sp; | |
| } else { | |
| CPU_SP = new_sp; | |
| } | |
| rv = parse_selector(&iret_sel, new_cs); | /* check segment register */ |
| if (rv < 0) { | for (i = 0; i < CPU_SEGREG_NUM; i++) { |
| EXCEPTION(GP_EXCEPTION, iret_sel.idx); | 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; | |
| } | } |
| } | |
| } | |
| } | |
| /* check code segment descriptor */ | /*--- |
| if (!iret_sel.desc.s || !iret_sel.desc.u.seg.c) { | * IRET_pm: new_flags & VM_FLAG |
| EXCEPTION(GP_EXCEPTION, iret_sel.idx); | */ |
| } | static void |
| IRET_pm_return_to_vm86(DWORD new_cs, DWORD new_ip, DWORD new_flags) | |
| { | |
| WORD segsel[CPU_SEGREG_NUM]; | |
| DWORD sp; | |
| DWORD new_sp; | |
| int i; | |
| /* check privilege level */ | VERBOSE(("IRET_pm: Interrupt procedure was in virtual-8086 mode: PE=1, VM=1 in flags image")); |
| 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); | |
| } | |
| /* not present */ | if (CPU_STAT_CPL != 0) { |
| if (selector_is_not_present(&iret_sel)) { | ia32_panic("IRET_pm: CPL != 0"); |
| EXCEPTION(NP_EXCEPTION, iret_sel.idx); | } |
| } | |
| if (iret_sel.rpl > CPU_STAT_CPL) { | if (!CPU_INST_OP32) { |
| /* RETURN-OUTER-PRIVILEGE-LEVEL */ | ia32_panic("IRET_pm: 16bit mode"); |
| 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 (CPU_STAT_SS32) { |
| if (rv < 0) { | sp = CPU_ESP; |
| EXCEPTION(GP_EXCEPTION, ss_sel.idx); | } else { |
| } | sp = CPU_SP; |
| } | |
| CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 36); | |
| 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; | |
| for (i = 0; i < CPU_SEGREG_NUM; i++) { | |
| CPU_REGS_SREG(i) = segsel[i]; | |
| CPU_STAT_SREG_INIT(i); | |
| } | |
| /* check stack segment descriptor */ | /* to VM86 mode */ |
| /* check privilege level */ | set_eflags(new_flags, IOPL_FLAG|I_FLAG|VM_FLAG|RF_FLAG); |
| 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 */ | new_sp &= 0xffff; |
| if (selector_is_not_present(&ss_sel)) { | new_ip &= 0xffff; |
| EXCEPTION(SS_EXCEPTION, ss_sel.idx); | |
| } | |
| } else { | |
| VERBOSE(("RETURN-TO-SAME-PRIVILEGE-LEVEL")); | |
| } | |
| /* check code segment limit */ | CPU_ESP = new_sp; |
| if (new_ip > iret_sel.desc.u.seg.limit) { | SET_EIP(new_ip); |
| EXCEPTION(GP_EXCEPTION, 0); | } |
| } | |
| /* set new register */ | /*--- |
| load_cs(iret_sel.selector, &iret_sel.desc, iret_sel.rpl); | * IRET_pm: VM_FLAG |
| SET_EIP(new_ip); | */ |
| static void | |
| mask = 0; | IRET_pm_return_from_vm86(DWORD new_cs, DWORD new_ip, DWORD new_flags) |
| if (CPU_INST_OP32) | { |
| mask |= RF_FLAG; | DWORD stacksize; |
| 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); | |
| if (iret_sel.rpl > CPU_STAT_CPL) { | VERBOSE(("IRET_pm: virtual-8086 mode: VM=1")); |
| selector_t temp_sel; | |
| int i; | if (CPU_STAT_IOPL == CPU_IOPL3) { |
| VERBOSE(("IRET_pm: virtual-8086 mode: IOPL=3")); | |
| /* RETURN-OUTER-PRIVILEGE-LEVEL */ | if (CPU_INST_OP32) { |
| /* check segment register */ | stacksize = 12; |
| for (i = 0; i < CPU_SEGREG_NUM; i++) { | } else { |
| if ((i == CPU_CS_INDEX) || (i == CPU_SS_INDEX)) | stacksize = 6; |
| continue; | } |
| if (CPU_STAT_SS32) { | |
| rv = parse_selector(&temp_sel, CPU_REGS_SREG(i)); | CPU_ESP += stacksize; |
| if (rv < 0) { | } else { |
| CPU_REGS_SREG(i) = 0; | CPU_SP += stacksize; |
| 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; | |
| } | |
| } | |
| } | |
| } | } |
| set_eflags(new_flags, I_FLAG|RF_FLAG); | |
| CPU_SET_SEGREG(CPU_CS_INDEX, new_cs); | |
| SET_EIP(new_ip); | |
| return; | |
| } | } |
| VERBOSE(("IRET_pm: trap to virtual-8086 monitor: VM=1, IOPL<3")); | |
| EXCEPTION(GP_EXCEPTION, 0); | |
| } | } |