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.
This commit is contained in:
mahlemiut 2012-01-06 01:55:32 +00:00
parent 2bec585837
commit bdbe7ad11c
3 changed files with 209 additions and 61 deletions

View File

@ -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) static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level)
{ {
/* I386 Interrupts/Traps/Faults: /* 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. // for these exceptions, an error code is pushed onto the stack by the processor.
// no error code is pushed for software interrupts, either. // 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) 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) FAULT(FAULT_GP,0) // #GP(0)
} }
if(selector & 0x04) 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"); logerror("CALL: EIP is past segment limit.\n");
FAULT(FAULT_GP,0) // #GP(0) FAULT(FAULT_GP,0) // #GP(0)
} }
SetRPL = 1;
} }
break; break;
case 0x05: // task gate case 0x05: // task gate
@ -1682,17 +1803,24 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
I386_SREG desc; I386_SREG desc;
UINT8 CPL, RPL, DPL; UINT8 CPL, RPL, DPL;
if(operand32 == 0) if(operand32 == 0)
{ {
newEIP = POP16(cpustate) & 0xffff; newEIP = POP16(cpustate) & 0xffff;
newCS = POP16(cpustate) & 0xffff; newCS = POP16(cpustate) & 0xffff;
REG16(SP) += count; REG16(SP) += count;
newESP = POP16(cpustate) & 0xffff;
newSS = POP16(cpustate) & 0xffff;
REG16(SP) -= (8+count); // re-adjust stack pointer
} }
else else
{ {
newEIP = POP32(cpustate); newEIP = POP32(cpustate);
newCS = POP32(cpustate) & 0xffff; newCS = POP32(cpustate) & 0xffff;
REG32(ESP) += count; REG32(ESP) += count;
newESP = POP32(cpustate);
newSS = POP32(cpustate) & 0xffff;
REG32(ESP) -= (16+count); // re-adjust stack pointer
} }
memset(&desc, 0, sizeof(desc)); 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) 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) 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 */ /* same privilege level */
if((newCS & ~0x07) == 0) 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) FAULT(FAULT_GP,0)
} }
if(newCS & 0x04) 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)) 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) 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) FAULT(FAULT_SS,0)
} }
} }
if(operand32 == 0)
REG16(SP) += (4+count);
else
REG32(ESP) += (8+count);
} }
else if(RPL > CPL) else if(RPL > CPL)
{ {
/* outer privilege level */ /* outer privilege level */
if(operand32 == 0) if(operand32 == 0)
{ {
newESP = POP16(cpustate) & 0xffff; if(REG16(SP)+8+count > cpustate->sreg[SS].limit)
newSS = POP16(cpustate);
if(newESP+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) FAULT(FAULT_SS,0)
} }
} }
else else
{ {
newESP = POP32(cpustate); if(REG32(ESP)+16+count > cpustate->sreg[SS].limit)
newSS = POP32(cpustate);
if(newESP+16+count > cpustate->sreg[SS].limit)
{ {
logerror("RETF: ESP is past stack segment limit.\n"); logerror("RETF: ESP is past stack segment limit.\n");
FAULT(FAULT_SS,0) 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) 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) 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) 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) 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 ); i386_load_segment_descriptor(cpustate, SS );
if(operand32 == 0) if(operand32 == 0)
REG16(SP) += count; REG16(SP) += (8+count);
else else
REG32(ESP) += count; REG32(ESP) += (16+count);
/* Check that DS, ES, FS and GS are valid for the new privilege level */ /* Check that DS, ES, FS and GS are valid for the new privilege level */
i386_check_sreg_validity(cpustate,DS); i386_check_sreg_validity(cpustate,DS);
@ -1945,15 +2074,21 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
CPL = cpustate->CPL; CPL = cpustate->CPL;
if(operand32 == 0) if(operand32 == 0)
{ {
newEIP = POP16(cpustate); newEIP = POP16(cpustate) & 0xffff;
newCS = POP16(cpustate); newCS = POP16(cpustate) & 0xffff;
newflags = POP16(cpustate); newflags = POP16(cpustate) & 0xffff;
newESP = POP16(cpustate) & 0xffff;
newSS = POP16(cpustate) & 0xffff;
REG16(SP) -= 10;
} }
else else
{ {
newEIP = POP32(cpustate); newEIP = POP32(cpustate);
newCS = POP32(cpustate) & 0xffff; newCS = POP32(cpustate) & 0xffff;
newflags = POP32(cpustate); newflags = POP32(cpustate);
newESP = POP32(cpustate);
newSS = POP32(cpustate) & 0xffff;
REG32(ESP) -= 20;
} }
if(V8086_MODE) 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 if(newflags & 0x00020000) // if returning to virtual 8086 mode
{ {
/* Return to v86 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 else
{ {
@ -2024,7 +2159,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
RPL = newCS & 0x03; RPL = newCS & 0x03;
if(RPL < CPL) 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) FAULT(FAULT_GP,newCS & ~0x07)
} }
if(RPL == CPL) if(RPL == CPL)
@ -2074,7 +2209,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
RPL = newCS & 0x03; RPL = newCS & 0x03;
if((desc.flags & 0x0018) != 0x0018) 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) FAULT(FAULT_GP,newCS & ~0x07)
} }
if(desc.flags & 0x0004) if(desc.flags & 0x0004)
@ -2108,37 +2243,20 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
cpustate->eip = newEIP; cpustate->eip = newEIP;
cpustate->sreg[CS].selector = newCS; cpustate->sreg[CS].selector = newCS;
set_flags(cpustate,newflags); set_flags(cpustate,newflags);
REG16(SP) += 6;
} }
else else
{ {
cpustate->eip = newEIP; cpustate->eip = newEIP;
cpustate->sreg[CS].selector = newCS & 0xffff; cpustate->sreg[CS].selector = newCS & 0xffff;
set_flags(cpustate,newflags); set_flags(cpustate,newflags);
REG32(ESP) += 12;
} }
} }
else if(RPL > CPL) else if(RPL > CPL)
{ {
I386_SREG stack; I386_SREG stack;
/* return to outer privilege level */ /* return to outer privilege level */
if(operand32 == 0)
{
newESP = POP16(cpustate);
newSS = POP16(cpustate);
if(REG16(SP) > cpustate->sreg[SS].limit)
{
logerror("IRET: ESP is past SS limit.\n");
FAULT(FAULT_SS,0) }
}
else
{
newESP = POP32(cpustate);
newSS = POP32(cpustate) & 0xffff;
if(REG32(ESP) > cpustate->sreg[SS].limit)
{
logerror("IRET: ESP is past SS limit.\n");
FAULT(FAULT_SS,0)
}
}
memset(&desc, 0, sizeof(desc)); memset(&desc, 0, sizeof(desc));
desc.selector = newCS; desc.selector = newCS;
i386_load_protected_mode_segment(cpustate,&desc); i386_load_protected_mode_segment(cpustate,&desc);
@ -2147,6 +2265,21 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
memset(&stack, 0, sizeof(stack)); memset(&stack, 0, sizeof(stack));
stack.selector = newSS; stack.selector = newSS;
i386_load_protected_mode_segment(cpustate,&stack); i386_load_protected_mode_segment(cpustate,&stack);
if(operand32 == 0)
{
if(REG16(SP)+10 > cpustate->sreg[SS].limit)
{
logerror("IRET: SP is past SS limit.\n");
FAULT(FAULT_SS,0) }
}
else
{
if(REG32(ESP)+20 > cpustate->sreg[SS].limit)
{
logerror("IRET: ESP is past SS limit.\n");
FAULT(FAULT_SS,0)
}
}
/* Check CS selector and descriptor */ /* Check CS selector and descriptor */
if((newCS & ~0x07) == 0) if((newCS & ~0x07) == 0)
{ {
@ -2250,6 +2383,11 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
FAULT(FAULT_GP,0) FAULT(FAULT_GP,0)
} }
if(operand32 == 0)
REG16(SP) += 10;
else
REG32(ESP) += 20;
if(operand32 == 0) if(operand32 == 0)
{ {
cpustate->eip = newEIP & 0xffff; cpustate->eip = newEIP & 0xffff;

View File

@ -3288,7 +3288,7 @@ static void I386OP(lar_r16_rm16)(i386_state *cpustate) // Opcode 0x0f 0x02
if(seg.selector == 0) if(seg.selector == 0)
{ {
SetZF(0); // not a valid segment 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 else
{ {

View File

@ -763,8 +763,13 @@ static void I386OP(mov_sreg_rm16)(i386_state *cpustate) // Opcode 0x8e
} }
} }
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; cpustate->sreg[s].selector = selector;
i386_load_segment_descriptor(cpustate, s ); i386_load_segment_descriptor(cpustate, s );
}
} }
static void I386OP(mov_al_i8)(i386_state *cpustate) // Opcode 0xb0 static void I386OP(mov_al_i8)(i386_state *cpustate) // Opcode 0xb0
@ -905,6 +910,8 @@ static void I386OP(arpl)(i386_state *cpustate) // Opcode 0x63
UINT8 modrm = FETCH(cpustate); UINT8 modrm = FETCH(cpustate);
UINT8 flag = 0; UINT8 flag = 0;
if(PROTECTED_MODE)
{
if( modrm >= 0xc0 ) { if( modrm >= 0xc0 ) {
src = LOAD_REG16(modrm); src = LOAD_REG16(modrm);
dst = LOAD_RM16(modrm); dst = LOAD_RM16(modrm);
@ -924,6 +931,9 @@ static void I386OP(arpl)(i386_state *cpustate) // Opcode 0x63
} }
} }
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 static void I386OP(push_i8)(i386_state *cpustate) // Opcode 0x6a