i386: Big pmode update. [Carl]

This commit is contained in:
mahlemiut 2012-05-03 05:58:32 +00:00
parent 51c9a46f66
commit 605a48921b
2 changed files with 152 additions and 118 deletions

View File

@ -53,9 +53,14 @@ static UINT32 i386_load_protected_mode_segment(i386_state *cpustate, I386_SREG *
if (limit == 0 || entry + 7 > limit)
return 0;
int cpl = cpustate->CPL;
cpustate->CPL = 0;
v1 = READ32(cpustate, base + entry );
v2 = READ32(cpustate, base + entry + 4 );
cpustate->CPL = cpl;
seg->flags = (v2 >> 8) & 0xf0ff;
seg->base = (v2 & 0xff000000) | ((v2 & 0xff) << 16) | ((v1 >> 16) & 0xffff);
seg->limit = (v2 & 0xf0000) | (v1 & 0xffff);
@ -88,9 +93,14 @@ static void i386_load_call_gate(i386_state* cpustate, I386_CALL_GATE *gate)
if (limit == 0 || entry + 7 > limit)
return;
int cpl = cpustate->CPL;
cpustate->CPL = 0;
v1 = READ32(cpustate, base + entry );
v2 = READ32(cpustate, base + entry + 4 );
cpustate->CPL = cpl;
/* Note that for task gates, offset and dword_count are not used */
gate->selector = (v1 >> 16) & 0xffff;
gate->offset = (v1 & 0x0000ffff) | (v2 & 0xffff0000);
@ -129,35 +139,39 @@ static void i386_load_segment_descriptor(i386_state *cpustate, int segment )
/* Retrieves the stack selector located in the current TSS */
static UINT32 i386_get_stack_segment(i386_state* cpustate, UINT8 privilege)
{
UINT32 ret;
if(privilege >= 3)
return 0;
return READ32(cpustate,(cpustate->task.base+8) + (8*privilege));
}
int cpl = cpustate->CPL;
cpustate->CPL = 0;
static UINT16 i286_get_stack_segment(i386_state* cpustate, UINT8 privilege)
{
if(privilege >= 3)
return 0;
if(cpustate->task.flags & 8)
ret = READ32(cpustate,(cpustate->task.base+8) + (8*privilege));
else
ret = READ16(cpustate,(cpustate->task.base+4) + (4*privilege));
return READ16(cpustate,(cpustate->task.base+4) + (4*privilege));
cpustate->CPL = cpl;
return ret;
}
/* Retrieves the stack pointer located in the current TSS */
static UINT32 i386_get_stack_ptr(i386_state* cpustate, UINT8 privilege)
{
UINT32 ret;
if(privilege >= 3)
return 0;
return READ32(cpustate,(cpustate->task.base+4) + (8*privilege));
}
int cpl = cpustate->CPL;
cpustate->CPL = 0;
static UINT16 i286_get_stack_ptr(i386_state* cpustate, UINT8 privilege)
{
if(privilege >= 3)
return 0;
if(cpustate->task.flags & 8)
ret = READ32(cpustate,(cpustate->task.base+4) + (8*privilege));
else
ret = READ16(cpustate,(cpustate->task.base+2) + (4*privilege));
return READ16(cpustate,(cpustate->task.base+2) + (4*privilege));
cpustate->CPL = cpl;
return ret;
}
static UINT32 get_flags(i386_state *cpustate)
@ -596,8 +610,10 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
UINT8 CPL = cpustate->CPL, DPL = 0; //, RPL = 0;
/* 32-bit */
cpustate->CPL = 0;
v1 = READ32(cpustate, cpustate->idtr.base + entry );
v2 = READ32(cpustate, cpustate->idtr.base + entry + 4 );
cpustate->CPL = CPL;
offset = (v2 & 0xffff0000) | (v1 & 0xffff);
segment = (v1 >> 16) & 0xffff;
type = (v2>>8) & 0x1F;
@ -661,6 +677,7 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
/* Get privilege level 0 stack pointer from TSS */
cpustate->sreg[SS].selector = i386_get_stack_segment(cpustate,0);
REG32(ESP) = i386_get_stack_ptr(cpustate,0);
cpustate->CPL = segment & 0x03;
i386_load_segment_descriptor(cpustate,SS);
PUSH32(cpustate,cpustate->sreg[GS].selector & 0xffff);
PUSH32(cpustate,cpustate->sreg[FS].selector & 0xffff);
@ -686,7 +703,6 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
cpustate->sreg[CS].selector = segment;
cpustate->eip = offset;
// CPL set to CS RPL?
cpustate->CPL = segment & 0x03;
i386_load_segment_descriptor(cpustate,CS);
CHANGE_PC(cpustate,cpustate->eip);
@ -702,25 +718,25 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
if(segment & 0x04)
{
logerror("IRQ: Task gate: TSS is not in the GDT.\n");
FAULT_EXP(FAULT_TS,segment & ~0x07);
FAULT_EXP(FAULT_TS,segment & ~0x03);
}
else
{
if(segment > cpustate->gdtr.limit)
{
logerror("IRQ: Task gate: TSS is past GDT limit.\n");
FAULT_EXP(FAULT_TS,segment & ~0x07);
FAULT_EXP(FAULT_TS,segment & ~0x03);
}
}
if((desc.flags & 0x000f) != 0x09 && (desc.flags & 0x000f) != 0x01)
{
logerror("IRQ: Task gate: TSS is not an available TSS.\n");
FAULT_EXP(FAULT_TS,segment & ~0x07);
FAULT_EXP(FAULT_TS,segment & ~0x03);
}
if((desc.flags & 0x0080) == 0)
{
logerror("IRQ: Task gate: TSS is not present.\n");
FAULT_EXP(FAULT_NP,segment & ~0x07);
FAULT_EXP(FAULT_NP,segment & ~0x03);
}
if(!(irq == 3 || irq == 4 || irq == 9 || irq_gate == 1))
cpustate->eip = cpustate->prev_eip;
@ -750,7 +766,7 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
if((segment & ~0x07) > cpustate->ldtr.limit)
{
logerror("IRQ: Gate segment is past LDT limit.\n");
FAULT_EXP(FAULT_GP,(segment & 0x07)+cpustate->ext)
FAULT_EXP(FAULT_GP,(segment & 0x03)+cpustate->ext)
}
}
else
@ -758,18 +774,18 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
if((segment & ~0x07) > cpustate->gdtr.limit)
{
logerror("IRQ: Gate segment is past GDT limit.\n");
FAULT_EXP(FAULT_GP,(segment & 0x07)+cpustate->ext)
FAULT_EXP(FAULT_GP,(segment & 0x03)+cpustate->ext)
}
}
if((desc.flags & 0x0018) != 0x18)
{
logerror("IRQ: Gate descriptor is not a code segment.\n");
FAULT_EXP(FAULT_GP,(segment & 0x07)+cpustate->ext)
FAULT_EXP(FAULT_GP,(segment & 0x03)+cpustate->ext)
}
if((desc.flags & 0x0080) == 0)
{
logerror("IRQ: Gate segment is not present.\n");
FAULT_EXP(FAULT_NP,(segment & 0x07)+cpustate->ext)
FAULT_EXP(FAULT_NP,(segment & 0x03)+cpustate->ext)
}
if((desc.flags & 0x0004) == 0 && (DPL < CPL))
{
@ -779,17 +795,14 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
/* Check new stack segment in TSS */
memset(&stack, 0, sizeof(stack));
if(flags & 0x0008)
stack.selector = i386_get_stack_segment(cpustate,DPL);
else
stack.selector = i286_get_stack_segment(cpustate,DPL);
stack.selector = i386_get_stack_segment(cpustate,DPL);
i386_load_protected_mode_segment(cpustate,&stack,NULL);
oldSS = cpustate->sreg[SS].selector;
if(flags & 0x0008)
oldESP = REG32(ESP);
else
oldESP = REG16(SP);
if((stack.selector & ~0x07) == 0)
if((stack.selector & ~0x03) == 0)
{
logerror("IRQ: New stack selector is null.\n");
FAULT_EXP(FAULT_GP,cpustate->ext)
@ -799,7 +812,7 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
if((stack.selector & ~0x07) > cpustate->ldtr.base)
{
logerror("IRQ: New stack selector is past LDT limit.\n");
FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext)
FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext)
}
}
else
@ -807,32 +820,32 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
if((stack.selector & ~0x07) > cpustate->gdtr.base)
{
logerror("IRQ: New stack selector is past GDT limit.\n");
FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext)
FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext)
}
}
if((stack.selector & 0x03) != DPL)
{
logerror("IRQ: New stack selector RPL is not equal to code segment DPL.\n");
FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext)
FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext)
}
if(((stack.flags >> 5) & 0x03) != DPL)
{
logerror("IRQ: New stack segment DPL is not equal to code segment DPL.\n");
FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext)
FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext)
}
if(((stack.flags & 0x0018) != 0x10) && (stack.flags & 0x0002) != 0)
{
logerror("IRQ: New stack segment is not a writable data segment.\n");
FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) // #TS(stack selector + EXT)
FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext) // #TS(stack selector + EXT)
}
if((stack.flags & 0x0080) == 0)
{
logerror("IRQ: New stack segment is not present.\n");
FAULT_EXP(FAULT_SS,(stack.selector & ~0x07)+cpustate->ext) // #TS(stack selector + EXT)
FAULT_EXP(FAULT_SS,(stack.selector & ~0x03)+cpustate->ext) // #TS(stack selector + EXT)
}
newESP = i386_get_stack_ptr(cpustate,DPL);
if(type & 0x08) // 32-bit gate
{
newESP = i386_get_stack_ptr(cpustate,DPL);
if(newESP < 20)
{
logerror("IRQ: New stack has no space for return addresses.\n");
@ -841,7 +854,7 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
}
else // 16-bit gate
{
newESP = i286_get_stack_ptr(cpustate,DPL);
newESP &= 0xffff;
if(newESP < 10)
{
logerror("IRQ: New stack has no space for return addresses.\n");
@ -853,13 +866,17 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
logerror("IRQ: New EIP is past code segment limit.\n");
FAULT_EXP(FAULT_GP,0)
}
/* change CPL before accessing the stack */
cpustate->CPL = DPL;
/* check for page fault at new stack TODO: check if stack frame crosses page boundary */
WRITE_TEST(cpustate, stack.base+newESP-1);
/* Load new stack segment descriptor */
cpustate->sreg[SS].selector = stack.selector;
i386_load_segment_descriptor(cpustate,SS);
if(flags & 0x0008)
REG32(ESP) = i386_get_stack_ptr(cpustate,DPL);
else
REG16(SP) = i286_get_stack_ptr(cpustate,DPL);
REG16(SP) = i386_get_stack_ptr(cpustate,DPL);
if(type & 0x08)
{
// 32-bit gate
@ -872,7 +889,6 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
PUSH16(cpustate,oldSS);
PUSH16(cpustate,oldESP);
}
cpustate->CPL = DPL;
SetRPL = 1;
}
else
@ -951,6 +967,8 @@ static void i386_trap_with_error(i386_state *cpustate,int irq, int irq_gate, int
{
UINT32 entry = irq * 8;
UINT32 v2,type;
int cpl = cpustate->CPL;
cpustate->CPL = 0;
v2 = READ32(cpustate, cpustate->idtr.base + entry + 4 );
type = (v2>>8) & 0x1F;
if(type == 5)
@ -959,6 +977,7 @@ static void i386_trap_with_error(i386_state *cpustate,int irq, int irq_gate, int
v2 = READ32(cpustate, cpustate->gdtr.base + ((v2 >> 16) & 0xfff8) + 4);
type = (v2>>8) & 0x1F;
}
cpustate->CPL = cpl;
if(type >= 9)
PUSH32(cpustate,error);
else
@ -1136,6 +1155,7 @@ static void i386_task_switch(i386_state *cpustate, UINT16 selector, UINT8 nested
cpustate->cr[0] |= 0x08;
/* Load incoming task state from the new task's TSS */
cpustate->CPL = 0;
tss = cpustate->task.base;
cpustate->ldtr.segment = READ32(cpustate,tss+0x60) & 0xffff;
seg.selector = cpustate->ldtr.segment;
@ -1351,19 +1371,17 @@ static void i386_protected_mode_jump(i386_state *cpustate, UINT16 seg, UINT32 of
}
if(call_gate.selector & 0x04)
{
/* check GDT limit */
if((call_gate.selector & ~0x07) > cpustate->gdtr.limit)
if((call_gate.selector & ~0x07) > cpustate->ldtr.limit)
{
logerror("JMP: Call Gate: Gate Selector is past GDT segment limit\n");
logerror("JMP: Call Gate: Gate Selector is past LDT segment limit\n");
FAULT(FAULT_GP,call_gate.selector & 0xfffc)
}
}
else
{
/* check LDT limit */
if((call_gate.selector & ~0x07) > cpustate->ldtr.limit)
if((call_gate.selector & ~0x07) > cpustate->gdtr.limit)
{
logerror("JMP: Call Gate: Gate Selector is past LDT segment limit\n");
logerror("JMP: Call Gate: Gate Selector is past GDT segment limit\n");
FAULT(FAULT_GP,call_gate.selector & 0xfffc)
}
}
@ -1638,7 +1656,7 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
FAULT(FAULT_NP,desc.selector & ~0x03) // #GP(selector)
}
desc.selector = gate.selector;
if((gate.selector & ~0x07) == 0)
if((gate.selector & ~0x03) == 0)
{
logerror("CALL: Call gate: Segment is null.\n");
FAULT(FAULT_GP,0) // #GP(0)
@ -1684,12 +1702,9 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
/* more privilege */
/* Check new SS segment for privilege level from TSS */
memset(&stack, 0, sizeof(stack));
if(operand32 != 0)
stack.selector = i386_get_stack_segment(cpustate,DPL);
else
stack.selector = i286_get_stack_segment(cpustate,DPL);
stack.selector = i386_get_stack_segment(cpustate,DPL);
i386_load_protected_mode_segment(cpustate,&stack,NULL);
if((stack.selector & ~0x07) == 0)
if((stack.selector & ~0x03) == 0)
{
logerror("CALL: Call gate: TSS selector is null\n");
FAULT(FAULT_TS,0) // #TS(0)
@ -1730,9 +1745,9 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
logerror("CALL: Call gate: Stack segment is not present\n");
FAULT(FAULT_SS,stack.selector) // #SS(SS selector)
}
UINT32 newESP = i386_get_stack_ptr(cpustate,DPL);
if(operand32 != 0)
{
UINT32 newESP = i386_get_stack_ptr(cpustate,DPL);
if(newESP < ((gate.dword_count & 0x1f) + 16))
{
logerror("CALL: Call gate: New stack has no room for 32-bit return address and parameters.\n");
@ -1746,7 +1761,7 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
}
else
{
UINT32 newESP = i286_get_stack_ptr(cpustate,DPL) & 0x0000ffff;
newESP &= 0x0000ffff;
if(newESP < ((gate.dword_count & 0x1f) + 8))
{
logerror("CALL: Call gate: New stack has no room for 16-bit return address and parameters.\n");
@ -1761,23 +1776,25 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
selector = gate.selector;
offset = gate.offset;
cpustate->CPL = (stack.flags >> 5) & 0x03;
/* check for page fault at new stack TODO: check if stack frame crosses page boundary */
WRITE_TEST(cpustate, stack.base+newESP-1);
/* switch to new stack */
oldSS = cpustate->sreg[SS].selector;
cpustate->sreg[SS].selector = i386_get_stack_segment(cpustate,gate.selector & 0x03);
if(operand32 != 0)
{
oldESP = REG32(ESP);
cpustate->sreg[SS].selector = i386_get_stack_segment(cpustate,gate.selector & 0x03);
}
else
{
oldESP = REG16(SP);
cpustate->sreg[SS].selector = i286_get_stack_segment(cpustate,gate.selector & 0x03);
}
i386_load_segment_descriptor(cpustate, SS );
if(operand32 != 0)
REG32(ESP) = i386_get_stack_ptr(cpustate,gate.selector & 0x03);
else
REG16(SP) = i286_get_stack_ptr(cpustate,gate.selector & 0x03) & 0x0000ffff;
REG16(SP) = i386_get_stack_ptr(cpustate,gate.selector & 0x03) & 0x0000ffff;
if(operand32 != 0)
{
@ -1796,10 +1813,13 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
/* copy parameters from old stack to new stack */
for(x=(gate.dword_count & 0x1f)-1;x>=0;x--)
{
UINT32 addr = temp.base + oldESP + (x*2);
PUSH16(cpustate,READ16(cpustate,addr));
UINT32 addr = oldESP + (operand32?(x*4):(x*2));
addr = temp.base + (temp.d?addr:(addr&0xffff));
if(operand32)
PUSH32(cpustate,READ32(cpustate,addr));
else
PUSH16(cpustate,READ16(cpustate,addr));
}
cpustate->CPL = (stack.flags >> 5) & 0x03;
SetRPL = 1;
}
else
@ -1842,17 +1862,17 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
if(DPL < CPL)
{
logerror("CALL: Task Gate: Gate DPL is less than CPL.\n");
FAULT(FAULT_TS,selector & ~0x07) // #TS(selector)
FAULT(FAULT_TS,selector & ~0x03) // #TS(selector)
}
if(DPL < RPL)
{
logerror("CALL: Task Gate: Gate DPL is less than RPL.\n");
FAULT(FAULT_TS,selector & ~0x07) // #TS(selector)
FAULT(FAULT_TS,selector & ~0x03) // #TS(selector)
}
if(gate.ar & 0x0080)
{
logerror("CALL: Task Gate: Gate is not present.\n");
FAULT(FAULT_NP,selector & ~0x07) // #NP(selector)
FAULT(FAULT_NP,selector & ~0x03) // #NP(selector)
}
/* Check the TSS that the task gate points to */
desc.selector = gate.selector;
@ -1860,25 +1880,25 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
if(gate.selector & 0x04)
{
logerror("CALL: Task Gate: TSS is not global.\n");
FAULT(FAULT_TS,gate.selector & ~0x07) // #TS(selector)
FAULT(FAULT_TS,gate.selector & ~0x03) // #TS(selector)
}
else
{
if((gate.selector & ~0x07) > cpustate->gdtr.limit)
{
logerror("CALL: Task Gate: TSS is past GDT limit.\n");
FAULT(FAULT_TS,gate.selector & ~0x07) // #TS(selector)
FAULT(FAULT_TS,gate.selector & ~0x03) // #TS(selector)
}
}
if(desc.flags & 0x0002)
{
logerror("CALL: Task Gate: TSS is busy.\n");
FAULT(FAULT_TS,gate.selector & ~0x07) // #TS(selector)
FAULT(FAULT_TS,gate.selector & ~0x03) // #TS(selector)
}
if(desc.flags & 0x0080)
{
logerror("CALL: Task Gate: TSS is not present.\n");
FAULT(FAULT_NP,gate.selector & ~0x07) // #TS(selector)
FAULT(FAULT_NP,gate.selector & ~0x03) // #TS(selector)
}
if(desc.flags & 0x08)
i386_task_switch(cpustate,desc.selector,1); // with nesting
@ -1981,17 +2001,17 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
}
if(desc.flags & 0x0004)
{
if(DPL > CPL)
if(DPL > RPL)
{
logerror("RETF: Conforming code segment DPL is greater than CPL.\n");
logerror("RETF: Conforming code segment DPL is greater than CS RPL.\n");
FAULT(FAULT_GP,newCS & ~0x03)
}
}
else
{
if(DPL != CPL)
if(DPL != RPL)
{
logerror("RETF: Non-conforming code segment DPL does not equal CPL.\n");
logerror("RETF: Non-conforming code segment DPL does not equal CS RPL.\n");
FAULT(FAULT_GP,newCS & ~0x03)
}
}
@ -2061,7 +2081,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
if((newCS & ~0x07) >= cpustate->ldtr.limit)
{
logerror("RETF: CS segment selector is past LDT limit.\n");
FAULT(FAULT_GP,newCS & ~0x07)
FAULT(FAULT_GP,newCS & ~0x03)
}
}
else
@ -2069,20 +2089,20 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
if((newCS & ~0x07) >= cpustate->gdtr.limit)
{
logerror("RETF: CS segment selector is past GDT limit.\n");
FAULT(FAULT_GP,newCS & ~0x07)
FAULT(FAULT_GP,newCS & ~0x03)
}
}
if((desc.flags & 0x0018) != 0x0018)
{
logerror("RETF: CS segment is not a code segment.\n");
FAULT(FAULT_GP,newCS & ~0x07)
FAULT(FAULT_GP,newCS & ~0x03)
}
if(desc.flags & 0x0004)
{
if(DPL > RPL)
{
logerror("RETF: Conforming CS segment DPL is greater than return selector RPL.\n");
FAULT(FAULT_GP,newCS & ~0x07)
FAULT(FAULT_GP,newCS & ~0x03)
}
}
else
@ -2090,13 +2110,13 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
if(DPL != RPL)
{
logerror("RETF: Non-conforming CS segment DPL is not equal to return selector RPL.\n");
FAULT(FAULT_GP,newCS & ~0x07)
FAULT(FAULT_GP,newCS & ~0x03)
}
}
if((desc.flags & 0x0080) == 0)
{
logerror("RETF: CS segment is not present.\n");
FAULT(FAULT_NP,newCS & ~0x07)
FAULT(FAULT_NP,newCS & ~0x03)
}
if(newEIP > desc.limit)
{
@ -2131,7 +2151,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
if((newSS & ~0x07) > cpustate->ldtr.limit)
{
logerror("RETF (%08x): SS segment selector is past LDT limit.\n",cpustate->pc);
FAULT(FAULT_GP,newSS & ~0x07)
FAULT(FAULT_GP,newSS & ~0x03)
}
}
else
@ -2139,28 +2159,28 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
if((newSS & ~0x07) > cpustate->gdtr.limit)
{
logerror("RETF (%08x): SS segment selector is past GDT limit.\n",cpustate->pc);
FAULT(FAULT_GP,newSS & ~0x07)
FAULT(FAULT_GP,newSS & ~0x03)
}
}
if((newSS & 0x03) != RPL)
{
logerror("RETF: SS segment RPL is not equal to CS segment RPL.\n");
FAULT(FAULT_GP,newSS & ~0x07)
FAULT(FAULT_GP,newSS & ~0x03)
}
if((desc.flags & 0x0018) != 0x0010 || (desc.flags & 0x0002) == 0)
{
logerror("RETF: SS segment is not a writable data segment.\n");
FAULT(FAULT_GP,newSS & ~0x07)
FAULT(FAULT_GP,newSS & ~0x03)
}
if(((desc.flags >> 5) & 0x03) != RPL)
{
logerror("RETF: SS DPL is not equal to CS segment RPL.\n");
FAULT(FAULT_GP,newSS & ~0x07)
FAULT(FAULT_GP,newSS & ~0x03)
}
if((desc.flags & 0x0080) == 0)
{
logerror("RETF: SS segment is not present.\n");
FAULT(FAULT_GP,newSS & ~0x07)
FAULT(FAULT_GP,newSS & ~0x03)
}
cpustate->CPL = newCS & 0x03;
@ -2249,12 +2269,12 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if(task & 0x04)
{
logerror("IRET: Task return: Back-linked TSS is not in GDT.\n");
FAULT(FAULT_TS,task & ~0x07)
FAULT(FAULT_TS,task & ~0x03)
}
if((task & ~0x07) >= cpustate->gdtr.limit)
{
logerror("IRET: Task return: Back-linked TSS is not in GDT.\n");
FAULT(FAULT_TS,task & ~0x07)
FAULT(FAULT_TS,task & ~0x03)
}
memset(&desc, 0, sizeof(desc));
desc.selector = task;
@ -2262,12 +2282,12 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((desc.flags & 0x001f) != 0x000b)
{
logerror("IRET (%08x): Task return: Back-linked TSS is not a busy TSS.\n",cpustate->pc);
FAULT(FAULT_TS,task & ~0x07)
FAULT(FAULT_TS,task & ~0x03)
}
if((desc.flags & 0x0080) == 0)
{
logerror("IRET: Task return: Back-linked TSS is not present.\n");
FAULT(FAULT_NP,task & ~0x07)
FAULT(FAULT_NP,task & ~0x03)
}
if(desc.flags & 0x08)
i386_task_switch(cpustate,desc.selector,0);
@ -2462,7 +2482,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if(RPL < CPL)
{
logerror("IRET (%08x): Return CS RPL is less than CPL.\n",cpustate->pc);
FAULT(FAULT_GP,newCS & ~0x07)
FAULT(FAULT_GP,newCS & ~0x03)
}
if(RPL == CPL)
{
@ -2495,7 +2515,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((newCS & ~0x07) >= cpustate->ldtr.limit)
{
logerror("IRET: Return CS selector (%04x) is past LDT limit.\n",newCS);
FAULT(FAULT_GP,newCS & ~0x07)
FAULT(FAULT_GP,newCS & ~0x03)
}
}
else
@ -2503,7 +2523,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((newCS & ~0x07) >= cpustate->gdtr.limit)
{
logerror("IRET: Return CS selector is past GDT limit.\n");
FAULT(FAULT_GP,newCS & ~0x07)
FAULT(FAULT_GP,newCS & ~0x03)
}
}
memset(&desc, 0, sizeof(desc));
@ -2518,24 +2538,24 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
}
if(desc.flags & 0x0004)
{
if(DPL > CPL)
if(DPL > RPL)
{
logerror("IRET: Conforming return CS DPL is greater than CPL.\n");
FAULT(FAULT_GP,newCS & ~0x07)
logerror("IRET: Conforming return CS DPL is greater than CS RPL.\n");
FAULT(FAULT_GP,newCS & ~0x03)
}
}
else
{
if(DPL != CPL)
if(DPL != RPL)
{
logerror("IRET: Non-conforming return CS DPL is not equal to CPL.\n");
FAULT(FAULT_GP,newCS & ~0x07)
logerror("IRET: Non-conforming return CS DPL is not equal to CS RPL.\n");
FAULT(FAULT_GP,newCS & ~0x03)
}
}
if((desc.flags & 0x0080) == 0)
{
logerror("IRET: Return CS segment is not present.\n");
FAULT(FAULT_NP,newCS & ~0x07)
FAULT(FAULT_NP,newCS & ~0x03)
}
if(newEIP > desc.limit)
{
@ -2601,7 +2621,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((newCS & ~0x07) >= cpustate->ldtr.limit)
{
logerror("IRET: Return CS selector is past LDT limit.\n");
FAULT(FAULT_GP,newCS & ~0x07);
FAULT(FAULT_GP,newCS & ~0x03);
}
}
else
@ -2609,20 +2629,20 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((newCS & ~0x07) >= cpustate->gdtr.limit)
{
logerror("IRET: Return CS selector is past GDT limit.\n");
FAULT(FAULT_GP,newCS & ~0x07);
FAULT(FAULT_GP,newCS & ~0x03);
}
}
if((desc.flags & 0x0018) != 0x0018)
{
logerror("IRET: Return CS segment is not a code segment.\n");
FAULT(FAULT_GP,newCS & ~0x07)
FAULT(FAULT_GP,newCS & ~0x03)
}
if(desc.flags & 0x0004)
{
if(DPL <= CPL)
if(DPL > RPL)
{
logerror("IRET: Conforming return CS DPL is not greater than CPL.\n");
FAULT(FAULT_GP,newCS & ~0x07)
logerror("IRET: Conforming return CS DPL is greater than CS RPL.\n");
FAULT(FAULT_GP,newCS & ~0x03)
}
}
else
@ -2630,13 +2650,13 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if(DPL != RPL)
{
logerror("IRET: Non-conforming return CS DPL does not equal CS RPL.\n");
FAULT(FAULT_GP,newCS & ~0x07)
FAULT(FAULT_GP,newCS & ~0x03)
}
}
if((desc.flags & 0x0080) == 0)
{
logerror("IRET: Return CS segment is not present.\n");
FAULT(FAULT_NP,newCS & ~0x07)
FAULT(FAULT_NP,newCS & ~0x03)
}
/* Check SS selector and descriptor */
@ -2664,7 +2684,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((newSS & ~0x07) >= cpustate->ldtr.limit)
{
logerror("IRET: Return SS selector is past LDT limit.\n");
FAULT(FAULT_GP,newSS & ~0x07);
FAULT(FAULT_GP,newSS & ~0x03);
}
}
else
@ -2672,33 +2692,33 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((newSS & ~0x07) >= cpustate->gdtr.limit)
{
logerror("IRET: Return SS selector is past GDT limit.\n");
FAULT(FAULT_GP,newSS & ~0x07);
FAULT(FAULT_GP,newSS & ~0x03);
}
}
if((newSS & 0x03) != RPL)
{
logerror("IRET: Return SS RPL is not equal to return CS RPL.\n");
FAULT(FAULT_GP,newSS & ~0x07)
FAULT(FAULT_GP,newSS & ~0x03)
}
if((stack.flags & 0x0018) != 0x0010)
{
logerror("IRET: Return SS segment is not a data segment.\n");
FAULT(FAULT_GP,newSS & ~0x07)
FAULT(FAULT_GP,newSS & ~0x03)
}
if((stack.flags & 0x0002) == 0)
{
logerror("IRET: Return SS segment is not writable.\n");
FAULT(FAULT_GP,newSS & ~0x07)
FAULT(FAULT_GP,newSS & ~0x03)
}
if(DPL != RPL)
{
logerror("IRET: Return SS DPL does not equal SS RPL.\n");
FAULT(FAULT_GP,newSS & ~0x07)
FAULT(FAULT_GP,newSS & ~0x03)
}
if((stack.flags & 0x0080) == 0)
{
logerror("IRET: Return SS segment is not present.\n");
FAULT(FAULT_NP,newSS & ~0x07)
FAULT(FAULT_NP,newSS & ~0x03)
}
if(newEIP > desc.limit)
{
@ -3127,7 +3147,7 @@ static CPU_EXECUTE( i386 )
cpustate->address_prefix = 0;
cpustate->ext = 1;
cpustate->old_tf = cpustate->TF;
int old_tf = cpustate->TF;
cpustate->segment_prefix = 0;
cpustate->prev_eip = cpustate->eip;
@ -3142,7 +3162,7 @@ static CPU_EXECUTE( i386 )
try
{
I386OP(decode_opcode)(cpustate);
if(cpustate->TF && cpustate->old_tf)
if(cpustate->TF && old_tf)
{
cpustate->prev_eip = cpustate->eip;
cpustate->ext = 1;

View File

@ -240,7 +240,6 @@ struct _i386_state
UINT8 performed_intersegment_jump;
UINT8 delayed_interrupt_enable;
UINT8 old_tf;
UINT32 cr[5]; // Control registers
UINT32 dr[8]; // Debug registers
@ -419,18 +418,19 @@ INLINE int translate_address(i386_state *cpustate, int rwn, UINT32 *address, UIN
UINT32 offset = a & 0xfff;
UINT32 page_entry;
UINT32 ret = 1;
bool user = (cpustate->CPL == 3);
*error = 0;
// TODO: cr0 wp bit, 486 and higher
UINT32 page_dir = cpustate->program->read_dword(pdbr + directory * 4);
if(page_dir & 1)
if((page_dir & 1) && ((page_dir & 4) || !user))
{
if (!(cpustate->cr[4] & 0x10))
{
page_entry = cpustate->program->read_dword((page_dir & 0xfffff000) + (table * 4));
if(!(page_entry & 1))
ret = 0;
else if(!(page_entry & 2) && cpustate->CPL && (rwn == 1))
else if((!(page_entry & 2) && user && (rwn == 1)) || (!(page_entry & 4) && user))
{
*error = 1;
ret = 0;
@ -450,7 +450,7 @@ INLINE int translate_address(i386_state *cpustate, int rwn, UINT32 *address, UIN
{
if (page_dir & 0x80)
{
if(!(page_dir & 2) && cpustate->CPL && (rwn == 1))
if(!(page_dir & 2) && user && (rwn == 1))
{
*error = 1;
ret = 0;
@ -469,7 +469,7 @@ INLINE int translate_address(i386_state *cpustate, int rwn, UINT32 *address, UIN
page_entry = cpustate->program->read_dword((page_dir & 0xfffff000) + (table * 4));
if(!(page_entry & 1))
ret = 0;
else if(!(page_entry & 2) && cpustate->CPL && (rwn == 1))
else if((!(page_entry & 2) && user && (rwn == 1)) || (!(page_entry & 4) && user))
{
*error = 1;
ret = 0;
@ -488,11 +488,15 @@ INLINE int translate_address(i386_state *cpustate, int rwn, UINT32 *address, UIN
}
}
else
{
if(page_dir & 1)
*error = 1;
ret = 0;
}
if(!ret)
{
if(rwn != -1)
*error |= ((rwn & 1)<<1) | ((cpustate->CPL==3)?1<<2:0);
*error |= ((rwn & 1)<<1) | (user<<2);
return 0;
}
return 1;
@ -672,6 +676,16 @@ INLINE UINT64 READ64(i386_state *cpustate,UINT32 ea)
return value;
}
INLINE void WRITE_TEST(i386_state *cpustate,UINT32 ea)
{
UINT32 address = ea, error;
if (cpustate->cr[0] & 0x80000000) // page translation enabled
{
if(!translate_address(cpustate,1,&address,&error))
PF_THROW(error);
}
}
INLINE void WRITE8(i386_state *cpustate,UINT32 ea, UINT8 value)
{
UINT32 address = ea, error;