Perform GP_FAULT checks before NP_FAULT checks

This commit is contained in:
Jeff Parsons 2015-08-24 18:11:17 -07:00
commit 1a092371d3

View file

@ -626,6 +626,10 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
var fGate, selCode, cplOld, addrTSS, offSP, lenSP, regSPPrev, regSSPrev, regPSClear, regSP; var fGate, selCode, cplOld, addrTSS, offSP, lenSP, regSPPrev, regSSPrev, regPSClear, regSP;
/*
* TODO: As discussed below for X86Seg.ID.DATA, it's likely that testing the PRESENT bit should
* be performed *after* checking the other, more serious potential problems.
*/
if (selMasked && !(acc & X86.DESC.ACC.PRESENT)) { if (selMasked && !(acc & X86.DESC.ACC.PRESENT)) {
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel & X86.ERRCODE.SELMASK); if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel & X86.ERRCODE.SELMASK);
return X86.ADDR_INVALID; return X86.ADDR_INVALID;
@ -818,7 +822,6 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
case X86Seg.ID.DATA: case X86Seg.ID.DATA:
if (selMasked) { if (selMasked) {
if (!(acc & X86.DESC.ACC.PRESENT)) {
/* /*
* OS/2 1.0 faults on segments with "empty descriptors" multiple times during boot; for example: * OS/2 1.0 faults on segments with "empty descriptors" multiple times during boot; for example:
* *
@ -834,17 +837,37 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
* dumpDesc(0x002F): %174BE8 * dumpDesc(0x002F): %174BE8
* base=000000 limit=0000 type=0x00 (undefined) ext=0x0000 dpl=0x00 * base=000000 limit=0000 type=0x00 (undefined) ext=0x0000 dpl=0x00
* *
* Before I added the X86.DESC.ACC.PRESENT check, I used to (incorrectly) dispatch this as a GP_FAULT, * And Windows 95 Setup, during the "Analyzing Your Computer" phase, will fault on an attempt to load
* but OS/2 still appeared to handle the fault OK. However, this condition is now properly handled as * a GDT selector of type LDT (why it does this is a mystery I've not yet investigated):
* an NP_FAULT. *
* Fault 0x0D (0x26F0) on opcode 0x8E @039F:039B (%199E9B)
* EAX=0000149F EBX=00000100 ECX=000026F3 EDX=0020149F
* ESP=0000AA34 EBP=0000AA3C ESI=000026E7 EDI=00000080
* SS=155F[002AC9D0,C0BF] DS=149F[0031B470,9B1F] ES=0237[000C0000,FFFF]
* CS=039F[00199B00,2ABF] FS=0000[00000000,0000] GS=0000[00000000,0000]
* LD=0038[00FA4C50,FFEF] GD=[00FA0800,011F] ID=[00FA0000,07FF] TR=0088 A20=ON
* CR0=0000FFF1 CR2=00000000 CR3=00000000 PS=00003246 V0 D0 I1 T0 S0 Z1 A0 P1 C0
* 039F:039B 8EC1 MOV ES,CX
* ## ds cx
* dumpDesc(0x26F3): %00FA2EF0
* base=0006C726 limit=0000 type=0x02 (ldt,not present) ext=0x0000 dpl=0x00
*
* In both cases, the segment type is not valid for the target segment register *and* the PRESENT bit
* is clear. OS/2 didn't seem to care whether I reported NP_FAULT or GP_FAULT, but Windows 95 definitely
* cares: it will resolve the fault only if a GP_FAULT is reported. And Intel's 80386 Programmers Reference
* suggests that, yes, NP_FAULT checks are supposed to come *after* GP_FAULT checks.
*/ */
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel & X86.ERRCODE.SELMASK);
return X86.ADDR_INVALID;
}
if (type < X86.DESC.ACC.TYPE.SEG || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.READABLE)) == X86.DESC.ACC.TYPE.CODE) { if (type < X86.DESC.ACC.TYPE.SEG || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.READABLE)) == X86.DESC.ACC.TYPE.CODE) {
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK, true); if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK, true);
return X86.ADDR_INVALID; return X86.ADDR_INVALID;
} }
/*
* TODO: This would be a good place to perform some additional access rights checks, too.
*/
if (!(acc & X86.DESC.ACC.PRESENT)) {
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel & X86.ERRCODE.SELMASK);
return X86.ADDR_INVALID;
}
} }
break; break;