Phase 1 of consolidating descriptor processing in x86seg.js (TSS descriptors are next)

This commit is contained in:
Jeff Parsons 2014-11-27 09:28:12 -08:00 committed by jeffpar
commit 6194aac5af
4 changed files with 132 additions and 45 deletions

View file

@ -2216,6 +2216,11 @@ if (DEBUGGER) {
*/
if (DEBUG) {
this.assert(!(this.cpu.regAX & ~0xffff) && !(this.cpu.regBX & ~0xffff) && !(this.cpu.regCX & ~0xffff) && !(this.cpu.regDX & ~0xffff), "register out of bounds");
/*
if (!fSkipBP && this.cInstructions == 11303367) {
return true;
}
*/
if (!fSkipBP && MAXDEBUG) {
if (!this.cpu.regIP) {
this.println("suspicious IP");
@ -4728,6 +4733,9 @@ if (DEBUGGER) {
if (!sCmd) {
sCmd = this.aPrevCmds[this.iPrevCmd+1];
} else {
if (this.iPrevCmd < 0 && this.aPrevCmds.length) {
this.iPrevCmd = 0;
}
if (this.iPrevCmd < 0 || sCmd != this.aPrevCmds[this.iPrevCmd]) {
this.aPrevCmds.splice(0, 0, sCmd);
this.iPrevCmd = 0;

View file

@ -1308,7 +1308,7 @@ X86CPU.prototype.setIP = function(off)
* @param {number} off
* @param {number} sel
* @param {boolean} [fCall] is true if CALLF in progress, false if RETF in progress, null/undefined otherwise
* @return {boolean} true if a stack switch occurred; the only opcode that needs to care about this is opRETFn()
* @return {boolean|null} true if a stack switch occurred; the only opcode that really needs to care is opRETFn()
*/
X86CPU.prototype.setCSIP = function(off, sel, fCall)
{
@ -1316,13 +1316,14 @@ X86CPU.prototype.setCSIP = function(off, sel, fCall)
this.segCS.fCall = fCall;
/*
* We break this operation into the following discrete steps (eg, set IP, load CS, and then update EIP)
* so that segCS.load(sel) has the option of modifying IP when sel refers to a call gate.
* so that segCS.load(sel) has the option of modifying IP when sel refers to a gate (call, interrupt, trap, etc).
*/
this.regIP = off;
var base = this.segCS.load(sel);
if (base != null) {
this.regEIP = base + this.regIP;
if (base == null) {
return null;
}
this.regEIP = base + this.regIP;
if (PREFETCH) this.flushPrefetch(this.regEIP);
return this.segCS.fStackSwitch;
};

View file

@ -448,9 +448,10 @@ var X86Help = {
opHelpCallF: function(off, sel) {
var regCS = this.segCS.sel;
var regIP = this.regIP;
this.setCSIP(off, sel, true);
this.pushWord(regCS);
this.pushWord(regIP);
if (this.setCSIP(off, sel, true) != null) {
this.pushWord(regCS);
this.pushWord(regIP);
}
},
/**
* opHelpDIVOverflow()
@ -474,10 +475,22 @@ var X86Help = {
*/
opHelpINT: function(nIDT, nError, nCycles) {
/*
* TODO: We assess the cycle cost up front, because otherwise, if opHelpLoadIDT() fails and we end up in
* opHelpFault(), no cost may be assessed. Ultimately, opHelpFault() needs to determine an appropriate cost.
* TODO: We assess the cycle cost up front, because otherwise, if loadIDT() fails, no cost may be assessed.
*/
this.nStepCycles -= this.CYCLES.nOpCyclesInt + nCycles;
var regPS = this.getPS();
var regCS = this.segCS.sel;
var regIP = this.regIP;
var base = this.segCS.loadIDT(nIDT);
if (base != null) {
this.regEIP = base + this.regIP;
this.pushWord(regPS);
this.pushWord(regCS);
this.pushWord(regIP);
if (nError != null) this.pushWord(nError);
this.nFault = -1;
}
/*
if (X86Help.opHelpLoadIDT.call(this, nIDT)) {
if (this.descIDT.maskPS) {
X86Help.opHelpPushPS.call(this, nError);
@ -487,6 +500,7 @@ var X86Help = {
return;
}
X86Help.opHelpFault.call(this, X86.EXCEPTION.GP_FAULT, (nIDT << 3) | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true);
*/
},
/**
* opHelpIRET()
@ -506,9 +520,13 @@ var X86Help = {
return;
}
}
this.setCSIP(this.popWord(), this.popWord(), false);
this.setPS(this.popWord());
if (this.cIntReturn) this.checkIntReturn(this.regEIP);
var regIP = this.popWord();
var regCS = this.popWord();
var regPS = this.popWord();
if (this.setCSIP(regIP, regCS, false) != null) {
this.setPS(regPS);
if (this.cIntReturn) this.checkIntReturn(this.regEIP);
}
},
/**
* opHelpLoadIDT(nIDT)
@ -568,30 +586,6 @@ var X86Help = {
this.descIDT.maskPS = ~(X86.PS.TF | X86.PS.IF);
return true;
},
/**
* opHelpPushPS(nError)
*
* Helper to push processor state, CS:IP, and optional error code onto the stack, and then jump
* to whatever CS:IP was fetched into descIDT by opHelpLoadIDT().
*
* For protected-mode, this function must attempt to load the new code segment first, because if the new segment
* requires a change in privilege level, the return address must be pushed on the NEW stack, not the current stack.
*
* @this {X86CPU}
* @param {number|null|undefined} nError
*/
opHelpPushPS: function(nError) {
var regPS = this.getPS();
var regCS = this.segCS.sel;
var regIP = this.regIP;
this.regPS &= this.descIDT.maskPS;
this.setCSIP(this.descIDT.off, this.descIDT.sel, true);
this.pushWord(regPS);
this.pushWord(regCS);
this.pushWord(regIP);
if (nError != null) this.pushWord(nError);
this.nFault = -1;
},
/**
* opHelpSwitchTSS(selNew, fNest)
*

View file

@ -115,8 +115,6 @@ X86Seg.ID = {
X86Seg.loadReal = function loadReal(sel, fSuppress)
{
this.sel = sel;
this.limit = 0xffff;
this.cpl = this.dpl = 0;
return this.base = sel << 4;
};
@ -126,7 +124,7 @@ X86Seg.loadReal = function loadReal(sel, fSuppress)
* This replaces the segment's default load() function whenever the segment is notified via updateAccess() by the
* CPU's setProtMode() that the processor is now in protected-mode.
*
* Segments in protected-mode are referenced by selectors, which are indexes into descriptor tables (GDT, LDT, IDT)
* Segments in protected-mode are referenced by selectors, which are indexes into descriptor tables (GDT or LDT)
* whose descriptors are 4-word (8-byte) entries:
*
* word 0: segment limit (0-15)
@ -136,6 +134,8 @@ X86Seg.loadReal = function loadReal(sel, fSuppress)
*
* See X86.DESC for offset and bit definitions.
*
* IDT descriptor entries are handled separately by loadIDT().
*
* @this {X86Seg}
* @param {number} sel
* @param {boolean} [fSuppress] is true to suppress any errors, cycle assessment, etc
@ -168,6 +168,52 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
return null;
};
/**
* loadRealIDT(nIDT)
*
* @this {X86Seg}
* @param {number} nIDT
* @return {number|null} base address of selected segment, or null if error
*/
X86Seg.loadRealIDT = function loadRealIDT(nIDT)
{
if (DEBUG) {
this.cpu.assert(nIDT >= 0 && nIDT < 256 && !this.cpu.addrIDT && this.cpu.addrIDTLimit == 0x03FF);
}
/*
* Intel documentation for INT/INTO under "REAL ADDRESS MODE EXCEPTIONS" says:
*
* "[T]he 80286 will shut down if the SP = 1, 3, or 5 before executing the INT or INTO instruction--due to lack of stack space"
*
* TODO: Verify that 80286 real-mode actually enforces the above. See http://localhost:8088/pubs/pc/reference/intel/80286/progref/#page-260
*/
var offIDT = this.cpu.addrIDT + (nIDT << 2);
this.cpu.regIP = this.cpu.getWord(offIDT);
this.sel = this.cpu.getWord(offIDT + 2);
this.cpu.regPS &= ~(X86.PS.TF | X86.PS.IF);
return this.base = this.sel << 4;
};
/**
* loadProtIDT(nIDT)
*
* @this {X86Seg}
* @param {number} nIDT
* @return {number|null} base address of selected segment, or null if error
*/
X86Seg.loadProtIDT = function loadProtIDT(nIDT)
{
if (DEBUG) this.cpu.assert(nIDT >= 0 && nIDT < 256);
nIDT <<= 3;
var addrDesc = this.cpu.addrIDT + nIDT;
if (addrDesc + 7 <= this.cpu.addrIDTLimit) {
return this.loadDesc8(nIDT, addrDesc);
}
X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, nIDT | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true);
return null;
};
/**
* checkReadReal(off, cb, fSuppress)
*
@ -367,6 +413,9 @@ X86Seg.prototype.loadDesc8 = function(sel, addrDesc)
var selMasked = sel & X86.SEL.MASK;
while (true) {
var cplPrev, addrTSS, offSP, offSS, regSPPrev, regSSPrev;
if (this.id == X86Seg.ID.CODE) {
this.fStackSwitch = false;
var fCall = this.fCall;
@ -379,7 +428,7 @@ X86Seg.prototype.loadDesc8 = function(sel, addrDesc)
*/
if (rpl < this.cpl) rpl = this.cpl;
if (rpl <= dpl) {
var cplPrev = this.cpl;
cplPrev = this.cpl;
if (this.load(base & 0xffff, true) != null) {
this.cpu.regIP = limit;
if (this.cpl < cplPrev) {
@ -393,11 +442,11 @@ X86Seg.prototype.loadDesc8 = function(sel, addrDesc)
this.awScratch[i++] = this.cpu.getSOWord(this.cpu.segSS, regSP);
regSP += 2;
}
var addrTSS = this.cpu.segTSS.base;
var offSP = (this.cpl << 2) + X86.TSS.CPL0_SP;
var offSS = offSP + 2;
var regSPPrev = this.cpu.regSP;
var regSSPrev = this.cpu.segSS.sel;
addrTSS = this.cpu.segTSS.base;
offSP = (this.cpl << 2) + X86.TSS.CPL0_SP;
offSS = offSP + 2;
regSPPrev = this.cpu.regSP;
regSSPrev = this.cpu.segSS.sel;
this.cpu.regSP = this.cpu.getWord(addrTSS + offSP);
this.cpu.segSS.load(this.cpu.getWord(addrTSS + offSS));
this.cpu.pushWord(regSSPrev);
@ -409,6 +458,38 @@ X86Seg.prototype.loadDesc8 = function(sel, addrDesc)
}
}
}
else if (type == X86.DESC.ACC.TYPE.GATE_INT || type == X86.DESC.ACC.TYPE.GATE_TRAP) {
if (rpl < this.cpl) rpl = this.cpl;
if (rpl <= dpl) {
cplPrev = this.cpl;
if (this.load(base & 0xffff, true) != null) {
this.cpu.regIP = limit;
if (this.cpl < cplPrev) {
if (fCall !== true) {
base = null;
break;
}
regSP = this.cpu.regSP;
addrTSS = this.cpu.segTSS.base;
offSP = (this.cpl << 2) + X86.TSS.CPL0_SP;
offSS = offSP + 2;
regSPPrev = this.cpu.regSP;
regSSPrev = this.cpu.segSS.sel;
this.cpu.regSP = this.cpu.getWord(addrTSS + offSP);
this.cpu.segSS.load(this.cpu.getWord(addrTSS + offSS));
this.cpu.pushWord(regSSPrev);
this.cpu.pushWord(regSPPrev);
this.fStackSwitch = true;
}
if (type == X86.DESC.ACC.TYPE.GATE_INT) {
this.cpu.regPS &= ~(X86.PS.NT | X86.PS.TF | X86.PS.IF);
} else {
this.cpu.regPS &= ~(X86.PS.NT | X86.PS.TF);
}
return this.base;
}
}
}
else if (type >= X86.DESC.ACC.TYPE.CODE_EXECONLY /* || dpl > this.cpu.segCS.cpl */) {
rpl = sel & X86.SEL.RPL;
if (rpl > this.cpl) {
@ -547,6 +628,7 @@ X86Seg.prototype.updateAccess = function(fProt)
}
if (fProt) {
this.load = X86Seg.loadProt;
this.loadIDT = X86Seg.loadProtIDT;
this.checkRead = X86Seg.checkReadProt;
this.checkWrite = X86Seg.checkWriteProt;
if (this.acc & X86.DESC.ACC.TYPE.SEG) {
@ -574,8 +656,10 @@ X86Seg.prototype.updateAccess = function(fProt)
this.dpl = (this.acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT;
} else {
this.load = X86Seg.loadReal;
this.loadIDT = X86Seg.loadRealIDT;
this.checkRead = X86Seg.checkReadReal;
this.checkWrite = X86Seg.checkWriteReal;
this.limit = 0xffff;
this.cpl = this.dpl = 0;
this.addrDesc = null;
}