BX_CPU_C::return_protected(bxInstruction_c *i, Bit16u pop_bytes) { Bit16u raw_cs_selector, raw_ss_selector; bx_selector_t cs_selector, ss_selector; bx_descriptor_t cs_descriptor, ss_descriptor; Bit32u stack_param_offset; bx_address return_RIP, return_RSP, temp_RSP; Bit32u dword1, dword2; /* + 6+N*2: SS | +12+N*4: SS | +24+N*8 SS */ /* + 4+N*2: SP | + 8+N*4: ESP | +16+N*8 RSP */ /* parm N | + parm N | + parm N */ /* parm 3 | + parm 3 | + parm 3 */ /* parm 2 | + parm 2 | + parm 2 */ /* + 4: parm 1 | + 8: parm 1 | +16: parm 1 */ /* + 2: CS | + 4: CS | + 8: CS */ /* + 0: IP | + 0: EIP | + 0: RIP */ #if BX_SUPPORT_X86_64 if (StackAddrSize64()) temp_RSP = RSP; else #endif { if (BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS].cache.u.segment.d_b) temp_RSP = ESP; else temp_RSP = SP; } #if BX_SUPPORT_X86_64 if (i->os64L()) { raw_cs_selector = (Bit16u) read_virtual_qword_64(BX_SEG_REG_SS, temp_RSP + 8); return_RIP = read_virtual_qword_64(BX_SEG_REG_SS, temp_RSP); stack_param_offset = 16; } else #endif if (i->os32L()) { raw_cs_selector = (Bit16u) read_virtual_dword(BX_SEG_REG_SS, temp_RSP + 4); return_RIP = read_virtual_dword(BX_SEG_REG_SS, temp_RSP); stack_param_offset = 8; } else { raw_cs_selector = read_virtual_word(BX_SEG_REG_SS, temp_RSP + 2); return_RIP = read_virtual_word(BX_SEG_REG_SS, temp_RSP); stack_param_offset = 4; } // selector must be non-null else #GP(0) if ((raw_cs_selector & 0xfffc) == 0) { BX_ERROR(("return_protected: CS selector null")); exception(BX_GP_EXCEPTION, 0, 0); } parse_selector(raw_cs_selector, &cs_selector); // selector index must be within its descriptor table limits, // else #GP(selector) fetch_raw_descriptor(&cs_selector, &dword1, &dword2, BX_GP_EXCEPTION); // descriptor AR byte must indicate code segment, else #GP(selector) parse_descriptor(dword1, dword2, &cs_descriptor); // return selector RPL must be >= CPL, else #GP(return selector) if (cs_selector.rpl < CPL) { BX_ERROR(("return_protected: CS.rpl < CPL")); exception(BX_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0); } // check code-segment descriptor check_cs(&cs_descriptor, raw_cs_selector, 0, cs_selector.rpl); // if return selector RPL == CPL then // RETURN TO SAME PRIVILEGE LEVEL if (cs_selector.rpl == CPL) { BX_DEBUG(("return_protected: return to SAME PRIVILEGE LEVEL")); branch_far64(&cs_selector, &cs_descriptor, return_RIP, CPL); #if BX_SUPPORT_X86_64 if (StackAddrSize64()) RSP += stack_param_offset + pop_bytes; else #endif { if (BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS].cache.u.segment.d_b) RSP = ESP + stack_param_offset + pop_bytes; else SP += stack_param_offset + pop_bytes; } return; } /* RETURN TO OUTER PRIVILEGE LEVEL */ else { /* + 6+N*2: SS | +12+N*4: SS | +24+N*8 SS */ /* + 4+N*2: SP | + 8+N*4: ESP | +16+N*8 RSP */ /* parm N | + parm N | + parm N */ /* parm 3 | + parm 3 | + parm 3 */ /* parm 2 | + parm 2 | + parm 2 */ /* + 4: parm 1 | + 8: parm 1 | +16: parm 1 */ /* + 2: CS | + 4: CS | + 8: CS */ /* + 0: IP | + 0: EIP | + 0: RIP */ BX_DEBUG(("return_protected: return to OUTER PRIVILEGE LEVEL")); #if BX_SUPPORT_X86_64 if (i->os64L()) { raw_ss_selector = read_virtual_word_64 (BX_SEG_REG_SS, temp_RSP + 24 + pop_bytes); return_RSP = read_virtual_qword_64(BX_SEG_REG_SS, temp_RSP + 16 + pop_bytes); } else #endif if (i->os32L()) { raw_ss_selector = read_virtual_word (BX_SEG_REG_SS, temp_RSP + 12 + pop_bytes); return_RSP = read_virtual_dword(BX_SEG_REG_SS, temp_RSP + 8 + pop_bytes); } else { raw_ss_selector = read_virtual_word(BX_SEG_REG_SS, temp_RSP + 6 + pop_bytes); return_RSP = read_virtual_word(BX_SEG_REG_SS, temp_RSP + 4 + pop_bytes); } /* selector index must be within its descriptor table limits, * else #GP(selector) */ parse_selector(raw_ss_selector, &ss_selector); if ((raw_ss_selector & 0xfffc) == 0) { if (long_mode()) { if (! IS_LONG64_SEGMENT(cs_descriptor) || (cs_selector.rpl == 3)) { BX_ERROR(("return_protected: SS selector null")); exception(BX_GP_EXCEPTION, 0, 0); } } else // not in long or compatibility mode { BX_ERROR(("return_protected: SS selector null")); exception(BX_GP_EXCEPTION, 0, 0); } } fetch_raw_descriptor(&ss_selector, &dword1, &dword2, BX_GP_EXCEPTION); parse_descriptor(dword1, dword2, &ss_descriptor); /* selector RPL must = RPL of the return CS selector, * else #GP(selector) */ if (ss_selector.rpl != cs_selector.rpl) { BX_ERROR(("return_protected: ss.rpl != cs.rpl")); exception(BX_GP_EXCEPTION, raw_ss_selector & 0xfffc, 0); } /* descriptor AR byte must indicate a writable data segment, * else #GP(selector) */ if (ss_descriptor.valid==0 || ss_descriptor.segment==0 || IS_CODE_SEGMENT(ss_descriptor.type) || !IS_DATA_SEGMENT_WRITEABLE(ss_descriptor.type)) { BX_ERROR(("return_protected: SS.AR byte not writable data")); exception(BX_GP_EXCEPTION, raw_ss_selector & 0xfffc, 0); } /* descriptor dpl must = RPL of the return CS selector, * else #GP(selector) */ if (ss_descriptor.dpl != cs_selector.rpl) { BX_ERROR(("return_protected: SS.dpl != cs.rpl")); exception(BX_GP_EXCEPTION, raw_ss_selector & 0xfffc, 0); } /* segment must be present else #SS(selector) */ if (! IS_PRESENT(ss_descriptor)) { BX_ERROR(("return_protected: ss.present == 0")); exception(BX_SS_EXCEPTION, raw_ss_selector & 0xfffc, 0); } branch_far64(&cs_selector, &cs_descriptor, return_RIP, cs_selector.rpl); /* load SS:SP from stack */ /* load SS-cache with return SS descriptor */ load_ss(&ss_selector, &ss_descriptor, cs_selector.rpl); #if BX_SUPPORT_X86_64 if (StackAddrSize64()) RSP = return_RSP + pop_bytes; else #endif if (ss_descriptor.u.segment.d_b) RSP = (Bit32u) return_RSP + pop_bytes; else SP = (Bit16u) return_RSP + pop_bytes; /* check ES, DS, FS, GS for validity */ validate_seg_regs(); } }
void BX_CPU_C::long_mode_int(Bit8u vector, unsigned soft_int, bx_bool push_error, Bit16u error_code) { bx_descriptor_t gate_descriptor, cs_descriptor; bx_selector_t cs_selector; // interrupt vector must be within IDT table limits, // else #GP(vector*8 + 2 + EXT) if ((vector*16 + 15) > BX_CPU_THIS_PTR idtr.limit) { BX_ERROR(("interrupt(long mode): vector must be within IDT table limits, IDT.limit = 0x%x", BX_CPU_THIS_PTR idtr.limit)); exception(BX_GP_EXCEPTION, vector*8 + 2); } Bit64u desctmp1 = system_read_qword(BX_CPU_THIS_PTR idtr.base + vector*16); Bit64u desctmp2 = system_read_qword(BX_CPU_THIS_PTR idtr.base + vector*16 + 8); if (desctmp2 & BX_CONST64(0x00001F0000000000)) { BX_ERROR(("interrupt(long mode): IDT entry extended attributes DWORD4 TYPE != 0")); exception(BX_GP_EXCEPTION, vector*8 + 2); } Bit32u dword1 = GET32L(desctmp1); Bit32u dword2 = GET32H(desctmp1); Bit32u dword3 = GET32L(desctmp2); parse_descriptor(dword1, dword2, &gate_descriptor); if ((gate_descriptor.valid==0) || gate_descriptor.segment) { BX_ERROR(("interrupt(long mode): gate descriptor is not valid sys seg")); exception(BX_GP_EXCEPTION, vector*8 + 2); } // descriptor AR byte must indicate interrupt gate, trap gate, // or task gate, else #GP(vector*8 + 2 + EXT) if (gate_descriptor.type != BX_386_INTERRUPT_GATE && gate_descriptor.type != BX_386_TRAP_GATE) { BX_ERROR(("interrupt(long mode): unsupported gate type %u", (unsigned) gate_descriptor.type)); exception(BX_GP_EXCEPTION, vector*8 + 2); } // if software interrupt, then gate descripor DPL must be >= CPL, // else #GP(vector * 8 + 2 + EXT) if (soft_int && gate_descriptor.dpl < CPL) { BX_ERROR(("interrupt(long mode): soft_int && gate.dpl < CPL")); exception(BX_GP_EXCEPTION, vector*8 + 2); } // Gate must be present, else #NP(vector * 8 + 2 + EXT) if (! IS_PRESENT(gate_descriptor)) { BX_ERROR(("interrupt(long mode): gate.p == 0")); exception(BX_NP_EXCEPTION, vector*8 + 2); } Bit16u gate_dest_selector = gate_descriptor.u.gate.dest_selector; Bit64u gate_dest_offset = ((Bit64u)dword3 << 32) | gate_descriptor.u.gate.dest_offset; unsigned ist = gate_descriptor.u.gate.param_count & 0x7; // examine CS selector and descriptor given in gate descriptor // selector must be non-null else #GP(EXT) if ((gate_dest_selector & 0xfffc) == 0) { BX_ERROR(("int_trap_gate(long mode): selector null")); exception(BX_GP_EXCEPTION, 0); } parse_selector(gate_dest_selector, &cs_selector); // selector must be within its descriptor table limits // else #GP(selector+EXT) fetch_raw_descriptor(&cs_selector, &dword1, &dword2, BX_GP_EXCEPTION); parse_descriptor(dword1, dword2, &cs_descriptor); // descriptor AR byte must indicate code seg // and code segment descriptor DPL<=CPL, else #GP(selector+EXT) if (cs_descriptor.valid==0 || cs_descriptor.segment==0 || IS_DATA_SEGMENT(cs_descriptor.type) || cs_descriptor.dpl > CPL) { BX_ERROR(("interrupt(long mode): not accessible or not code segment")); exception(BX_GP_EXCEPTION, cs_selector.value & 0xfffc); } // check that it's a 64 bit segment if (! IS_LONG64_SEGMENT(cs_descriptor) || cs_descriptor.u.segment.d_b) { BX_ERROR(("interrupt(long mode): must be 64 bit segment")); exception(BX_GP_EXCEPTION, cs_selector.value & 0xfffc); } // segment must be present, else #NP(selector + EXT) if (! IS_PRESENT(cs_descriptor)) { BX_ERROR(("interrupt(long mode): segment not present")); exception(BX_NP_EXCEPTION, cs_selector.value & 0xfffc); } Bit64u RSP_for_cpl_x; Bit64u old_CS = BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value; Bit64u old_RIP = RIP; Bit64u old_SS = BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS].selector.value; Bit64u old_RSP = RSP; // if code segment is non-conforming and DPL < CPL then // INTERRUPT TO INNER PRIVILEGE: if (IS_CODE_SEGMENT_NON_CONFORMING(cs_descriptor.type) && cs_descriptor.dpl < CPL) { BX_DEBUG(("interrupt(long mode): INTERRUPT TO INNER PRIVILEGE")); // check selector and descriptor for new stack in current TSS if (ist > 0) { BX_DEBUG(("interrupt(long mode): trap to IST, vector = %d", ist)); RSP_for_cpl_x = get_RSP_from_TSS(ist+3); } else { RSP_for_cpl_x = get_RSP_from_TSS(cs_descriptor.dpl); } // align stack RSP_for_cpl_x &= BX_CONST64(0xfffffffffffffff0); // push old stack long pointer onto new stack write_new_stack_qword_64(RSP_for_cpl_x - 8, cs_descriptor.dpl, old_SS); write_new_stack_qword_64(RSP_for_cpl_x - 16, cs_descriptor.dpl, old_RSP); write_new_stack_qword_64(RSP_for_cpl_x - 24, cs_descriptor.dpl, read_eflags()); // push long pointer to return address onto new stack write_new_stack_qword_64(RSP_for_cpl_x - 32, cs_descriptor.dpl, old_CS); write_new_stack_qword_64(RSP_for_cpl_x - 40, cs_descriptor.dpl, old_RIP); RSP_for_cpl_x -= 40; if (push_error) { RSP_for_cpl_x -= 8; write_new_stack_qword_64(RSP_for_cpl_x, cs_descriptor.dpl, error_code); } // load CS:RIP (guaranteed to be in 64 bit mode) branch_far64(&cs_selector, &cs_descriptor, gate_dest_offset, cs_descriptor.dpl); // set up null SS descriptor load_null_selector(&BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS], cs_descriptor.dpl); } else if(IS_CODE_SEGMENT_CONFORMING(cs_descriptor.type) || cs_descriptor.dpl==CPL) { // if code segment is conforming OR code segment DPL = CPL then // INTERRUPT TO SAME PRIVILEGE LEVEL: BX_DEBUG(("interrupt(long mode): INTERRUPT TO SAME PRIVILEGE")); // check selector and descriptor for new stack in current TSS if (ist > 0) { BX_DEBUG(("interrupt(long mode): trap to IST, vector = %d", ist)); RSP_for_cpl_x = get_RSP_from_TSS(ist+3); } else { RSP_for_cpl_x = RSP; } // align stack RSP_for_cpl_x &= BX_CONST64(0xfffffffffffffff0); // push flags onto stack // push current CS selector onto stack // push return offset onto stack write_new_stack_qword_64(RSP_for_cpl_x - 8, cs_descriptor.dpl, old_SS); write_new_stack_qword_64(RSP_for_cpl_x - 16, cs_descriptor.dpl, old_RSP); write_new_stack_qword_64(RSP_for_cpl_x - 24, cs_descriptor.dpl, read_eflags()); // push long pointer to return address onto new stack write_new_stack_qword_64(RSP_for_cpl_x - 32, cs_descriptor.dpl, old_CS); write_new_stack_qword_64(RSP_for_cpl_x - 40, cs_descriptor.dpl, old_RIP); RSP_for_cpl_x -= 40; if (push_error) { RSP_for_cpl_x -= 8; write_new_stack_qword_64(RSP_for_cpl_x, cs_descriptor.dpl, error_code); } // set the RPL field of CS to CPL branch_far64(&cs_selector, &cs_descriptor, gate_dest_offset, CPL); } else { BX_ERROR(("interrupt(long mode): bad descriptor type %u (CS.DPL=%u CPL=%u)", (unsigned) cs_descriptor.type, (unsigned) cs_descriptor.dpl, (unsigned) CPL)); exception(BX_GP_EXCEPTION, cs_selector.value & 0xfffc); } RSP = RSP_for_cpl_x; // if interrupt gate then set IF to 0 if (!(gate_descriptor.type & 1)) // even is int-gate BX_CPU_THIS_PTR clear_IF(); BX_CPU_THIS_PTR clear_TF(); //BX_CPU_THIS_PTR clear_VM(); // VM is clear in long mode BX_CPU_THIS_PTR clear_RF(); BX_CPU_THIS_PTR clear_NT(); }
BX_CPU_C::call_gate64(bx_selector_t *gate_selector) { bx_selector_t cs_selector; Bit32u dword1, dword2, dword3; bx_descriptor_t cs_descriptor; bx_descriptor_t gate_descriptor; // examine code segment selector in call gate descriptor BX_DEBUG(("call_gate64: CALL 64bit call gate")); fetch_raw_descriptor_64(gate_selector, &dword1, &dword2, &dword3, BX_GP_EXCEPTION); parse_descriptor(dword1, dword2, &gate_descriptor); Bit16u dest_selector = gate_descriptor.u.gate.dest_selector; // selector must not be null else #GP(0) if ((dest_selector & 0xfffc) == 0) { BX_ERROR(("call_gate64: selector in gate null")); exception(BX_GP_EXCEPTION, 0, 0); } parse_selector(dest_selector, &cs_selector); // selector must be within its descriptor table limits, // else #GP(code segment selector) fetch_raw_descriptor(&cs_selector, &dword1, &dword2, BX_GP_EXCEPTION); parse_descriptor(dword1, dword2, &cs_descriptor); // find the RIP in the gate_descriptor Bit64u new_RIP = gate_descriptor.u.gate.dest_offset; new_RIP |= ((Bit64u)dword3 << 32); // AR byte of selected descriptor must indicate code segment, // else #GP(code segment selector) // DPL of selected descriptor must be <= CPL, // else #GP(code segment selector) if (cs_descriptor.valid==0 || cs_descriptor.segment==0 || IS_DATA_SEGMENT(cs_descriptor.type) || cs_descriptor.dpl > CPL) { BX_ERROR(("call_gate64: selected descriptor is not code")); exception(BX_GP_EXCEPTION, dest_selector & 0xfffc, 0); } // In long mode, only 64-bit call gates are allowed, and they must point // to 64-bit code segments, else #GP(selector) if (! IS_LONG64_SEGMENT(cs_descriptor) || cs_descriptor.u.segment.d_b) { BX_ERROR(("call_gate64: not 64-bit code segment in call gate 64")); exception(BX_GP_EXCEPTION, dest_selector & 0xfffc, 0); } // code segment must be present else #NP(selector) if (! IS_PRESENT(cs_descriptor)) { BX_ERROR(("call_gate64: code segment not present !")); exception(BX_NP_EXCEPTION, dest_selector & 0xfffc, 0); } Bit64u old_CS = BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value; Bit64u old_RIP = RIP; // CALL GATE TO MORE PRIVILEGE // if non-conforming code segment and DPL < CPL then if (IS_CODE_SEGMENT_NON_CONFORMING(cs_descriptor.type) && (cs_descriptor.dpl < CPL)) { Bit64u RSP_for_cpl_x; BX_DEBUG(("CALL GATE TO MORE PRIVILEGE LEVEL")); // get new RSP for new privilege level from TSS get_RSP_from_TSS(cs_descriptor.dpl, &RSP_for_cpl_x); Bit64u old_SS = BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS].selector.value; Bit64u old_RSP = RSP; if (! IsCanonical(RSP_for_cpl_x)) { // #SS(selector) when changing priviledge level BX_ERROR(("call_gate64: canonical address failure %08x%08x", GET32H(RSP_for_cpl_x), GET32L(RSP_for_cpl_x))); exception(BX_SS_EXCEPTION, old_SS & 0xfffc, 0); } // push old stack long pointer onto new stack write_new_stack_qword_64(RSP_for_cpl_x - 8, cs_descriptor.dpl, old_SS); write_new_stack_qword_64(RSP_for_cpl_x - 16, cs_descriptor.dpl, old_RSP); // push long pointer to return address onto new stack write_new_stack_qword_64(RSP_for_cpl_x - 24, cs_descriptor.dpl, old_CS); write_new_stack_qword_64(RSP_for_cpl_x - 32, cs_descriptor.dpl, old_RIP); RSP_for_cpl_x -= 32; // prepare new stack null SS selector bx_selector_t ss_selector; bx_descriptor_t ss_descriptor; // set up a null descriptor parse_selector(0, &ss_selector); parse_descriptor(0, 0, &ss_descriptor); // load CS:RIP (guaranteed to be in 64 bit mode) branch_far64(&cs_selector, &cs_descriptor, new_RIP, cs_descriptor.dpl); // set up null SS descriptor load_ss(&ss_selector, &ss_descriptor, cs_descriptor.dpl); RSP = RSP_for_cpl_x; } else { BX_DEBUG(("CALL GATE TO SAME PRIVILEGE")); // push to 64-bit stack, switch to long64 guaranteed write_new_stack_qword_64(RSP - 8, CPL, old_CS); write_new_stack_qword_64(RSP - 16, CPL, old_RIP); RSP -= 16; // load CS:RIP (guaranteed to be in 64 bit mode) branch_far64(&cs_selector, &cs_descriptor, new_RIP, CPL); } }