Exemplo n.º 1
0
static void MEMCALL
cpu_memorywrite_check(descriptor_t *sdp, UINT32 offset, UINT len, int e)
{

    __ASSERT(sdp != NULL);
    __ASSERT(len > 0);

    if (!SEG_IS_VALID(sdp)) {
        e = GP_EXCEPTION;
        goto exc;
    }
    if (!SEG_IS_PRESENT(sdp)
            || SEG_IS_SYSTEM(sdp)
            || SEG_IS_CODE(sdp)
            || (SEG_IS_DATA(sdp) && !SEG_IS_WRITABLE_DATA(sdp))) {
        goto exc;
    }

    switch (sdp->type) {
    case 2:
    case 3:	/* rw */
    case 6:
    case 7:	/* rw (expand down) */
        if (!check_limit_upstairs(sdp, offset, len, SEG_IS_32BIT(sdp)))
            goto exc;
        break;

    default:
        goto exc;
    }
    sdp->flag |= CPU_DESC_FLAG_WRITABLE | CPU_DESC_FLAG_READABLE;
    return;

exc:
    VERBOSE(("cpu_memorywrite_check: check failure: offset = 0x%08x, len = %d", offset, len));
#if defined(DEBUG)
    segdesc_dump(sdp);
#endif
    EXCEPTION(e, 0);
}
Exemplo n.º 2
0
static int MEMCALL
check_limit_upstairs(descriptor_t *sdp, UINT32 offset, UINT len, BOOL is32bit)
{
    UINT32 limit;
    UINT32 end;

    __ASSERT(sdp != NULL);
    __ASSERT(len > 0);

    len--;
    end = offset + len;

    if (SEG_IS_DATA(sdp) && SEG_IS_EXPANDDOWN_DATA(sdp)) {
        /* expand-down data segment */
        limit = SEG_IS_32BIT(sdp) ? 0xffffffff : 0x0000ffff;
        if (sdp->u.seg.limit == 0) {
            /*
             *   32bit       16bit
             * +-------+   +-------+ FFFFFFFFh
             * |       |   |       |
             * |       |   +  [1]  + 0000FFFFh
             * | valid |   |       |
             * |       |   +-------+ 0000FFFFh - len -1
             * |       |   | valid |
             * +-------+   +-------+ 00000000h
             */
            if (!SEG_IS_32BIT(sdp)) {
                if ((len > limit)		/* len check */
                        || (end > limit)) {		/* [1] */
                    goto exc;
                }
            } else {
                sdp->flag |= CPU_DESC_FLAG_WHOLEADR;
            }
        } else {
            /*
             *   32bit       16bit
             * +-------+   +-------+ FFFFFFFFh
             * |  [2]  |   |       |
             * +-------+   +.......+ FFFFFFFFh - len - 1
             * |       |   |  [2]  |
             * |       |   +.......+ 0000FFFFh
             * | valid |   |       |
             * |       |   +-------+ 0000FFFFh - len - 1
             * |       |   | valid |
             * +-------+   +-------+ seg.limit
             * |  [1]  |   |  [1]  |
             * +-------+   +-------+ 00000000h
             */
            if ((len > limit - sdp->u.seg.limit)	/* len check */
                    || (end < offset)			/* wrap check */
                    || (offset < sdp->u.seg.limit) 	/* [1] */
                    || (end > limit)) {			/* [2] */
                goto exc;
            }
        }
    } else {
        /* expand-up data or code segment */
        if (sdp->u.seg.limit == 0xffffffff) {
            /*
             *  16/32bit
             * +-------+ FFFFFFFFh
             * |       |
             * |       |
             * | valid |
             * |       |
             * |       |
             * +-------+ 00000000h
             */
            sdp->flag |= CPU_DESC_FLAG_WHOLEADR;
        } else {
            /*
             *  16/32bit
             * +-------+ FFFFFFFFh
             * |       |
             * |       |
             * |  [1]  |
             * +.......+ seg.limit
             * |       |
             * +-------+ seg.limit - len - 1
             * | valid |
             * +-------+ 00000000h
             */
            if ((len > sdp->u.seg.limit)		/* len check */
                    || (end < offset)			/* wrap check */
                    || (end > sdp->u.seg.limit + 1)) {	/* [1] */
                goto exc;
            }
        }
    }
    return 1;	/* Ok! */

exc:
    VERBOSE(("check_limit_upstairs: check failure: offset = 0x%08x, len = %d", offset, len + 1));
#if defined(DEBUG)
    segdesc_dump(sdp);
#endif
    return 0;
}
Exemplo n.º 3
0
static void CPUCALL
interrupt_intr_or_trap(const descriptor_t *gsdp, int intrtype, int errorp, int error_code)
{
    selector_t cs_sel, ss_sel;
    UINT stacksize;
    UINT32 old_flags;
    UINT32 new_flags;
    UINT32 mask;
    UINT32 sp;
    UINT32 new_ip, new_sp;
    UINT32 old_ip, old_sp;
    UINT16 old_cs, old_ss, new_ss;
    BOOL is32bit;
    int exc_errcode;
    int rv;

    new_ip = gsdp->u.gate.offset;
    old_ss = CPU_SS;
    old_cs = CPU_CS;
    old_ip = CPU_EIP;
    old_sp = CPU_ESP;
    old_flags = REAL_EFLAGREG;
    new_flags = REAL_EFLAGREG & ~(T_FLAG|RF_FLAG|NT_FLAG|VM_FLAG);
    mask = T_FLAG|RF_FLAG|NT_FLAG|VM_FLAG;

    switch (gsdp->type) {
    case CPU_SYSDESC_TYPE_INTR_16:
    case CPU_SYSDESC_TYPE_INTR_32:
        VERBOSE(("interrupt: INTERRUPT-GATE"));
        new_flags &= ~I_FLAG;
        mask |= I_FLAG;
        break;

    case CPU_SYSDESC_TYPE_TRAP_16:
    case CPU_SYSDESC_TYPE_TRAP_32:
        VERBOSE(("interrupt: TRAP-GATE"));
        break;

    default:
        ia32_panic("interrupt: gate descriptor type is invalid (type = %d)", gsdp->type);
        break;
    }

    exc_errcode = gsdp->u.gate.selector & ~3;
    if (intrtype == INTR_TYPE_EXTINTR)
        exc_errcode++;

    rv = parse_selector(&cs_sel, gsdp->u.gate.selector);
    if (rv < 0) {
        VERBOSE(("interrupt: parse_selector (selector = %04x, rv = %d)", gsdp->u.gate.selector, rv));
        EXCEPTION(GP_EXCEPTION, exc_errcode);
    }

    /* check segment type */
    if (SEG_IS_SYSTEM(&cs_sel.desc)) {
        VERBOSE(("interrupt: code segment is system segment"));
        EXCEPTION(GP_EXCEPTION, exc_errcode);
    }
    if (SEG_IS_DATA(&cs_sel.desc)) {
        VERBOSE(("interrupt: code segment is data segment"));
        EXCEPTION(GP_EXCEPTION, exc_errcode);
    }

    /* check privilege level */
    if (cs_sel.desc.dpl > CPU_STAT_CPL) {
        VERBOSE(("interrupt: DPL(%d) > CPL(%d)", cs_sel.desc.dpl, CPU_STAT_CPL));
        EXCEPTION(GP_EXCEPTION, exc_errcode);
    }

    /* not present */
    if (selector_is_not_present(&cs_sel)) {
        VERBOSE(("interrupt: selector is not present"));
        EXCEPTION(NP_EXCEPTION, exc_errcode);
    }

    is32bit = gsdp->type & CPU_SYSDESC_TYPE_32BIT;
    if (!SEG_IS_CONFORMING_CODE(&cs_sel.desc) && (cs_sel.desc.dpl < CPU_STAT_CPL)) {
        stacksize = errorp ? 12 : 10;
        if (!CPU_STAT_VM86) {
            VERBOSE(("interrupt: INTER-PRIVILEGE-LEVEL-INTERRUPT"));
        } else {
            /* VM86 */
            VERBOSE(("interrupt: INTERRUPT-FROM-VIRTUAL-8086-MODE"));
            if (cs_sel.desc.dpl != 0) {
                /* 16.3.1.1 */
                VERBOSE(("interrupt: DPL[CS](%d) != 0", cs_sel.desc.dpl));
                EXCEPTION(GP_EXCEPTION, exc_errcode);
            }
            stacksize += 8;
        }
        if (is32bit) {
            stacksize *= 2;
        }

        /* get stack pointer from TSS */
        get_stack_pointer_from_tss(cs_sel.desc.dpl, &new_ss, &new_sp);

        /* parse stack segment descriptor */
        rv = parse_selector(&ss_sel, new_ss);

        /* update exception error code */
        exc_errcode = ss_sel.idx;
        if (intrtype == INTR_TYPE_EXTINTR)
            exc_errcode++;

        if (rv < 0) {
            VERBOSE(("interrupt: parse_selector (selector = %04x, rv = %d)", new_ss, rv));
            EXCEPTION(TS_EXCEPTION, exc_errcode);
        }

        /* check privilege level */
        if (ss_sel.rpl != cs_sel.desc.dpl) {
            VERBOSE(("interrupt: selector RPL[SS](%d) != DPL[CS](%d)", ss_sel.rpl, cs_sel.desc.dpl));
            EXCEPTION(TS_EXCEPTION, exc_errcode);
        }
        if (ss_sel.desc.dpl != cs_sel.desc.dpl) {
            VERBOSE(("interrupt: descriptor DPL[SS](%d) != DPL[CS](%d)", ss_sel.desc.dpl, cs_sel.desc.dpl));
            EXCEPTION(TS_EXCEPTION, exc_errcode);
        }

        /* stack segment must be writable data segment. */
        if (SEG_IS_SYSTEM(&ss_sel.desc)) {
            VERBOSE(("interrupt: stack segment is system segment"));
            EXCEPTION(TS_EXCEPTION, exc_errcode);
        }
        if (SEG_IS_CODE(&ss_sel.desc)) {
            VERBOSE(("interrupt: stack segment is code segment"));
            EXCEPTION(TS_EXCEPTION, exc_errcode);
        }
        if (!SEG_IS_WRITABLE_DATA(&ss_sel.desc)) {
            VERBOSE(("interrupt: stack segment is read-only data segment"));
            EXCEPTION(TS_EXCEPTION, exc_errcode);
        }

        /* not present */
        if (selector_is_not_present(&ss_sel)) {
            VERBOSE(("interrupt: selector is not present"));
            EXCEPTION(SS_EXCEPTION, exc_errcode);
        }

        /* check stack room size */
        cpu_stack_push_check(ss_sel.idx, &ss_sel.desc, new_sp, stacksize, ss_sel.desc.d);

        /* out of range */
        if (new_ip > cs_sel.desc.u.seg.limit) {
            VERBOSE(("interrupt: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, cs_sel.desc.u.seg.limit));
            EXCEPTION(GP_EXCEPTION, 0);
        }

        load_ss(ss_sel.selector, &ss_sel.desc, cs_sel.desc.dpl);
        CPU_ESP = new_sp;

        load_cs(cs_sel.selector, &cs_sel.desc, cs_sel.desc.dpl);
        CPU_EIP = new_ip;

        if (is32bit) {
            if (CPU_STAT_VM86) {
                PUSH0_32(CPU_GS);
                PUSH0_32(CPU_FS);
                PUSH0_32(CPU_DS);
                PUSH0_32(CPU_ES);

                LOAD_SEGREG(CPU_GS_INDEX, 0);
                CPU_STAT_SREG(CPU_GS_INDEX).valid = 0;
                LOAD_SEGREG(CPU_FS_INDEX, 0);
                CPU_STAT_SREG(CPU_FS_INDEX).valid = 0;
                LOAD_SEGREG(CPU_DS_INDEX, 0);
                CPU_STAT_SREG(CPU_DS_INDEX).valid = 0;
                LOAD_SEGREG(CPU_ES_INDEX, 0);
                CPU_STAT_SREG(CPU_ES_INDEX).valid = 0;
            }
            PUSH0_32(old_ss);
            PUSH0_32(old_sp);
            PUSH0_32(old_flags);
            PUSH0_32(old_cs);
            PUSH0_32(old_ip);
            if (errorp) {
                PUSH0_32(error_code);
            }
        } else {
            if (CPU_STAT_VM86) {
                ia32_panic("interrupt: 16bit gate && VM86");
            }
            PUSH0_16(old_ss);
            PUSH0_16(old_sp);
            PUSH0_16(old_flags);
            PUSH0_16(old_cs);
            PUSH0_16(old_ip);
            if (errorp) {
                PUSH0_16(error_code);
            }
        }
    } else {
        if (CPU_STAT_VM86) {
            VERBOSE(("interrupt: VM86"));
            EXCEPTION(GP_EXCEPTION, exc_errcode);
        }
        if (!SEG_IS_CONFORMING_CODE(&cs_sel.desc) && (cs_sel.desc.dpl != CPU_STAT_CPL)) {
            VERBOSE(("interrupt: %sCONFORMING-CODE-SEGMENT(%d) && DPL[CS](%d) != CPL", SEG_IS_CONFORMING_CODE(&cs_sel.desc) ? "" : "NON-", cs_sel.desc.dpl, CPU_STAT_CPL));
            EXCEPTION(GP_EXCEPTION, exc_errcode);
        }

        VERBOSE(("interrupt: INTRA-PRIVILEGE-LEVEL-INTERRUPT"));

        stacksize = errorp ? 8 : 6;
        if (is32bit) {
            stacksize *= 2;
        }

        /* check stack room size */
        if (CPU_STAT_SS32) {
            sp = CPU_ESP;
        } else {
            sp = CPU_SP;
        }
        /*
         * 17.1
         * コールゲート、割り込みゲート、またはトラップゲートを通じて
         * プログラムの制御を他のコード・セグメントに移行するときは、
         * 移行中に使用されるオペランド・サイズは使用されるゲートの
         * タイプ(16 ビットまたは32 ビット)によって決まる(移行命
         * 令のD フラグ、プリフィックスのいずれにもよらない)。
         */
        SS_PUSH_CHECK1(sp, stacksize, is32bit);

        /* out of range */
        if (new_ip > cs_sel.desc.u.seg.limit) {
            VERBOSE(("interrupt: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, cs_sel.desc.u.seg.limit));
            EXCEPTION(GP_EXCEPTION, 0);
        }

        load_cs(cs_sel.selector, &cs_sel.desc, CPU_STAT_CPL);
        CPU_EIP = new_ip;

        if (is32bit) {
            PUSH0_32(old_flags);
            PUSH0_32(old_cs);
            PUSH0_32(old_ip);
            if (errorp) {
                PUSH0_32(error_code);
            }
        } else {
            PUSH0_16(old_flags);
            PUSH0_16(old_cs);
            PUSH0_16(old_ip);
            if (errorp) {
                PUSH0_16(error_code);
            }
        }
    }
    set_eflags(new_flags, mask);

    VERBOSE(("interrupt: new EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP));
}