Exemple #1
0
void CPUCALL
JMP16_Ep(UINT32 op)
{
	descriptor_t sd;
	UINT32 madr;
	UINT16 new_ip;
	UINT16 new_cs;
	UINT16 sreg;

	CPU_WORKCLOCK(11);
	if (op < 0xc0) {
		madr = calc_ea_dst(op);
		new_ip = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr);
		new_cs = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr + 2);
		if (!CPU_STAT_PM || CPU_STAT_VM86) {
			/* Real mode or VM86 mode */
			/* check new instrunction pointer with new code segment */
			load_segreg(CPU_CS_INDEX, new_cs, &sreg, &sd, GP_EXCEPTION);
			if (new_ip > sd.u.seg.limit) {
				EXCEPTION(GP_EXCEPTION, 0);
			}
			LOAD_SEGREG(CPU_CS_INDEX, new_cs);
			CPU_EIP = new_ip;
		} else {
			/* Protected mode */
			JMPfar_pm(new_cs, new_ip);
		}
		return;
	}
	EXCEPTION(UD_EXCEPTION, 0);
}
Exemple #2
0
void
BTC_EwIb(UINT32 op)
{
	UINT16 *out;
	UINT32 src, dst, res, madr;
	UINT16 bit;

	if (op >= 0xc0) {
		CPU_WORKCLOCK(2);
		GET_PCBYTE(src);
		out = reg16_b20[op];
		dst = *out;
		bit = BIT_MAKEBIT16(src);
		if (dst & bit) {
			CPU_FLAGL |= C_FLAG;
		} else {
			CPU_FLAGL &= ~C_FLAG;
		}
		res = dst ^ bit;
		*out = (UINT16)res;
	} else {
		CPU_WORKCLOCK(6);
		madr = calc_ea_dst(op);
		GET_PCBYTE(src);
		dst = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr);
		bit = BIT_MAKEBIT16(src);
		if (dst & bit) {
			CPU_FLAGL |= C_FLAG;
		} else {
			CPU_FLAGL &= ~C_FLAG;
		}
		res = dst ^ bit;
		cpu_vmemorywrite_w(CPU_INST_SEGREG_INDEX, madr, (UINT16)res);
	}
}
Exemple #3
0
/*
 * BTC
 */
void
BTC_EwGw(void)
{
	UINT16 *out;
	UINT32 op, src, dst, res, madr;
	UINT16 bit;

	PREPART_EA_REG16(op, src);
	bit = BIT_MAKEBIT16(src);
	if (op >= 0xc0) {
		CPU_WORKCLOCK(2);
		out = reg16_b20[op];
		dst = *out;
		if (dst & bit) {
			CPU_FLAGL |= C_FLAG;
		} else {
			CPU_FLAGL &= ~C_FLAG;
		}
		res = dst ^ bit;
		*out = (UINT16)res;
	} else {
		CPU_WORKCLOCK(7);
		madr = calc_ea_dst(op);
		madr += BIT_OFFSET16(src);
		dst = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr);
		if (dst & bit) {
			CPU_FLAGL |= C_FLAG;
		} else {
			CPU_FLAGL &= ~C_FLAG;
		}
		res = dst ^ bit;
		cpu_vmemorywrite_w(CPU_INST_SEGREG_INDEX, madr, (UINT16)res);
	}
}
Exemple #4
0
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) {
            STACK_POP_CHECK(CPU_REGS_SREG(CPU_SS_INDEX), &CPU_STAT_SREG(CPU_SS_INDEX), sp, 12);
            new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, sp);
            new_cs = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4);
            new_flags = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 8);
        } else {
            STACK_POP_CHECK(CPU_REGS_SREG(CPU_SS_INDEX), &CPU_STAT_SREG(CPU_SS_INDEX), sp, 6);
            new_ip = cpu_vmemoryread_w(CPU_SS_INDEX, sp);
            new_cs = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 2);
            new_flags = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4);
        }

        VERBOSE(("IRET_pm: new_ip = %08x, new_cs = %04x, new_eflags = %08x", new_ip, new_cs, new_flags));

        if (CPU_EFLAG & VM_FLAG) {
            /* RETURN-FROM-VIRTUAL-8086-MODE */
            IRET_pm_return_from_vm86(new_cs, new_ip, new_flags);
        } else if (new_flags & VM_FLAG) {
            /* 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);
        }
    }

    VERBOSE(("IRET_pm: new EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP));
}
Exemple #5
0
/*---
 * IRET_pm: new_flags & VM_FLAG
 */
static void
IRET_pm_return_to_vm86(UINT16 new_cs, UINT32 new_ip, UINT32 new_flags)
{
    UINT16 segsel[CPU_SEGREG_NUM];
    UINT32 sp;
    UINT32 new_sp;
    int i;

    VERBOSE(("IRET_pm: Interrupt procedure was in virtual-8086 mode: PE=1, VM=1 in flags image"));

    if (CPU_STAT_CPL != 0) {
        ia32_panic("IRET_pm: CPL != 0");
    }

    if (!CPU_INST_OP32) {
        ia32_panic("IRET_pm: 16bit mode");
    }

    if (CPU_STAT_SS32) {
        sp = CPU_ESP;
    } else {
        sp = CPU_SP;
    }
    STACK_POP_CHECK(CPU_REGS_SREG(CPU_SS_INDEX), &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] = (UINT16)new_cs;

    for (i = 0; i < CPU_SEGREG_NUM; i++) {
        CPU_REGS_SREG(i) = segsel[i];
        CPU_STAT_SREG_INIT(i);
    }

    /* to VM86 mode */
    set_eflags(new_flags, IOPL_FLAG|I_FLAG|VM_FLAG|RF_FLAG);

    new_sp &= 0xffff;
    new_ip &= 0xffff;

    CPU_ESP = new_sp;
    SET_EIP(new_ip);
}
Exemple #6
0
void
BOUND_GwMa(void)
{
	UINT32 op, madr;
	UINT16 reg;

	CPU_WORKCLOCK(13);
	GET_PCBYTE(op);
	if (op < 0xc0) {
		reg = *(reg16_b53[op]);
		madr = calc_ea_dst(op);
		if (reg >= cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr) &&
		    reg <= cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr + 2)) {
				return;
		}
		EXCEPTION(BR_EXCEPTION, 0);
		return;
	}
	EXCEPTION(UD_EXCEPTION, 0);
}
Exemple #7
0
void CPUCALL
CALL16_Ep(UINT32 op)
{
	descriptor_t sd;
	UINT32 madr;
	UINT16 new_ip;
	UINT16 new_cs;
	UINT16 sreg;

	CPU_WORKCLOCK(16);
	if (op < 0xc0) {
		madr = calc_ea_dst(op);
		new_ip = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr);
		new_cs = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr + 2);
		if (!CPU_STAT_PM || CPU_STAT_VM86) {
			/* Real mode or VM86 mode */
			CPU_SET_PREV_ESP();
			load_segreg(CPU_CS_INDEX, new_cs, &sreg, &sd, GP_EXCEPTION);
			if (new_ip > sd.u.seg.limit) {
				EXCEPTION(GP_EXCEPTION, 0);
			}

			PUSH0_16(CPU_CS);
			PUSH0_16(CPU_IP);

			LOAD_SEGREG(CPU_CS_INDEX, new_cs);
			CPU_EIP = new_ip;
			CPU_CLEAR_PREV_ESP();
		} else {
			/* Protected mode */
			CALLfar_pm(new_cs, new_ip);
		}
		return;
	} 
	EXCEPTION(UD_EXCEPTION, 0);
}
Exemple #8
0
void
TEST_EwGw(void)
{
	UINT32 op, src, tmp, madr;

	PREPART_EA_REG16(op, src);
	if (op >= 0xc0) {
		CPU_WORKCLOCK(2);
		tmp = *(reg16_b20[op]);
	} else {
		CPU_WORKCLOCK(7);
		madr = calc_ea_dst(op);
		tmp = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr);
	}
	WORD_AND(tmp, src);
}
Exemple #9
0
void
TEST_EwIw(UINT32 op)
{
	UINT32 src, tmp, madr;

	if (op >= 0xc0) {
		CPU_WORKCLOCK(2);
		tmp = *(reg16_b20[op]);
	} else {
		CPU_WORKCLOCK(6);
		madr = calc_ea_dst(op);
		tmp = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr);
	}
	GET_PCWORD(src);
	WORD_AND(tmp, src);
}
Exemple #10
0
void
BT_EwIb(UINT32 op)
{
	UINT32 src, dst, madr;

	if (op >= 0xc0) {
		CPU_WORKCLOCK(2);
		GET_PCBYTE(src);
		dst = *(reg16_b20[op]);
	} else {
		CPU_WORKCLOCK(6);
		madr = calc_ea_dst(op);
		GET_PCBYTE(src);
		dst = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr);
	}
	CPU_FLAGL &= ~C_FLAG;
	CPU_FLAGL |= (dst >> BIT_INDEX16(src)) & 1;
}
Exemple #11
0
/*
 * BT
 */
void
BT_EwGw(void)
{
	UINT32 op, src, dst, madr;

	PREPART_EA_REG16(op, src);
	if (op >= 0xc0) {
		CPU_WORKCLOCK(2);
		dst = *(reg16_b20[op]);
	} else {
		CPU_WORKCLOCK(7);
		madr = calc_ea_dst(op);
		madr += BIT_OFFSET16(src);
		dst = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr);
	}
	CPU_FLAGL &= ~C_FLAG;
	CPU_FLAGL |= (dst >> BIT_INDEX16(src)) & 1;
}
Exemple #12
0
void CPUCALL
JMP_Ew(UINT32 op)
{
	UINT32 madr;
	UINT16 new_ip;

	if (op >= 0xc0) {
		CPU_WORKCLOCK(7);
		new_ip = *(reg16_b20[op]);
	} else {
		CPU_WORKCLOCK(11);
		madr = calc_ea_dst(op);
		new_ip = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, madr);
	}
	if (new_ip > CPU_STAT_CS_LIMIT) {
		EXCEPTION(GP_EXCEPTION, 0);
	}
	CPU_EIP = new_ip;
}
Exemple #13
0
/*
 * STACK
 */
void
ENTER16_IwIb(void)
{
	UINT32 sp, bp;
	UINT32 val;
	UINT16 dimsize;
	UINT16 new_bp;
	UINT8 level;

	GET_PCWORD(dimsize);
	GET_PCBYTE(level);
	level &= 0x1f;

	CPU_SET_PREV_ESP();
	PUSH0_16(CPU_BP);
	if (level == 0) {			/* enter level=0 */
		CPU_WORKCLOCK(11);
		CPU_BP = CPU_SP;
		if (!CPU_STAT_SS32) {
			CPU_SP -= dimsize;
		} else {
			CPU_ESP -= dimsize;
		}
	} else {
		--level;
		if (level == 0) {		/* enter level=1 */
			CPU_WORKCLOCK(15);
			sp = CPU_SP;
			PUSH0_16(sp);
			CPU_BP = (UINT16)sp;
			if (!CPU_STAT_SS32) {
				CPU_SP -= dimsize;
			} else {
				CPU_ESP -= dimsize;
			}
		} else {			/* enter level=2-31 */
			CPU_WORKCLOCK(12 + level * 4);
			if (!CPU_STAT_SS32) {
				bp = CPU_BP;
				new_bp = CPU_SP;
				while (level--) {
					bp -= 2;
					CPU_SP -= 2;
					val = cpu_vmemoryread_w(CPU_SS_INDEX, bp);
					cpu_vmemorywrite_w(CPU_SS_INDEX, CPU_SP, (UINT16)val);
				}
				REGPUSH0(new_bp);
				CPU_BP = new_bp;
				CPU_SP -= dimsize;
			} else {
				bp = CPU_EBP;
				new_bp = CPU_SP;
				while (level--) {
					bp -= 2;
					CPU_ESP -= 2;
					val = cpu_vmemoryread_w(CPU_SS_INDEX, bp);
					cpu_vmemorywrite_w(CPU_SS_INDEX, CPU_ESP, (UINT16)val);
				}
				REGPUSH0_16_32(new_bp);
				CPU_BP = new_bp;
				CPU_ESP -= dimsize;
			}
		}
	}
	CPU_CLEAR_PREV_ESP();
}
Exemple #14
0
void
RETfar_pm(UINT nbytes)
{
    selector_t cs_sel, ss_sel, temp_sel;
    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) {
        STACK_POP_CHECK(CPU_REGS_SREG(CPU_SS_INDEX), &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 {
        STACK_POP_CHECK(CPU_REGS_SREG(CPU_SS_INDEX), &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 += (UINT16)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) {
            STACK_POP_CHECK(CPU_REGS_SREG(CPU_SS_INDEX), &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 {
            STACK_POP_CHECK(CPU_REGS_SREG(CPU_SS_INDEX), &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 */
        if (selector_is_not_present(&ss_sel)) {
            VERBOSE(("RETfar_pm: stack segment is not present"));
            EXCEPTION(SS_EXCEPTION, ss_sel.idx);
        }

        /* check code segment limit */
        if (new_ip > 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));
        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);

        load_ss(ss_sel.selector, &ss_sel.desc, cs_sel.rpl);
        if (CPU_STAT_SS32) {
            CPU_ESP = new_sp + nbytes;
        } else {
            CPU_SP = (UINT16)(new_sp + nbytes);
        }

        /* check segment register */
        for (i = 0; i < CPU_SEGREG_NUM; i++) {
            descriptor_t *dp;
            BOOL valid;

            dp = &CPU_STAT_SREG(i);
            if ((!dp->u.seg.c || !dp->u.seg.ec)
                    && (CPU_STAT_SREG(i).dpl < CPU_STAT_CPL)) {
                /* segment register is invalid */
                CPU_REGS_SREG(i) = 0;
                CPU_STAT_SREG_CLEAR(i);
                continue;
            }

            rv = parse_selector(&temp_sel, CPU_REGS_SREG(i));
            if (rv < 0) {
                /* segment register is invalid */
                CPU_REGS_SREG(i) = 0;
                CPU_STAT_SREG_CLEAR(i);
                continue;
            }

            valid = TRUE;
            if (!temp_sel.desc.s) {
                /* system segment */
                valid = FALSE;
            }
            if (temp_sel.desc.u.seg.c && !temp_sel.desc.u.seg.wr) {
                /* execute-only code segment */
                valid = FALSE;
            }
            if (!temp_sel.desc.u.seg.c || !temp_sel.desc.u.seg.ec) {
                if (CPU_STAT_CPL > temp_sel.desc.dpl) {
                    valid = FALSE;
                }
            }

            if (!valid) {
                /* segment register is invalid */
                CPU_REGS_SREG(i) = 0;
                CPU_STAT_SREG(i).valid = 0;
            }
        }
    }

    VERBOSE(("RETfar_pm: new EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP));
}
Exemple #15
0
/*---
 * CALLfar_pm: call gate (MORE-PRIVILEGE)
 */
static void
CALLfar_pm_call_gate_more_privilege(const selector_t *callgate_sel, selector_t *cs_sel)
{
    UINT32 param[32];	/* copy param */
    selector_t ss_sel;
    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: 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) {
        STACK_PUSH_CHECK(ss_sel.idx, &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 = (UINT16)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 {
        STACK_PUSH_CHECK(ss_sel.idx, &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 = (UINT16)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);
    }
}
Exemple #16
0
/*---
 * IRET_pm: OUTER-PRIVILEGE
 */
static void
IRET_pm_protected_mode_return_outer_privilege(const selector_t *cs_sel, UINT32 new_ip, UINT32 new_flags)
{
    descriptor_t *dp;
    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) {
        STACK_POP_CHECK(CPU_REGS_SREG(CPU_SS_INDEX), &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 {
        STACK_POP_CHECK(CPU_REGS_SREG(CPU_SS_INDEX), &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) {
            mask |= VM_FLAG|VIF_FLAG|VIP_FLAG;
        }
    }

    /* set new register */
    load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->rpl);
    SET_EIP(new_ip);

    set_eflags(new_flags, mask);

    load_ss(ss_sel.selector, &ss_sel.desc, cs_sel->rpl);
    if (CPU_STAT_SS32) {
        CPU_ESP = new_sp;
    } else {
        CPU_SP = (UINT16)new_sp;
    }

    /* check segment register */
    for (i = 0; i < CPU_SEGREG_NUM; i++) {
        if ((i != CPU_CS_INDEX) && (i != CPU_SS_INDEX)) {
            dp = &CPU_STAT_SREG(i);
            if ((!dp->u.seg.c || !dp->u.seg.ec)
                    && (CPU_STAT_SREG(i).dpl < CPU_STAT_CPL)) {
                /* segment register is invalid */
                CPU_REGS_SREG(i) = 0;
                CPU_STAT_SREG_CLEAR(i);
            }
        }
    }
}