CPU caches CPL now (independent of segCS.cpl)
This commit is contained in:
parent
fc9a2a903f
commit
4bb34eb81d
5 changed files with 88 additions and 36 deletions
|
|
@ -2945,8 +2945,8 @@ if (DEBUGGER) {
|
|||
/*
|
||||
* Halt if running with interrupts disabled and IOPL < CPL, because that's likely an error
|
||||
*/
|
||||
if (!(this.cpu.regPS & X86.PS.IF) && this.cpu.nIOPL < this.cpu.segCS.cpl) {
|
||||
this.printMessage("interrupts disabled at IOPL " + this.cpu.nIOPL + " and CPL " + this.cpu.segCS.cpl, true);
|
||||
if (!(this.cpu.regPS & X86.PS.IF) && this.cpu.nIOPL < this.cpu.nCPL) {
|
||||
this.printMessage("interrupts disabled at IOPL " + this.cpu.nIOPL + " and CPL " + this.cpu.nCPL, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -761,7 +761,7 @@ X86CPU.prototype.mapPageBlock = function(addr, fWrite, fSuppress)
|
|||
return this.memEmpty;
|
||||
}
|
||||
|
||||
if (!(pde & X86.PTE.USER) && this.segCS.cpl == 3) {
|
||||
if (!(pde & X86.PTE.USER) && this.nCPL == 3) {
|
||||
if (!fSuppress) X86.fnPageFault.call(this, addr, true, fWrite);
|
||||
return this.memEmpty;
|
||||
}
|
||||
|
|
@ -781,7 +781,7 @@ X86CPU.prototype.mapPageBlock = function(addr, fWrite, fSuppress)
|
|||
return this.memEmpty;
|
||||
}
|
||||
|
||||
if (!(pte & X86.PTE.USER) && this.segCS.cpl == 3) {
|
||||
if (!(pte & X86.PTE.USER) && this.nCPL == 3) {
|
||||
if (!fSuppress) X86.fnPageFault.call(this, addr, true, fWrite);
|
||||
return this.memEmpty;
|
||||
}
|
||||
|
|
@ -1263,7 +1263,7 @@ X86CPU.prototype.resetRegs = function()
|
|||
*/
|
||||
this.intFlags = X86.INTFLAG.NONE;
|
||||
|
||||
this.setCSIP(0, 0xffff); // this should be called before the first setPS() call
|
||||
this.setCSIP(0, 0xffff); // this should be called before the first setPS() call, in part so that CPL will be set
|
||||
|
||||
if (!I386) this.resetSizes();
|
||||
|
||||
|
|
@ -1331,7 +1331,7 @@ X86CPU.prototype.resetRegs = function()
|
|||
|
||||
/*
|
||||
* This resets the Processor Status flags (regPS), along with all the internal "result registers";
|
||||
* we've taken care to ensure that both segCS.cpl and nIOPL are initialized before this first setPS() call.
|
||||
* we've taken care to ensure that both CPL and IOPL are initialized before this first setPS() call.
|
||||
*/
|
||||
this.setPS(0);
|
||||
|
||||
|
|
@ -1846,12 +1846,8 @@ X86CPU.prototype.getCS = function()
|
|||
*/
|
||||
X86CPU.prototype.setCS = function(sel)
|
||||
{
|
||||
var regEIP = this.getIP();
|
||||
this.regLIP = (this.segCS.load(sel) + regEIP)|0;
|
||||
this.regLIPLimit = (this.segCS.base + this.segCS.limit)|0;
|
||||
if (I386) this.resetSizes();
|
||||
this.setCSIP(this.getIP(), sel);
|
||||
if (!BUGS_8086) this.opFlags |= this.OPFLAG_NOINTR_8086;
|
||||
if (PREFETCH) this.flushPrefetch(this.regLIP);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2044,10 +2040,16 @@ X86CPU.prototype.setCSIP = function(off, sel, fCall)
|
|||
this.regEIP = off;
|
||||
var base = this.segCS.load(sel);
|
||||
if (base !== X86.ADDR_INVALID) {
|
||||
|
||||
/*
|
||||
* TODO: Should this code be factored into a setLIP() function? The other primary client would be fnINT().
|
||||
*/
|
||||
if (I386) this.resetSizes();
|
||||
this.regLIP = (base + (this.regEIP & (I386? this.dataMask : 0xffff)))|0;
|
||||
this.regLIPLimit = (base + this.segCS.limit)|0;
|
||||
if (I386) this.resetSizes();
|
||||
this.nCPL = this.segCS.cpl; // cache the current CPL where it's more convenient
|
||||
if (PREFETCH) this.flushPrefetch(this.regLIP);
|
||||
|
||||
return this.segCS.fStackSwitch;
|
||||
}
|
||||
return null;
|
||||
|
|
@ -2713,7 +2715,7 @@ X86CPU.prototype.setPS = function(regPS, cpl)
|
|||
* So, if the CPU is an 80286, we zero incoming bits 12-14 in real-mode (bit 15 is never allowed to
|
||||
* be modified, so there's no need to mask it). And if the CPU is an 80386, we zero only bit 14 (PS.NT),
|
||||
* allowing the IOPL bits to change; however, that should not affect any real-mode operations, since
|
||||
* segCS.cpl will always be zero, making the IOPL setting irrelevant.
|
||||
* CPL will always be zero, making IOPL irrelevant.
|
||||
*
|
||||
* It's still an open question whether an 80386 should also clear the Nested Task (PS.NT) flag in
|
||||
* real-mode; if not, then initProcessor() should set PS_CLEAR_RM to zero.
|
||||
|
|
@ -2724,7 +2726,7 @@ X86CPU.prototype.setPS = function(regPS, cpl)
|
|||
* There are some cases (eg, an IRET returning to a less privileged code segment) where the CPL
|
||||
* we compare against should come from the outgoing code segment, so if the caller provided it, use it.
|
||||
*/
|
||||
if (cpl === undefined) cpl = this.segCS.cpl;
|
||||
if (cpl === undefined) cpl = this.nCPL;
|
||||
|
||||
/*
|
||||
* Since PS.IOPL and PS.IF are part of PS_DIRECT, we need to take care of any 80286-specific behaviors
|
||||
|
|
@ -2762,7 +2764,7 @@ X86CPU.prototype.setPS = function(regPS, cpl)
|
|||
X86CPU.prototype.checkIOPM = function(port, nPorts)
|
||||
{
|
||||
var bitsPorts = 0;
|
||||
if (I386 && (this.regCR0 & X86.CR0.MSW.PE) && (this.segCS.cpl > this.nIOPL || (this.regPS & X86.PS.VM)) && this.segTSS.addrIOPM) {
|
||||
if (I386 && (this.regCR0 & X86.CR0.MSW.PE) && (this.nCPL > this.nIOPL || (this.regPS & X86.PS.VM)) && this.segTSS.addrIOPM) {
|
||||
var offIOPM = port >>> 3;
|
||||
var addrIOPM = this.segTSS.addrIOPM + offIOPM;
|
||||
bitsPorts = ((1 << nPorts) - 1) << (port & 0x7);
|
||||
|
|
|
|||
|
|
@ -1247,9 +1247,14 @@ X86.fnINT = function INT(nIDT, nError, nCycles)
|
|||
this.pushWord(oldIP);
|
||||
if (nError != null) this.pushWord(nError);
|
||||
this.nFault = -1;
|
||||
|
||||
/*
|
||||
* TODO: Should this code be factored into a setLIP() function? The other primary client would be setCSIP().
|
||||
*/
|
||||
if (I386) this.resetSizes();
|
||||
this.regLIP = addr;
|
||||
this.regLIPLimit = (this.segCS.base + this.segCS.limit)|0;
|
||||
if (I386) this.resetSizes();
|
||||
this.nCPL = this.segCS.cpl; // cache the current CPL where it's more convenient
|
||||
if (PREFETCH) this.flushPrefetch(this.regLIP);
|
||||
}
|
||||
};
|
||||
|
|
@ -1265,6 +1270,7 @@ X86.fnIRET = function IRET()
|
|||
* TODO: We assess a fixed cycle cost up front, because at the moment, switchTSS() doesn't assess anything.
|
||||
*/
|
||||
this.nStepCycles -= this.cycleCounts.nOpCyclesIRet;
|
||||
|
||||
if (this.regCR0 & X86.CR0.MSW.PE) {
|
||||
if (this.regPS & X86.PS.NT) {
|
||||
var addrNew = this.segTSS.base;
|
||||
|
|
@ -1276,7 +1282,8 @@ X86.fnIRET = function IRET()
|
|||
return;
|
||||
}
|
||||
}
|
||||
var cpl = this.segCS.cpl;
|
||||
|
||||
var cpl = this.nCPL;
|
||||
var newIP = this.popWord();
|
||||
var newCS = this.popWord();
|
||||
var newPS = this.popWord();
|
||||
|
|
@ -1390,7 +1397,7 @@ X86.fnLAR = function LAR(dst, src)
|
|||
*/
|
||||
this.clearZF();
|
||||
if (this.segVER.load(src, true) !== X86.ADDR_INVALID) {
|
||||
if (this.segVER.dpl >= this.segCS.cpl && this.segVER.dpl >= (src & X86.SEL.RPL)) {
|
||||
if (this.segVER.dpl >= this.nCPL && this.segVER.dpl >= (src & X86.SEL.RPL)) {
|
||||
this.setZF();
|
||||
dst = this.segVER.acc & ~X86.DESC.ACC.BASE1623;
|
||||
if (this.dataSize > 2) {
|
||||
|
|
@ -1695,7 +1702,7 @@ X86.fnLSL = function LSL(dst, src)
|
|||
*/
|
||||
if ((src & X86.SEL.MASK) && this.segVER.load(src, true) !== X86.ADDR_INVALID) {
|
||||
var fConforming = ((this.segVER.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) == X86.DESC.ACC.TYPE.CODE_CONFORMING);
|
||||
if ((fConforming || this.segVER.dpl >= this.segCS.cpl) && this.segVER.dpl >= (src & X86.SEL.RPL)) {
|
||||
if ((fConforming || this.segVER.dpl >= this.nCPL) && this.segVER.dpl >= (src & X86.SEL.RPL)) {
|
||||
this.setZF();
|
||||
return this.segVER.limit;
|
||||
}
|
||||
|
|
@ -2256,11 +2263,11 @@ X86.fnRETF = function RETF(n)
|
|||
* it safe and using CODE_CONFORMING instead of CODE_CONFORMING_READABLE. Also, for the record, I've not
|
||||
* seen this situation occur yet (eg, in OS/2 1.0).
|
||||
*/
|
||||
if ((this.segDS.sel & X86.SEL.MASK) && this.segDS.dpl < this.segCS.cpl && (this.segDS.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) != X86.DESC.ACC.TYPE.CODE_CONFORMING) {
|
||||
if ((this.segDS.sel & X86.SEL.MASK) && this.segDS.dpl < this.nCPL && (this.segDS.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) != X86.DESC.ACC.TYPE.CODE_CONFORMING) {
|
||||
this.assert(false); // I'm not asserting this is bad, I just want to see it in action
|
||||
this.segDS.load(0);
|
||||
}
|
||||
if ((this.segES.sel & X86.SEL.MASK) && this.segES.dpl < this.segCS.cpl && (this.segES.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) != X86.DESC.ACC.TYPE.CODE_CONFORMING) {
|
||||
if ((this.segES.sel & X86.SEL.MASK) && this.segES.dpl < this.nCPL && (this.segES.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) != X86.DESC.ACC.TYPE.CODE_CONFORMING) {
|
||||
this.assert(false); // I'm not asserting this is bad, I just want to see it in action
|
||||
this.segES.load(0);
|
||||
}
|
||||
|
|
@ -3352,7 +3359,7 @@ X86.fnVERR = function VERR(dst, src)
|
|||
* Otherwise, DPL must be greater than or equal to (have less or the same privilege as) both the
|
||||
* current privilege level and the selector's RPL.
|
||||
*/
|
||||
if (this.segVER.dpl >= this.segCS.cpl && this.segVER.dpl >= (dst & X86.SEL.RPL) ||
|
||||
if (this.segVER.dpl >= this.nCPL && this.segVER.dpl >= (dst & X86.SEL.RPL) ||
|
||||
(this.segVER.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) == X86.DESC.ACC.TYPE.CODE_CONFORMING) {
|
||||
this.setZF();
|
||||
return dst;
|
||||
|
|
@ -3390,7 +3397,7 @@ X86.fnVERW = function VERW(dst, src)
|
|||
* DPL must be greater than or equal to (have less or the same privilege as) both the current
|
||||
* privilege level and the selector's RPL.
|
||||
*/
|
||||
if (this.segVER.dpl >= this.segCS.cpl && this.segVER.dpl >= (dst & X86.SEL.RPL)) {
|
||||
if (this.segVER.dpl >= this.nCPL && this.segVER.dpl >= (dst & X86.SEL.RPL)) {
|
||||
this.setZF();
|
||||
return dst;
|
||||
}
|
||||
|
|
@ -3758,7 +3765,7 @@ X86.fnPageFault = function(addr, fPresent, fWrite)
|
|||
var nError = 0;
|
||||
if (fPresent) nError |= X86.PTE.PRESENT;
|
||||
if (fWrite) nError |= X86.PTE.READWRITE;
|
||||
if (this.segCS.cpl == 3) nError |= X86.PTE.USER;
|
||||
if (this.nCPL == 3) nError |= X86.PTE.USER;
|
||||
X86.fnFault.call(this, X86.EXCEPTION.PG_FAULT, nError);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -145,9 +145,9 @@ X86.opLSL = function LSL()
|
|||
*/
|
||||
X86.opLOADALL = function LOADALL()
|
||||
{
|
||||
if (this.segCS.cpl) {
|
||||
if (this.nCPL) {
|
||||
/*
|
||||
* You're not allowed to use LOADALL if the current privilege level is something other than zero.
|
||||
* To use LOADALL, CPL must be zero.
|
||||
*/
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0, true);
|
||||
return;
|
||||
|
|
@ -202,9 +202,9 @@ X86.opLOADALL = function LOADALL()
|
|||
X86.opCLTS = function CLTS()
|
||||
{
|
||||
/*
|
||||
* NOTE: The following code shouldn't need to test for X86.PS.VM because V86-mode is CPL 3.
|
||||
* NOTE: The following code shouldn't need to also test X86.PS.VM, because V86-mode is CPL 3.
|
||||
*/
|
||||
if (this.segCS.cpl) {
|
||||
if (this.nCPL) {
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
|
|
@ -234,9 +234,9 @@ X86.opCLTS = function CLTS()
|
|||
X86.opMOVrc = function MOVrc()
|
||||
{
|
||||
/*
|
||||
* NOTE: The following code shouldn't need to test for X86.PS.VM because V86-mode is CPL 3.
|
||||
* NOTE: The following code shouldn't need to also test X86.PS.VM, because V86-mode is CPL 3.
|
||||
*/
|
||||
if (this.segCS.cpl) {
|
||||
if (this.nCPL) {
|
||||
/*
|
||||
* You're not allowed to read control registers if the current privilege level is not zero
|
||||
*/
|
||||
|
|
@ -317,9 +317,9 @@ X86.opMOVrc = function MOVrc()
|
|||
X86.opMOVcr = function MOVcr()
|
||||
{
|
||||
/*
|
||||
* NOTE: The following code shouldn't need to test for X86.PS.VM because V86-mode is CPL 3.
|
||||
* NOTE: The following code shouldn't need to also test X86.PS.VM, because V86-mode is CPL 3.
|
||||
*/
|
||||
if (this.segCS.cpl) {
|
||||
if (this.nCPL) {
|
||||
/*
|
||||
* You're not allowed to write control registers if the current privilege level is not zero
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2453,18 +2453,38 @@ X86.opWAIT = function WAIT()
|
|||
};
|
||||
|
||||
/**
|
||||
* op=0x9C (PUSHF)
|
||||
* op=0x9C (PUSHF/PUSHFD)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
X86.opPUSHF = function PUSHF()
|
||||
{
|
||||
this.pushWord(this.getPS());
|
||||
/*
|
||||
* TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode.
|
||||
*/
|
||||
var regPS = this.getPS();
|
||||
if (I386) {
|
||||
if ((regPS & X86.PS.VM) && this.nIOPL < 3) {
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* It doesn't matter whether this is PUSHF or PUSHFD: the VM and RF flags are never pushed, so
|
||||
* we can always clear them.
|
||||
*
|
||||
* This does, however, beg the question: how does code running in V86-mode detect that's in V86-mode
|
||||
* and not real-mode? By using the SMSW instruction and checking the PE (protected-mode enabled) bit.
|
||||
* The SMSW instruction returns a subset of the CR0 bits, and unlike the MOV reg,CR0 instruction, is
|
||||
* allowed in V86-mode.
|
||||
*/
|
||||
regPS &= ~(X86.PS.VM | X86.PS.RF);
|
||||
}
|
||||
this.pushWord(regPS);
|
||||
this.nStepCycles -= this.cycleCounts.nOpCyclesPushReg;
|
||||
};
|
||||
|
||||
/**
|
||||
* op=0x9D (POPF)
|
||||
* op=0x9D (POPF/POPFD)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
|
|
@ -2478,7 +2498,7 @@ X86.opPOPF = function POPF()
|
|||
return;
|
||||
}
|
||||
/*
|
||||
* On the 80386, regardless of mode, VM and RF (the only defined EFLAGS bit above bit 15) are never changed by POPFD.
|
||||
* Regardless of mode, VM and RF (the only defined EFLAGS bit above bit 15) are never changed by POPFD.
|
||||
*/
|
||||
var newPS = this.popWord();
|
||||
if (I386) newPS = (newPS & 0xffff) | (this.regPS & ~0xffff);
|
||||
|
|
@ -3471,6 +3491,13 @@ X86.opINTO = function INTO()
|
|||
*/
|
||||
X86.opIRET = function IRET()
|
||||
{
|
||||
/*
|
||||
* TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode.
|
||||
*/
|
||||
if (I386 && (this.regPS & X86.PS.VM) && this.nIOPL < 3) {
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
X86.fnIRET.call(this);
|
||||
};
|
||||
|
||||
|
|
@ -4058,6 +4085,14 @@ X86.opSTC = function STC()
|
|||
*/
|
||||
X86.opCLI = function CLI()
|
||||
{
|
||||
/*
|
||||
* The following code should be sufficient for all modes, because in real-mode, CPL is always zero,
|
||||
* and in V86-mode, CPL is always 3.
|
||||
*/
|
||||
if (this.nCPL > this.nIOPL) {
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
this.clearIF();
|
||||
this.nStepCycles -= this.cycleCounts.nOpCyclesCLI; // CLI takes LONGER on an 80286
|
||||
};
|
||||
|
|
@ -4069,6 +4104,14 @@ X86.opCLI = function CLI()
|
|||
*/
|
||||
X86.opSTI = function STI()
|
||||
{
|
||||
/*
|
||||
* The following code should be sufficient for all modes, because in real-mode, CPL is always zero,
|
||||
* and in V86-mode, CPL is always 3.
|
||||
*/
|
||||
if (this.nCPL > this.nIOPL) {
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
this.setIF();
|
||||
this.opFlags |= X86.OPFLAG.NOINTR;
|
||||
this.nStepCycles -= 2; // STI takes 2 cycles on all CPUs
|
||||
|
|
|
|||
Loading…
Reference in a new issue