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); } }
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_protected(bxInstruction_c *i, Bit16u cs_raw, bx_address disp) { bx_selector_t cs_selector; Bit32u dword1, dword2; bx_descriptor_t cs_descriptor; /* new cs selector must not be null, else #GP(0) */ if ((cs_raw & 0xfffc) == 0) { BX_ERROR(("call_protected: CS selector null")); exception(BX_GP_EXCEPTION, 0); } parse_selector(cs_raw, &cs_selector); // check new CS selector index within its descriptor limits, // else #GP(new CS selector) fetch_raw_descriptor(&cs_selector, &dword1, &dword2, BX_GP_EXCEPTION); parse_descriptor(dword1, dword2, &cs_descriptor); // examine AR byte of selected descriptor for various legal values if (cs_descriptor.valid==0) { BX_ERROR(("call_protected: invalid CS descriptor")); exception(BX_GP_EXCEPTION, cs_raw & 0xfffc); } if (cs_descriptor.segment) // normal segment { check_cs(&cs_descriptor, cs_raw, BX_SELECTOR_RPL(cs_raw), CPL); #if BX_SUPPORT_X86_64 if (long_mode() && cs_descriptor.u.segment.l) { Bit64u temp_rsp = RSP; // moving to long mode, push return address onto 64-bit stack if (i->os64L()) { write_new_stack_qword_64(temp_rsp - 8, cs_descriptor.dpl, BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value); write_new_stack_qword_64(temp_rsp - 16, cs_descriptor.dpl, RIP); temp_rsp -= 16; } else if (i->os32L()) { write_new_stack_dword_64(temp_rsp - 4, cs_descriptor.dpl, BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value); write_new_stack_dword_64(temp_rsp - 8, cs_descriptor.dpl, EIP); temp_rsp -= 8; } else { write_new_stack_word_64(temp_rsp - 2, cs_descriptor.dpl, BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value); write_new_stack_word_64(temp_rsp - 4, cs_descriptor.dpl, IP); temp_rsp -= 4; } // load code segment descriptor into CS cache // load CS with new code segment selector // set RPL of CS to CPL branch_far64(&cs_selector, &cs_descriptor, disp, CPL); RSP = temp_rsp; } else #endif { Bit32u temp_RSP; // moving to legacy mode, push return address onto 32-bit stack 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()) { write_new_stack_qword_32(&BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS], temp_RSP - 8, cs_descriptor.dpl, BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value); write_new_stack_qword_32(&BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS], temp_RSP - 16, cs_descriptor.dpl, RIP); temp_RSP -= 16; } else #endif if (i->os32L()) { write_new_stack_dword_32(&BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS], temp_RSP - 4, cs_descriptor.dpl, BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value); write_new_stack_dword_32(&BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS], temp_RSP - 8, cs_descriptor.dpl, EIP); temp_RSP -= 8; } else { write_new_stack_word_32(&BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS], temp_RSP - 2, cs_descriptor.dpl, BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value); write_new_stack_word_32(&BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS], temp_RSP - 4, cs_descriptor.dpl, IP); temp_RSP -= 4; } // load code segment descriptor into CS cache // load CS with new code segment selector // set RPL of CS to CPL branch_far64(&cs_selector, &cs_descriptor, disp, CPL); if (BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS].cache.u.segment.d_b) ESP = (Bit32u) temp_RSP; else SP = (Bit16u) temp_RSP; } return; } else { // gate & special segment bx_descriptor_t gate_descriptor = cs_descriptor; bx_selector_t gate_selector = cs_selector; // descriptor DPL must be >= CPL else #GP(gate selector) if (gate_descriptor.dpl < CPL) { BX_ERROR(("call_protected: descriptor.dpl < CPL")); exception(BX_GP_EXCEPTION, cs_raw & 0xfffc); } // descriptor DPL must be >= gate selector RPL else #GP(gate selector) if (gate_descriptor.dpl < gate_selector.rpl) { BX_ERROR(("call_protected: descriptor.dpl < selector.rpl")); exception(BX_GP_EXCEPTION, cs_raw & 0xfffc); } #if BX_SUPPORT_X86_64 if (long_mode()) { // call gate type is higher priority than non-present bit check if (gate_descriptor.type != BX_386_CALL_GATE) { BX_ERROR(("call_protected: gate type %u unsupported in long mode", (unsigned) gate_descriptor.type)); exception(BX_GP_EXCEPTION, cs_raw & 0xfffc); } // gate descriptor must be present else #NP(gate selector) if (! IS_PRESENT(gate_descriptor)) { BX_ERROR(("call_protected: call gate not present")); exception(BX_NP_EXCEPTION, cs_raw & 0xfffc); } call_gate64(&gate_selector); return; } #endif switch (gate_descriptor.type) { case BX_SYS_SEGMENT_AVAIL_286_TSS: case BX_SYS_SEGMENT_AVAIL_386_TSS: if (gate_descriptor.type==BX_SYS_SEGMENT_AVAIL_286_TSS) BX_DEBUG(("call_protected: 16bit available TSS")); else BX_DEBUG(("call_protected: 32bit available TSS")); if (gate_descriptor.valid==0 || gate_selector.ti) { BX_ERROR(("call_protected: call bad TSS selector !")); exception(BX_GP_EXCEPTION, cs_raw & 0xfffc); } // TSS must be present, else #NP(TSS selector) if (! IS_PRESENT(gate_descriptor)) { BX_ERROR(("call_protected: call not present TSS !")); exception(BX_NP_EXCEPTION, cs_raw & 0xfffc); } // SWITCH_TASKS _without_ nesting to TSS task_switch(i, &gate_selector, &gate_descriptor, BX_TASK_FROM_CALL, dword1, dword2); // EIP must be in code seg limit, else #GP(0) if (EIP > BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].cache.u.segment.limit_scaled) { BX_ERROR(("call_protected: EIP not within CS limits")); exception(BX_GP_EXCEPTION, 0); } return; case BX_TASK_GATE: task_gate(i, &gate_selector, &gate_descriptor, BX_TASK_FROM_CALL); return; case BX_286_CALL_GATE: case BX_386_CALL_GATE: // gate descriptor must be present else #NP(gate selector) if (! IS_PRESENT(gate_descriptor)) { BX_ERROR(("call_protected: gate not present")); exception(BX_NP_EXCEPTION, cs_raw & 0xfffc); } call_gate(&gate_descriptor); return; default: // can't get here BX_ERROR(("call_protected(): gate.type(%u) unsupported", (unsigned) gate_descriptor.type)); exception(BX_GP_EXCEPTION, cs_raw & 0xfffc); } } }