| version 1.1, 2003/12/08 00:55:31 | version 1.24, 2011/12/29 13:32:12 | 
| Line 1 | Line 1 | 
 | /*      $Id$    */ |  | 
 |  |  | 
 | /* | /* | 
 | * Copyright (c) 2003 NONAKA Kimihiro | * Copyright (c) 2003 NONAKA Kimihiro | 
 | * All rights reserved. | * All rights reserved. | 
| Line 12 | Line 10 | 
 | * 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | 
 | *    notice, this list of conditions and the following disclaimer in the | *    notice, this list of conditions and the following disclaimer in the | 
 | *    documentation and/or other materials provided with the distribution. | *    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 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 
 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
| Line 34 | Line 30 | 
 | #include "ctrlxfer.h" | #include "ctrlxfer.h" | 
 |  |  | 
 |  |  | 
| void | /*------------------------------------------------------------------------------ | 
| JMPfar_pm(WORD selector, DWORD new_ip) | * JMPfar_pm | 
|  | */ | 
|  | static void CPUCALL JMPfar_pm_code_segment(const selector_t *cs_sel, UINT32 new_ip); | 
|  | static void CPUCALL JMPfar_pm_call_gate(const selector_t *callgate_sel); | 
|  | static void CPUCALL JMPfar_pm_task_gate(selector_t *taskgate_sel); | 
|  | static void CPUCALL JMPfar_pm_tss(selector_t *tss_sel); | 
|  |  | 
|  | void CPUCALL | 
|  | JMPfar_pm(UINT16 selector, UINT32 new_ip) | 
 | { | { | 
 | selector_t jmp_sel; | selector_t jmp_sel; | 
 | selector_t sel2; |  | 
 | int rv; | int rv; | 
 |  |  | 
 |  | 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)); | 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) { | 
 |  | VERBOSE(("JMPfar_pm: parse_selector (selector = %04x, rv = %d)", selector, rv)); | 
 | EXCEPTION(GP_EXCEPTION, jmp_sel.idx); | EXCEPTION(GP_EXCEPTION, jmp_sel.idx); | 
 | } | } | 
 |  |  | 
| if (jmp_sel.desc.s) { | if (!SEG_IS_SYSTEM(&jmp_sel.desc)) { | 
| /* code segment descriptor */ | 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 */ | /* 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)) { |  | 
| 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) { |  | 
| EXCEPTION(GP_EXCEPTION, jmp_sel.idx); |  | 
| } |  | 
| } |  | 
|  |  | 
| /* not present */ |  | 
| if (selector_is_not_present(&jmp_sel)) { |  | 
| 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) { |  | 
| EXCEPTION(GP_EXCEPTION, 0); |  | 
| } |  | 
|  |  | 
| 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")); | 
 |  |  | 
 | 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)) { |  | 
| EXCEPTION(GP_EXCEPTION, jmp_sel.idx); |  | 
| } |  | 
|  |  | 
| /* not present */ |  | 
| if (selector_is_not_present(&jmp_sel)) { |  | 
| EXCEPTION(NP_EXCEPTION, jmp_sel.idx); |  | 
| } |  | 
|  |  | 
| /* parse call gate selector */ |  | 
| rv = parse_selector(&sel2, jmp_sel.desc.u.gate.selector); |  | 
| if (rv < 0) { |  | 
| EXCEPTION(GP_EXCEPTION, sel2.idx); |  | 
| } |  | 
|  |  | 
| /* check code segment descriptor */ |  | 
| if (!sel2.desc.s || !sel2.desc.u.seg.c) { |  | 
| 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)) { |  | 
| EXCEPTION(GP_EXCEPTION, sel2.idx); |  | 
| } |  | 
| } else { |  | 
| /* ²¼´¬ p.120 4.8.1.2. */ |  | 
| if (sel2.desc.dpl > CPU_STAT_CPL) { |  | 
| EXCEPTION(GP_EXCEPTION, sel2.idx); |  | 
| } |  | 
| } |  | 
|  |  | 
| /* not present */ |  | 
| if (selector_is_not_present(&sel2)) { |  | 
| 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) { |  | 
| EXCEPTION(GP_EXCEPTION, 0); |  | 
| } |  | 
|  |  | 
| 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)) { |  | 
| EXCEPTION(GP_EXCEPTION, jmp_sel.idx); |  | 
| } |  | 
|  |  | 
| /* not present */ |  | 
| if (selector_is_not_present(&jmp_sel)) { |  | 
| 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) { |  | 
| 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: |  | 
| EXCEPTION(GP_EXCEPTION, sel2.idx); |  | 
| break; |  | 
| } |  | 
|  |  | 
| /* not present */ |  | 
| if (selector_is_not_present(&sel2)) { |  | 
| EXCEPTION(NP_EXCEPTION, sel2.idx); |  | 
| } |  | 
|  |  | 
| task_switch(&sel2, TASK_SWITCH_JMP); |  | 
|  |  | 
| /* out of range */ |  | 
| if (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)) { |  | 
| EXCEPTION(TS_EXCEPTION, jmp_sel.idx); |  | 
| } |  | 
|  |  | 
| /* not present */ |  | 
| if (selector_is_not_present(&jmp_sel)) { |  | 
| EXCEPTION(NP_EXCEPTION, jmp_sel.idx); |  | 
| } |  | 
|  |  | 
| task_switch(&jmp_sel, TASK_SWITCH_JMP); |  | 
|  |  | 
| /* 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 242  JMPfar_pm(WORD selector, DWORD new_ip) | Line 89  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)", 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 CPUCALL | 
| CALLfar_pm(WORD selector, DWORD new_ip) | JMPfar_pm_code_segment(const selector_t *cs_sel, UINT32 new_ip) | 
 | { | { | 
 | selector_t call_sel; |  | 
 | selector_t sel2; |  | 
 | int rv; |  | 
 |  |  | 
| VERBOSE(("CALLfar_pm: selector = 0x%04x, new_ip = 0x%08x", selector, new_ip)); | VERBOSE(("JMPfar_pm: CODE-SEGMENT")); | 
 |  |  | 
| rv = parse_selector(&call_sel, selector); | /* check privilege level */ | 
| if (rv < 0) { | if (!SEG_IS_CONFORMING_CODE(&cs_sel->desc)) { | 
| 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: code 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) | CPU_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 CPUCALL | 
| } | JMPfar_pm_call_gate(const 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: call gate 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); | 
| new_ip &= 0xffff; | 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); | 
|  | } | 
|  |  | 
|  | /* 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); | 
|  | } | 
|  |  | 
|  | /* 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); | 
 | } | } | 
 | load_cs(call_sel.selector, &call_sel.desc, CPU_STAT_CPL); |  | 
 | SET_EIP(new_ip); |  | 
 | } 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)); | 
|  | EXCEPTION(GP_EXCEPTION, cs_sel.idx); | 
|  | } | 
|  | } | 
 |  |  | 
| /* system descriptor */ | /* not present */ | 
| switch (call_sel.desc.type) { | if (selector_is_not_present(&cs_sel)) { | 
| case CPU_SYSDESC_TYPE_CALL_16: | VERBOSE(("JMPfar_pm: code selector is not present")); | 
| case CPU_SYSDESC_TYPE_CALL_32: | EXCEPTION(NP_EXCEPTION, cs_sel.idx); | 
| VERBOSE(("CALL-GATE")); | } | 
 |  |  | 
| /* check privilege level */ | /* out of range */ | 
| if ((call_sel.desc.dpl < CPU_STAT_CPL) | if (callgate_sel->desc.u.gate.offset > cs_sel.desc.u.seg.limit) { | 
| || (call_sel.desc.dpl < call_sel.rpl)) { | 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, call_sel.idx); | EXCEPTION(GP_EXCEPTION, 0); | 
| } | } | 
 |  |  | 
| /* not present */ | load_cs(cs_sel.selector, &cs_sel.desc, CPU_STAT_CPL); | 
| if (selector_is_not_present(&call_sel)) { | CPU_EIP = callgate_sel->desc.u.gate.offset; | 
| EXCEPTION(NP_EXCEPTION, call_sel.idx); | } | 
| } |  | 
 |  |  | 
| /* parse code segment descriptor */ | /*--- | 
| rv = parse_selector(&sel2, call_sel.desc.u.gate.selector); | * JMPfar: task gate | 
| if (rv < 0) { | */ | 
| EXCEPTION(GP_EXCEPTION, sel2.idx); | static void CPUCALL | 
| } | JMPfar_pm_task_gate(selector_t *taskgate_sel) | 
|  | { | 
|  | selector_t tss_sel; | 
|  | int rv; | 
 |  |  | 
| /* check code segment & privilege level */ | VERBOSE(("JMPfar_pm: TASK-GATE")); | 
| if (!sel2.desc.s |  | 
| || !sel2.desc.u.seg.c |  | 
| || sel2.desc.dpl > CPU_STAT_CPL) { |  | 
| EXCEPTION(GP_EXCEPTION, sel2.idx); |  | 
| } |  | 
 |  |  | 
| /* not present */ | /* check privilege level */ | 
| if (selector_is_not_present(&sel2)) { | if (taskgate_sel->desc.dpl < CPU_STAT_CPL) { | 
| EXCEPTION(NP_EXCEPTION, sel2.idx); | 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); | 
|  | } | 
 |  |  | 
| /* save register */ | /* not present */ | 
| temp_cs = CPU_CS; | if (selector_is_not_present(taskgate_sel)) { | 
| temp_ss = CPU_SS; | VERBOSE(("JMPfar_pm: selector is not present")); | 
| temp_eip = CPU_EIP; | EXCEPTION(NP_EXCEPTION, taskgate_sel->idx); | 
| temp_esp = CPU_ESP; | } | 
| if (sel2.desc.type == CPU_SYSDESC_TYPE_CALL_16) { |  | 
| temp_eip &= 0xffff; |  | 
| temp_esp &= 0xffff; |  | 
| } |  | 
 |  |  | 
| new_ip = call_sel.desc.u.gate.offset; | /* 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); | 
|  | } | 
 |  |  | 
| /* out of range */ | /* check descriptor type */ | 
| if (new_ip > sel2.desc.u.seg.limit) { | switch (tss_sel.desc.type) { | 
| EXCEPTION(GP_EXCEPTION, 0); | 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; | 
|  | } | 
 |  |  | 
| if (!sel2.desc.u.seg.ec | /* not present */ | 
| && (sel2.desc.dpl < CPU_STAT_CPL)) { | if (selector_is_not_present(&tss_sel)) { | 
| DWORD param[32];        // copy param | VERBOSE(("JMPfar_pm: selector is not present")); | 
| selector_t ss_sel; | EXCEPTION(NP_EXCEPTION, tss_sel.idx); | 
| 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; |  | 
 |  |  | 
| case CPU_SYSDESC_TYPE_TASK: | task_switch(&tss_sel, TASK_SWITCH_JMP); | 
| VERBOSE(("TASK-GATE")); |  | 
 |  |  | 
| /* check privilege level */ | /* out of range */ | 
| if ((call_sel.desc.dpl < CPU_STAT_CPL) | if (CPU_EIP > CPU_STAT_CS_LIMIT) { | 
| || (call_sel.desc.dpl < call_sel.rpl)) { | VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", CPU_EIP, CPU_STAT_CS_LIMIT)); | 
| EXCEPTION(GP_EXCEPTION, call_sel.idx); | EXCEPTION(GP_EXCEPTION, 0); | 
| } | } | 
|  | } | 
 |  |  | 
| /* not present */ | /*--- | 
| if (selector_is_not_present(&call_sel)) { | * JMPfar: TSS | 
| EXCEPTION(NP_EXCEPTION, call_sel.idx); | */ | 
| } | static void CPUCALL | 
|  | JMPfar_pm_tss(selector_t *tss_sel) | 
|  | { | 
 |  |  | 
| /* tss descriptor */ | VERBOSE(("JMPfar_pm: TASK-STATE-SEGMENT")); | 
| rv = parse_selector(&sel2, call_sel.desc.u.gate.selector); |  | 
| if (rv < 0 || sel2.ldt) { |  | 
| EXCEPTION(GP_EXCEPTION, sel2.idx); |  | 
| } |  | 
 |  |  | 
| /* check descriptor type */ | /* check privilege level */ | 
| switch (sel2.desc.type) { | if (tss_sel->desc.dpl < CPU_STAT_CPL) { | 
| case CPU_SYSDESC_TYPE_TSS_16: | VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d)", tss_sel->desc.dpl, CPU_STAT_CPL)); | 
| case CPU_SYSDESC_TYPE_TSS_32: | EXCEPTION(GP_EXCEPTION, tss_sel->idx); | 
| break; | } | 
|  | if (tss_sel->desc.dpl < tss_sel->rpl) { | 
| case CPU_SYSDESC_TYPE_TSS_BUSY_16: | VERBOSE(("JMPfar_pm: DPL(%d) < RPL(%d)", tss_sel->desc.dpl, tss_sel->rpl)); | 
| case CPU_SYSDESC_TYPE_TSS_BUSY_32: | EXCEPTION(GP_EXCEPTION, tss_sel->idx); | 
| VERBOSE(("CALLfar_pm: task is busy")); | } | 
| /*FALLTHROUGH*/ |  | 
| default: |  | 
| EXCEPTION(GP_EXCEPTION, sel2.idx); |  | 
| break; |  | 
| } |  | 
 |  |  | 
| /* not present */ | /* not present */ | 
| if (selector_is_not_present(&sel2)) { | if (selector_is_not_present(tss_sel)) { | 
| EXCEPTION(NP_EXCEPTION, sel2.idx); | VERBOSE(("JMPfar_pm: selector is not present")); | 
| } | EXCEPTION(NP_EXCEPTION, tss_sel->idx); | 
|  | } | 
 |  |  | 
| task_switch(&sel2, TASK_SWITCH_CALL); | task_switch(tss_sel, TASK_SWITCH_JMP); | 
 |  |  | 
| /* out of range */ | /* out of range */ | 
| if (CPU_EIP > CPU_STAT_CS_LIMIT) { | if (CPU_EIP > CPU_STAT_CS_LIMIT) { | 
| EXCEPTION(GP_EXCEPTION, 0); | 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; | } | 
|  | } | 
 |  |  | 
 | case CPU_SYSDESC_TYPE_TSS_16: |  | 
 | case CPU_SYSDESC_TYPE_TSS_32: |  | 
 | VERBOSE(("TASK-STATE-SEGMENT")); |  | 
 |  |  | 
| /* check privilege level */ | /*------------------------------------------------------------------------------ | 
| if ((call_sel.desc.dpl < CPU_STAT_CPL) | * CALLfar_pm | 
| || (call_sel.desc.dpl < call_sel.rpl)) { | */ | 
| EXCEPTION(GP_EXCEPTION, call_sel.idx); | static void CPUCALL CALLfar_pm_code_segment(const selector_t *cs_sel, UINT32 new_ip); | 
| } | static void CPUCALL CALLfar_pm_call_gate(const selector_t *callgate_sel); | 
|  | static void CPUCALL CALLfar_pm_task_gate(selector_t *taskgate_sel); | 
|  | static void CPUCALL CALLfar_pm_tss(selector_t *tss_sel); | 
 |  |  | 
| /* not present */ | void CPUCALL | 
| if (selector_is_not_present(&call_sel)) { | CALLfar_pm(UINT16 selector, UINT32 new_ip) | 
| EXCEPTION(NP_EXCEPTION, call_sel.idx); | { | 
| } | selector_t call_sel; | 
|  | int rv; | 
 |  |  | 
| task_switch(&call_sel, 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 */ | rv = parse_selector(&call_sel, selector); | 
| if (CPU_EIP > CPU_STAT_CS_LIMIT) { | if (rv < 0) { | 
| EXCEPTION(GP_EXCEPTION, 0); | VERBOSE(("CALLfar_pm: parse_selector (selector = %04x, rv = %d)", selector, rv)); | 
| } | EXCEPTION(GP_EXCEPTION, call_sel.idx); | 
|  | } | 
|  |  | 
|  | if (!SEG_IS_SYSTEM(&call_sel.desc)) { | 
|  | /* code or data segment descriptor */ | 
|  | VERBOSE(("CALLfar_pm: code or data segment descriptor")); | 
|  |  | 
|  | if (SEG_IS_DATA(&call_sel.desc)) { | 
|  | /* data segment */ | 
|  | VERBOSE(("CALLfar_pm: data segment")); | 
|  | EXCEPTION(GP_EXCEPTION, call_sel.idx); | 
|  | } | 
|  |  | 
|  | /* code segment descriptor */ | 
|  | CALLfar_pm_code_segment(&call_sel, new_ip); | 
|  | } else { | 
|  | /* system descriptor */ | 
|  | VERBOSE(("CALLfar_pm: system descriptor")); | 
|  |  | 
|  | switch (call_sel.desc.type) { | 
|  | case CPU_SYSDESC_TYPE_CALL_16: | 
|  | case CPU_SYSDESC_TYPE_CALL_32: | 
|  | CALLfar_pm_call_gate(&call_sel); | 
|  | break; | 
|  |  | 
|  | 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; | break; | 
 |  |  | 
 | case CPU_SYSDESC_TYPE_TSS_BUSY_16: | case CPU_SYSDESC_TYPE_TSS_BUSY_16: | 
| Line 569  CALLfar_pm(WORD selector, DWORD new_ip) | Line 382  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 CPUCALL | 
| RETfar_pm(DWORD nbytes) | CALLfar_pm_code_segment(const selector_t *cs_sel, UINT32 new_ip) | 
 | { | { | 
| selector_t ret_sel; | UINT32 sp; | 
| int rv; |  | 
| DWORD new_ip; |  | 
| WORD selector; |  | 
 |  |  | 
| VERBOSE(("RETfar_pm: nbytes = %d", 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 (!SEG_IS_CONFORMING_CODE(&cs_sel->desc)) { | 
| 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) { | 
|  | 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 { | 
|  | 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 CPUCALL CALLfar_pm_call_gate_same_privilege(const selector_t *call_sel, selector_t *cs_sel); | 
 |  | static void CPUCALL CALLfar_pm_call_gate_more_privilege(const selector_t *call_sel, selector_t *cs_sel); | 
 |  |  | 
 |  | static void CPUCALL | 
 |  | CALLfar_pm_call_gate(const 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 (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); | 
|  | } | 
 |  |  | 
| 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 (!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); | 
|  | } | 
|  | } | 
|  |  | 
|  | /*--- | 
|  | * CALLfar_pm: call gate (SAME-PRIVILEGE) | 
|  | */ | 
|  | static void CPUCALL | 
|  | CALLfar_pm_call_gate_same_privilege(const selector_t *callgate_sel, selector_t *cs_sel) | 
|  | { | 
|  | UINT32 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) { | 
|  | 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 CPUCALL | 
|  | 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 CPUCALL | 
|  | 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 CPUCALL | 
|  | 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 CPUCALL | 
|  | 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 */ | /* 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)); | 
| SET_EIP(new_ip); |  | 
| load_ss(ss_sel.selector, &ss_sel.desc, ret_sel.rpl); | load_ss(ss_sel.selector, &ss_sel.desc, cs_sel.rpl); | 
| CPU_ESP = new_sp; | 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 */ | /* 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) | if (i == CPU_SS_INDEX || i == CPU_CS_INDEX) | 
 | continue; | 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)); | 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; | segdesc_clear(sdp); | 
 | continue; | continue; | 
 | } | } | 
 |  |  | 
| /* check privilege level */ | /* | 
| if (!temp_sel.desc.s | * - system segment | 
| || (temp_sel.desc.u.seg.c && temp_sel.desc.u.seg.wr) | * - execute-only code segment | 
| || ((!temp_sel.desc.u.seg.c || !temp_sel.desc.u.seg.ec) | * - data or conforming code segment && CPL > DPL | 
| && ((temp_sel.desc.dpl < CPU_STAT_CPL) || (temp_sel.desc.dpl < temp_sel.rpl)))) { | */ | 
|  | 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_REGS_SREG(i) = 0; | 
| CPU_STAT_SREG(i).valid = 0; | segdesc_clear(sdp); | 
| 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 CPUCALL IRET_pm_protected_mode_return(UINT16 new_cs, UINT32 new_ip, UINT32 new_flags); | 
 |  | static void CPUCALL IRET_pm_protected_mode_return_same_privilege(const selector_t *cs_sel, UINT32 new_ip, UINT32 new_flags); | 
 |  | static void CPUCALL IRET_pm_protected_mode_return_outer_privilege(const selector_t *cs_sel, UINT32 new_ip, UINT32 new_flags); | 
 |  | static void CPUCALL IRET_pm_return_to_vm86(UINT16 new_cs, UINT32 new_ip, UINT32 new_flags); | 
 |  | static void CPUCALL 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 (CPU_INST_OP32) { | 
| if (new_ip > ret_sel.desc.u.seg.limit) { | SS_POP_CHECK(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; | SS_POP_CHECK(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) && 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; | int rv; | 
 | DWORD new_ip, new_flags; |  | 
 | WORD new_cs; |  | 
 |  |  | 
| VERBOSE(("IRET_pm")); | 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 (!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); | 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); | * IRET_pm: PROTECTED-MODE-RETURN | 
| if (rv < 0 || iret_sel.ldt) { | */ | 
| EXCEPTION(GP_EXCEPTION, iret_sel.idx); | static void CPUCALL | 
| } | IRET_pm_protected_mode_return(UINT16 new_cs, UINT32 new_ip, UINT32 new_flags) | 
|  | { | 
|  | selector_t cs_sel; | 
|  | int rv; | 
 |  |  | 
| /* check system segment */ | /* PROTECTED-MODE-RETURN */ | 
| if (iret_sel.desc.s) { | VERBOSE(("IRET_pm: PE=1, VM=0 in flags image")); | 
| 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; |  | 
 |  |  | 
| case CPU_SYSDESC_TYPE_TSS_16: | rv = parse_selector(&cs_sel, new_cs); | 
| case CPU_SYSDESC_TYPE_TSS_32: | if (rv < 0) { | 
| VERBOSE(("IRET_pm: task is not busy")); | VERBOSE(("IRET_pm: parse_selector (selector = %04x, rv = %d)", cs_sel.selector, rv)); | 
| /*FALLTHROUGH*/ | EXCEPTION(GP_EXCEPTION, cs_sel.idx); | 
| default: | } | 
| EXCEPTION(GP_EXCEPTION, iret_sel.idx); |  | 
| break; |  | 
| } |  | 
 |  |  | 
| /* not present */ | /* check code segment descriptor */ | 
| if (selector_is_not_present(&iret_sel)) { | if (SEG_IS_SYSTEM(&cs_sel.desc)) { | 
| EXCEPTION(NP_EXCEPTION, iret_sel.idx); | 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 */ | /* not present */ | 
| if (CPU_EIP > CPU_STAT_CS_LIMIT) { | if (selector_is_not_present(&cs_sel)) { | 
| EXCEPTION(GP_EXCEPTION, 0); | 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 CPUCALL | 
|  | 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 { | } else { | 
 |  | CPU_SP += (UINT16)stacksize; | 
 |  | } | 
 |  |  | 
 |  | set_eflags(new_flags, mask); | 
 |  | } | 
 |  |  | 
 |  | /*--- | 
 |  | * IRET_pm: OUTER-PRIVILEGE | 
 |  | */ | 
 |  | static void CPUCALL | 
 |  | 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) { | 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; | CPU_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); | load_ss(ss_sel.selector, &ss_sel.desc, cs_sel->rpl); | 
| } else { | if (CPU_STAT_SS32) { | 
| DWORD mask; | CPU_ESP = new_sp; | 
|  | } else { | 
|  | CPU_SP = (UINT16)new_sp; | 
|  | } | 
 |  |  | 
| /* PROTECTED-MODE-RETURN */ | set_eflags(new_flags, mask); | 
| VERBOSE(("PE=1, VM=0 in flags image")); |  | 
 |  |  | 
| 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)) { | 
|  | 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) { | * IRET_pm: new_flags & VM_FLAG | 
| EXCEPTION(GP_EXCEPTION, iret_sel.idx); | */ | 
| } | static void CPUCALL | 
|  | 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 */ | 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_INST_OP32) { | 
| if (selector_is_not_present(&iret_sel)) { | ia32_panic("IRET_pm: 16bit mode"); | 
| EXCEPTION(NP_EXCEPTION, iret_sel.idx); | } | 
| } |  | 
 |  |  | 
| if (iret_sel.rpl > CPU_STAT_CPL) { | if (CPU_STAT_SS32) { | 
| /* RETURN-OUTER-PRIVILEGE-LEVEL */ | sp = CPU_ESP; | 
| selector_t ss_sel; | } else { | 
| WORD new_sp, new_ss; | sp = CPU_SP; | 
|  | } | 
| if (CPU_INST_OP32) { | SS_POP_CHECK(sp, 36); | 
| 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")); |  | 
| } |  | 
 |  |  | 
| /* check code segment limit */ | if (new_ip > 0xffff) { | 
| if (new_ip > iret_sel.desc.u.seg.limit) { | EXCEPTION(GP_EXCEPTION, 0); | 
| EXCEPTION(GP_EXCEPTION, 0); | } | 
| } |  | 
 |  |  | 
| /* set new register */ | new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 12); | 
| load_cs(iret_sel.selector, &iret_sel.desc, iret_sel.rpl); | segsel[CPU_SS_INDEX] = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 16); | 
| SET_EIP(new_ip); | segsel[CPU_ES_INDEX] = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 20); | 
|  | segsel[CPU_DS_INDEX] = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 24); | 
| mask = 0; | segsel[CPU_FS_INDEX] = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 28); | 
| if (CPU_INST_OP32) | segsel[CPU_GS_INDEX] = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 32); | 
| mask |= RF_FLAG; | segsel[CPU_CS_INDEX] = new_cs; | 
| 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) { | for (i = 0; i < CPU_SEGREG_NUM; i++) { | 
| selector_t temp_sel; | segdesc_init(i, segsel[i], &CPU_STAT_SREG(i)); | 
| int i; | } | 
|  |  | 
| /* RETURN-OUTER-PRIVILEGE-LEVEL */ | CPU_ESP = new_sp; | 
| /* check segment register */ | CPU_EIP = new_ip; | 
| for (i = 0; i < CPU_SEGREG_NUM; i++) { |  | 
| if ((i == CPU_CS_INDEX) || (i == CPU_SS_INDEX)) | /* to VM86 mode */ | 
| continue; | set_eflags(new_flags, IOPL_FLAG|I_FLAG|VM_FLAG|RF_FLAG); | 
|  | } | 
| rv = parse_selector(&temp_sel, CPU_REGS_SREG(i)); |  | 
| if (rv < 0) { | /*--- | 
| CPU_REGS_SREG(i) = 0; | * IRET_pm: VM_FLAG | 
| CPU_STAT_SREG(i).valid = 0; | */ | 
| continue; | static void CPUCALL | 
| } | IRET_pm_return_from_vm86(UINT16 new_cs, UINT32 new_ip, UINT32 new_flags) | 
|  | { | 
| if ((!temp_sel.desc.u.seg.c || !temp_sel.desc.u.seg.ec) | UINT stacksize; | 
| && (CPU_STAT_CPL > temp_sel.desc.dpl)) { |  | 
| CPU_REGS_SREG(i) = 0; | VERBOSE(("IRET_pm: virtual-8086 mode: VM=1")); | 
| CPU_STAT_SREG(i).valid = 0; |  | 
| continue; | 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); | 
 | } | } |