diff options
author | 2012-01-06 01:55:32 +0000 | |
---|---|---|
committer | 2012-01-06 01:55:32 +0000 | |
commit | bdbe7ad11cd6293e34e9be804c4130a2ef380225 (patch) | |
tree | e546b2c670275a21bc33c586cda48b805704ba65 /src/emu | |
parent | 2bec585837cc38284419f67c90257e2794bb16bb (diff) |
i386: Moved stack changes to be after protection checks in RETF and IRET
Made ARPL protected mode only
Added protection checks when MOVing to a segment register.
Diffstat (limited to 'src/emu')
-rw-r--r-- | src/emu/cpu/i386/i386.c | 212 | ||||
-rw-r--r-- | src/emu/cpu/i386/i386op16.c | 2 | ||||
-rw-r--r-- | src/emu/cpu/i386/i386ops.c | 48 |
3 files changed, 205 insertions, 57 deletions
diff --git a/src/emu/cpu/i386/i386.c b/src/emu/cpu/i386/i386.c index 65fc2459d5a..3adcbef5ec9 100644 --- a/src/emu/cpu/i386/i386.c +++ b/src/emu/cpu/i386/i386.c @@ -379,6 +379,126 @@ static void i386_check_sreg_validity(i386_state* cpustate, int reg) } } +static void i386_protected_mode_sreg_load(i386_state *cpustate, UINT16 selector, UINT8 reg) +{ + // Checks done when MOV changes a segment register in protected mode + UINT8 CPL,RPL,DPL; + + CPL = cpustate->CPL; + RPL = selector & 0x0003; + + if(reg == SS) + { + I386_SREG stack; + + memset(&stack, 0, sizeof(stack)); + stack.selector = selector; + i386_load_protected_mode_segment(cpustate,&stack); + DPL = (stack.flags >> 5) & 0x03; + + if((selector & ~0x0007) == 0) + { + logerror("SReg Load (%08x): Selector is null.\n",cpustate->pc); + FAULT(FAULT_GP,0) + } + if(selector & 0x0004) // LDT + { + if((selector & ~0x0007) > cpustate->ldtr.limit) + { + logerror("SReg Load (%08x): Selector is out of LDT bounds.\n",cpustate->pc); + FAULT(FAULT_GP,selector) + } + } + else // GDT + { + if((selector & ~0x0007) > cpustate->gdtr.limit) + { + logerror("SReg Load (%08x): Selector is out of GDT bounds.\n",cpustate->pc); + FAULT(FAULT_GP,selector) + } + } + if (RPL != CPL) + { + logerror("SReg Load (%08x): Selector RPL does not equal CPL.\n",cpustate->pc); + FAULT(FAULT_GP,selector) + } + if(((stack.flags & 0x0018) != 0x10) && (stack.flags & 0x0002) != 0) + { + logerror("SReg Load (%08x): Segment is not a writable data segment.\n",cpustate->pc); + FAULT(FAULT_GP,selector) + } + if(DPL != CPL) + { + logerror("SReg Load (%08x): Segment DPL does not equal CPL.\n",cpustate->pc); + FAULT(FAULT_GP,selector) + } + if(!(stack.flags & 0x0080)) + { + logerror("SReg Load (%08x): Segment is not present.\n",cpustate->pc); + FAULT(FAULT_SS,selector) + } + } + if(reg == DS || reg == ES || reg == FS || reg == GS) + { + I386_SREG desc; + + if((selector & ~0x0007) == 0) + { + cpustate->sreg[reg].selector = selector; + i386_load_segment_descriptor(cpustate, reg ); + // TODO: mark segment as invalid + return; + } + + memset(&desc, 0, sizeof(desc)); + desc.selector = selector; + i386_load_protected_mode_segment(cpustate,&desc); + DPL = (desc.flags >> 5) & 0x03; + + if(selector & 0x0004) // LDT + { + if((selector & ~0x0007) > cpustate->ldtr.limit) + { + logerror("SReg Load (%08x): Selector is out of LDT bounds.\n",cpustate->pc); + FAULT(FAULT_GP,selector) + } + } + else // GDT + { + if((selector & ~0x0007) > cpustate->gdtr.limit) + { + logerror("SReg Load (%08x): Selector is out of GDT bounds.\n",cpustate->pc); + FAULT(FAULT_GP,selector) + } + } + if((desc.flags & 0x0018) != 0x10) + { + if(((desc.flags & 0x0002) != 0) && ((desc.flags & 0x0018) != 0x18)) + { + logerror("SReg Load (%08x): Segment is not a data segment or readable code segment.\n",cpustate->pc); + FAULT(FAULT_GP,selector) + } + } + if(((desc.flags & 0x0018) == 0x10) || ((!(desc.flags & 0x0004)) && ((desc.flags & 0x0018) == 0x18))) + { + // if data or non-conforming code segment + if((RPL > DPL) || (CPL > DPL)) + { + logerror("SReg Load (%08x): Selector RPL or CPL is not less or equal to segment DPL.\n",cpustate->pc); + FAULT(FAULT_GP,selector) + } + } + if(!(desc.flags & 0x0080)) + { + logerror("SReg Load (%08x): Segment is not present.\n",cpustate->pc); + FAULT(FAULT_NP,selector) + } + } + + cpustate->sreg[reg].selector = selector; + i386_load_segment_descriptor(cpustate, reg ); +} + static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level) { /* I386 Interrupts/Traps/Faults: @@ -728,7 +848,7 @@ static void i386_trap_with_error(i386_state *cpustate,int irq, int irq_gate, int { // for these exceptions, an error code is pushed onto the stack by the processor. // no error code is pushed for software interrupts, either. - PUSH32(cpustate,error); + PUSH16(cpustate,error); } } @@ -1253,7 +1373,7 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of if((selector & ~0x07) == 0) { - logerror("CALL: Selector is null.\n"); + logerror("CALL (%08x): Selector is null.\n",cpustate->pc); FAULT(FAULT_GP,0) // #GP(0) } if(selector & 0x04) @@ -1588,6 +1708,7 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of logerror("CALL: EIP is past segment limit.\n"); FAULT(FAULT_GP,0) // #GP(0) } + SetRPL = 1; } break; case 0x05: // task gate @@ -1682,17 +1803,24 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op I386_SREG desc; UINT8 CPL, RPL, DPL; + if(operand32 == 0) { newEIP = POP16(cpustate) & 0xffff; newCS = POP16(cpustate) & 0xffff; REG16(SP) += count; + newESP = POP16(cpustate) & 0xffff; + newSS = POP16(cpustate) & 0xffff; + REG16(SP) -= (8+count); // re-adjust stack pointer } else { newEIP = POP32(cpustate); newCS = POP32(cpustate) & 0xffff; REG32(ESP) += count; + newESP = POP32(cpustate); + newSS = POP32(cpustate) & 0xffff; + REG32(ESP) -= (16+count); // re-adjust stack pointer } memset(&desc, 0, sizeof(desc)); @@ -1704,7 +1832,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op if(RPL < CPL) { - logerror("RETF: Return segment RPL is less than CPL.\n"); + logerror("RETF (%08x): Return segment RPL is less than CPL.\n",cpustate->pc); FAULT(FAULT_GP,newCS & ~0x07) } @@ -1713,7 +1841,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op /* same privilege level */ if((newCS & ~0x07) == 0) { - logerror("RETF: Return segment RPL is less than CPL.\n"); + logerror("RETF: Return segment is null.\n"); FAULT(FAULT_GP,0) } if(newCS & 0x04) @@ -1767,7 +1895,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op { if(REG16(SP) > (cpustate->sreg[SS].limit & 0xffff)) { - logerror("RETF: SP is past stack segment limit.\n"); + logerror("RETF (%08x): SP is past stack segment limit.\n",cpustate->pc); FAULT(FAULT_SS,0) } } @@ -1779,25 +1907,25 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op FAULT(FAULT_SS,0) } } + if(operand32 == 0) + REG16(SP) += (4+count); + else + REG32(ESP) += (8+count); } else if(RPL > CPL) { /* outer privilege level */ if(operand32 == 0) { - newESP = POP16(cpustate) & 0xffff; - newSS = POP16(cpustate); - if(newESP+8+count > cpustate->sreg[SS].limit) + if(REG16(SP)+8+count > cpustate->sreg[SS].limit) { - logerror("RETF: SP is past stack segment limit.\n"); + logerror("RETF (%08x): SP is past stack segment limit.\n",cpustate->pc); FAULT(FAULT_SS,0) } } else { - newESP = POP32(cpustate); - newSS = POP32(cpustate); - if(newESP+16+count > cpustate->sreg[SS].limit) + if(REG32(ESP)+16+count > cpustate->sreg[SS].limit) { logerror("RETF: ESP is past stack segment limit.\n"); FAULT(FAULT_SS,0) @@ -1870,7 +1998,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op { if((newSS & ~0x07) > cpustate->ldtr.limit) { - logerror("RETF: SS segment selector is past LDT limit.\n"); + logerror("RETF (%08x): SS segment selector is past LDT limit.\n",cpustate->pc); FAULT(FAULT_GP,newSS & ~0x07) } } @@ -1878,7 +2006,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op { if((newSS & ~0x07) > cpustate->gdtr.limit) { - logerror("RETF: SS segment selector is past GDT limit.\n"); + logerror("RETF (%08x): SS segment selector is past GDT limit.\n",cpustate->pc); FAULT(FAULT_GP,newSS & ~0x07) } } @@ -1913,9 +2041,10 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op i386_load_segment_descriptor(cpustate, SS ); if(operand32 == 0) - REG16(SP) += count; + REG16(SP) += (8+count); else - REG32(ESP) += count; + REG32(ESP) += (16+count); + /* Check that DS, ES, FS and GS are valid for the new privilege level */ i386_check_sreg_validity(cpustate,DS); @@ -1945,15 +2074,21 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) CPL = cpustate->CPL; if(operand32 == 0) { - newEIP = POP16(cpustate); - newCS = POP16(cpustate); - newflags = POP16(cpustate); + newEIP = POP16(cpustate) & 0xffff; + newCS = POP16(cpustate) & 0xffff; + newflags = POP16(cpustate) & 0xffff; + newESP = POP16(cpustate) & 0xffff; + newSS = POP16(cpustate) & 0xffff; + REG16(SP) -= 10; } else { newEIP = POP32(cpustate); newCS = POP32(cpustate) & 0xffff; newflags = POP32(cpustate); + newESP = POP32(cpustate); + newSS = POP32(cpustate) & 0xffff; + REG32(ESP) -= 20; } if(V8086_MODE) @@ -2001,7 +2136,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) if(newflags & 0x00020000) // if returning to virtual 8086 mode { /* Return to v86 mode */ - popmessage("IRET: Unimplemented return to Virtual 8086 mode."); + popmessage("IRET (%08x): Unimplemented return to Virtual 8086 mode.",cpustate->pc); } else { @@ -2024,7 +2159,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) RPL = newCS & 0x03; if(RPL < CPL) { - logerror("IRET: Return CS RPL is less than CPL.\n"); + logerror("IRET (%08x): Return CS RPL is less than CPL.\n",cpustate->pc); FAULT(FAULT_GP,newCS & ~0x07) } if(RPL == CPL) @@ -2074,7 +2209,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) RPL = newCS & 0x03; if((desc.flags & 0x0018) != 0x0018) { - logerror("IRET: Return CS segment is not a code segment.\n"); + logerror("IRET (%08x): Return CS segment is not a code segment.\n",cpustate->pc); FAULT(FAULT_GP,newCS & ~0x07) } if(desc.flags & 0x0004) @@ -2108,45 +2243,43 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) cpustate->eip = newEIP; cpustate->sreg[CS].selector = newCS; set_flags(cpustate,newflags); + REG16(SP) += 6; } else { cpustate->eip = newEIP; cpustate->sreg[CS].selector = newCS & 0xffff; set_flags(cpustate,newflags); + REG32(ESP) += 12; } } else if(RPL > CPL) { I386_SREG stack; /* return to outer privilege level */ + memset(&desc, 0, sizeof(desc)); + desc.selector = newCS; + i386_load_protected_mode_segment(cpustate,&desc); + DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level + RPL = newCS & 0x03; + memset(&stack, 0, sizeof(stack)); + stack.selector = newSS; + i386_load_protected_mode_segment(cpustate,&stack); if(operand32 == 0) { - newESP = POP16(cpustate); - newSS = POP16(cpustate); - if(REG16(SP) > cpustate->sreg[SS].limit) + if(REG16(SP)+10 > cpustate->sreg[SS].limit) { - logerror("IRET: ESP is past SS limit.\n"); + logerror("IRET: SP is past SS limit.\n"); FAULT(FAULT_SS,0) } } else { - newESP = POP32(cpustate); - newSS = POP32(cpustate) & 0xffff; - if(REG32(ESP) > cpustate->sreg[SS].limit) + if(REG32(ESP)+20 > cpustate->sreg[SS].limit) { logerror("IRET: ESP is past SS limit.\n"); FAULT(FAULT_SS,0) } } - memset(&desc, 0, sizeof(desc)); - desc.selector = newCS; - i386_load_protected_mode_segment(cpustate,&desc); - DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level - RPL = newCS & 0x03; - memset(&stack, 0, sizeof(stack)); - stack.selector = newSS; - i386_load_protected_mode_segment(cpustate,&stack); /* Check CS selector and descriptor */ if((newCS & ~0x07) == 0) { @@ -2251,6 +2384,11 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) } if(operand32 == 0) + REG16(SP) += 10; + else + REG32(ESP) += 20; + + if(operand32 == 0) { cpustate->eip = newEIP & 0xffff; cpustate->sreg[CS].selector = newCS; diff --git a/src/emu/cpu/i386/i386op16.c b/src/emu/cpu/i386/i386op16.c index 8b265e09761..7dbaa84e543 100644 --- a/src/emu/cpu/i386/i386op16.c +++ b/src/emu/cpu/i386/i386op16.c @@ -3288,7 +3288,7 @@ static void I386OP(lar_r16_rm16)(i386_state *cpustate) // Opcode 0x0f 0x02 if(seg.selector == 0) { SetZF(0); // not a valid segment - logerror("i386: LAR: Selector %04x is invalid type.\n",seg.selector); + // logerror("i386 (%08x): LAR: Selector %04x is invalid type.\n",cpustate->pc,seg.selector); } else { diff --git a/src/emu/cpu/i386/i386ops.c b/src/emu/cpu/i386/i386ops.c index d5c1f8b4fbe..915e509c14b 100644 --- a/src/emu/cpu/i386/i386ops.c +++ b/src/emu/cpu/i386/i386ops.c @@ -763,8 +763,13 @@ static void I386OP(mov_sreg_rm16)(i386_state *cpustate) // Opcode 0x8e } } - cpustate->sreg[s].selector = selector; - i386_load_segment_descriptor(cpustate, s ); + if(PROTECTED_MODE && !(V8086_MODE)) // no checks in virtual 8086 mode? + i386_protected_mode_sreg_load(cpustate,selector,s); + else + { + cpustate->sreg[s].selector = selector; + i386_load_segment_descriptor(cpustate, s ); + } } static void I386OP(mov_al_i8)(i386_state *cpustate) // Opcode 0xb0 @@ -905,25 +910,30 @@ static void I386OP(arpl)(i386_state *cpustate) // Opcode 0x63 UINT8 modrm = FETCH(cpustate); UINT8 flag = 0; - if( modrm >= 0xc0 ) { - src = LOAD_REG16(modrm); - dst = LOAD_RM16(modrm); - if( (dst&0x3) < (src&0x3) ) { - dst = (dst&0xfffc) | (src&0x3); - flag = 1; - STORE_RM16(modrm, dst); - } - } else { - UINT32 ea = GetEA(cpustate, modrm); - src = LOAD_REG16(modrm); - dst = READ16(cpustate, ea); - if( (dst&0x3) < (src&0x3) ) { - dst = (dst&0xfffc) | (src&0x3); - flag = 1; - WRITE16(cpustate, ea, dst); + if(PROTECTED_MODE) + { + if( modrm >= 0xc0 ) { + src = LOAD_REG16(modrm); + dst = LOAD_RM16(modrm); + if( (dst&0x3) < (src&0x3) ) { + dst = (dst&0xfffc) | (src&0x3); + flag = 1; + STORE_RM16(modrm, dst); + } + } else { + UINT32 ea = GetEA(cpustate, modrm); + src = LOAD_REG16(modrm); + dst = READ16(cpustate, ea); + if( (dst&0x3) < (src&0x3) ) { + dst = (dst&0xfffc) | (src&0x3); + flag = 1; + WRITE16(cpustate, ea, dst); + } } + SetZF(flag); } - SetZF(flag); + else + i386_trap(cpustate, 6, 0, 0); // invalid opcode in real mode or v8086 mode } static void I386OP(push_i8)(i386_state *cpustate) // Opcode 0x6a |