Clean up segment limit checks

This commit is contained in:
Jeff Parsons 2015-05-11 08:40:06 -07:00 committed by jeffpar
commit a9b3eb764e

View file

@ -68,6 +68,7 @@ function X86Seg(cpu, id, sName, fProt)
this.sName = sName || ""; this.sName = sName || "";
this.sel = 0; this.sel = 0;
this.limit = 0xffff; this.limit = 0xffff;
this.offMax = this.limit + 1;
this.base = 0; this.base = 0;
this.acc = this.type = 0; this.acc = this.type = 0;
this.ext = 0; this.ext = 0;
@ -127,15 +128,11 @@ X86Seg.prototype.loadReal = function loadReal(sel, fSuppress)
{ {
this.sel = sel & 0xffff; this.sel = sel & 0xffff;
/* /*
* Apparently, loading a new value into a segment register in real-mode alters NEITHER the limit NOR these * Loading a new value into a segment register in real-mode alters ONLY the selector and the base;
* other attributes, so I've since moved these defaults into the constructor. If you run any code that switches * all other attributes (eg, limit, operand size, address size, etc) are unchanged. If you run any
* to protected-mode, loads a 32-bit code segment, and then switches back to real-mode, it is THAT code's * code that switches to protected-mode, loads a 32-bit code segment, and then switches back to
* responsibility to load a 16-bit segment into CS before returning to real-mode; otherwise, you're probably toast. * real-mode, it is THAT code's responsibility to load a 16-bit segment into CS before returning to
* * real-mode; otherwise, your machine will probably be toast.
* this.dataSize = this.addrSize = 2;
* this.dataMask = this.addrMask = 0xffff;
*
* In short, only the selector and base portions of a segment register can be changed in real-mode.
*/ */
return this.base = this.sel << 4; return this.base = this.sel << 4;
}; };
@ -175,18 +172,12 @@ X86Seg.prototype.loadProt = function loadProt(sel, fSuppress)
*/ */
sel &= 0xffff; sel &= 0xffff;
/*
* When comparing descriptor addresses, we must be mindful that any addresses above 2Gb will be negative;
* that in itself is not a problem UNLESS the descriptor table straddles the 2Gb boundary, meaning the
* starting address is positive but the ending (limit) address is negative. Although that situation is
* highly unlikely, the safest thing to do is coerce the bounding addresses to unsigned values, using ">>> 0."
*/
if (!(sel & X86.SEL.LDT)) { if (!(sel & X86.SEL.LDT)) {
addrDT = cpu.addrGDT >>> 0; addrDT = cpu.addrGDT;
addrDTLimit = cpu.addrGDTLimit >>> 0; addrDTLimit = cpu.addrGDTLimit;
} else { } else {
addrDT = cpu.segLDT.base >>> 0; addrDT = cpu.segLDT.base;
addrDTLimit = addrDT + cpu.segLDT.limit; // segment limit properties are already coerced unsigned addrDTLimit = (addrDT + cpu.segLDT.limit)|0;
} }
/* /*
* The ROM BIOS POST executes some test code in protected-mode without properly initializing the LDT, * The ROM BIOS POST executes some test code in protected-mode without properly initializing the LDT,
@ -199,8 +190,8 @@ X86Seg.prototype.loadProt = function loadProt(sel, fSuppress)
* of the CPU that prevents the GDT or LDT being located at physical address zero. * of the CPU that prevents the GDT or LDT being located at physical address zero.
*/ */
if (!fSuppress || addrDT) { if (!fSuppress || addrDT) {
var addrDesc = addrDT + (sel & X86.SEL.MASK); var addrDesc = (addrDT + (sel & X86.SEL.MASK))|0;
if (addrDesc + 7 <= addrDTLimit) { if ((addrDTLimit - addrDesc)|0 >= 7) {
/* /*
* TODO: This is only the first of many steps toward accurately counting cycles in protected mode; * TODO: This is only the first of many steps toward accurately counting cycles in protected mode;
* I simply noted that "POP segreg" takes 5 cycles in real mode and 20 in protected mode, so I'm * I simply noted that "POP segreg" takes 5 cycles in real mode and 20 in protected mode, so I'm
@ -259,8 +250,8 @@ X86Seg.prototype.loadIDTProt = function loadIDTProt(nIDT)
cpu.assert(nIDT >= 0 && nIDT < 256); cpu.assert(nIDT >= 0 && nIDT < 256);
nIDT <<= 3; nIDT <<= 3;
var addrDesc = cpu.addrIDT + nIDT; var addrDesc = (cpu.addrIDT + nIDT)|0;
if (addrDesc + 7 <= cpu.addrIDTLimit) { if (((cpu.addrIDTLimit - addrDesc)|0) >= 7) {
return this.loadDesc8(addrDesc, nIDT) + cpu.regEIP; return this.loadDesc8(addrDesc, nIDT) + cpu.regEIP;
} }
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, nIDT | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true); X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, nIDT | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true);
@ -270,8 +261,8 @@ X86Seg.prototype.loadIDTProt = function loadIDTProt(nIDT)
/** /**
* checkReadReal(off, cb, fSuppress) * checkReadReal(off, cb, fSuppress)
* *
* TODO: Invoke X86.fnFault.call(this.cpu, X86.EXCEPTION.GP_FAULT) if off is 0xffff and cb > 1; * TODO: Invoke X86.fnFault.call(this.cpu, X86.EXCEPTION.GP_FAULT) if off+cb is beyond offMax on 80186 and up;
* also, whether or not the fnFault() call should include an error code, since this is happening in real-mode. * also, determine whether fnFault() call should include an error code, since this is happening in real-mode.
* *
* @this {X86Seg} * @this {X86Seg}
* @param {number} off is a segment-relative offset * @param {number} off is a segment-relative offset
@ -287,8 +278,8 @@ X86Seg.prototype.checkReadReal = function checkReadReal(off, cb, fSuppress)
/** /**
* checkWriteReal(off, cb, fSuppress) * checkWriteReal(off, cb, fSuppress)
* *
* TODO: Invoke X86.fnFault.call(this.cpu, X86.EXCEPTION.GP_FAULT) if off is 0xffff and cb > 1; * TODO: Invoke X86.fnFault.call(this.cpu, X86.EXCEPTION.GP_FAULT) if off+cb is beyond offMax on 80186 and up;
* also, whether or not the fnFault() call should include an error code, since this is happening in real-mode. * also, determine whether fnFault() call should include an error code, since this is happening in real-mode.
* *
* @this {X86Seg} * @this {X86Seg}
* @param {number} off is a segment-relative offset * @param {number} off is a segment-relative offset
@ -313,10 +304,10 @@ X86Seg.prototype.checkWriteReal = function checkWriteReal(off, cb, fSuppress)
X86Seg.prototype.checkReadProt = function checkReadProt(off, cb, fSuppress) X86Seg.prototype.checkReadProt = function checkReadProt(off, cb, fSuppress)
{ {
/* /*
* Since "off" could be a 32-bit value with the sign bit (bit 31) set, we must convert * Since off could be a 32-bit value with the sign bit (bit 31) set, we must convert
* it to an unsigned value using ">>>"; limit was already converted at segment load time. * it to an unsigned value using ">>>"; offMax was already converted at segment load time.
*/ */
if ((off >>> 0) + cb - 1 <= this.limit) { if ((off >>> 0) + cb <= this.offMax) {
return (this.base + off)|0; return (this.base + off)|0;
} }
return this.checkReadProtDisallowed(off, cb, fSuppress); return this.checkReadProtDisallowed(off, cb, fSuppress);
@ -334,10 +325,10 @@ X86Seg.prototype.checkReadProt = function checkReadProt(off, cb, fSuppress)
X86Seg.prototype.checkReadProtDown = function checkReadProtDown(off, cb, fSuppress) X86Seg.prototype.checkReadProtDown = function checkReadProtDown(off, cb, fSuppress)
{ {
/* /*
* Since "off" could be a 32-bit value with the sign bit (bit 31) set, we must convert * Since off could be a 32-bit value with the sign bit (bit 31) set, we must convert
* it to an unsigned value using ">>>"; limit was already converted at segment load time. * it to an unsigned value using ">>>"; offMax was already converted at segment load time.
*/ */
if ((off >>> 0) + cb - 1 > this.limit) { if ((off >>> 0) + cb > this.offMax) {
return (this.base + off)|0; return (this.base + off)|0;
} }
return this.checkReadProtDisallowed(off, cb, fSuppress); return this.checkReadProtDisallowed(off, cb, fSuppress);
@ -372,10 +363,10 @@ X86Seg.prototype.checkReadProtDisallowed = function checkReadProtDisallowed(off,
X86Seg.prototype.checkWriteProt = function checkWriteProt(off, cb, fSuppress) X86Seg.prototype.checkWriteProt = function checkWriteProt(off, cb, fSuppress)
{ {
/* /*
* Since "off" could be a 32-bit value with the sign bit (bit 31) set, we must convert * Since off could be a 32-bit value with the sign bit (bit 31) set, we must convert
* it to an unsigned value using ">>>"; limit was already converted at segment load time. * it to an unsigned value using ">>>"; offMax was already converted at segment load time.
*/ */
if ((off >>> 0) + cb - 1 <= this.limit) { if ((off >>> 0) + cb <= this.offMax) {
return (this.base + off)|0; return (this.base + off)|0;
} }
return this.checkWriteProtDisallowed(off, cb, fSuppress); return this.checkWriteProtDisallowed(off, cb, fSuppress);
@ -393,10 +384,10 @@ X86Seg.prototype.checkWriteProt = function checkWriteProt(off, cb, fSuppress)
X86Seg.prototype.checkWriteProtDown = function checkWriteProtDown(off, cb, fSuppress) X86Seg.prototype.checkWriteProtDown = function checkWriteProtDown(off, cb, fSuppress)
{ {
/* /*
* Since "off" could be a 32-bit value with the sign bit (bit 31) set, we must convert * Since off could be a 32-bit value with the sign bit (bit 31) set, we must convert
* it to an unsigned value using ">>>"; limit was already converted at segment load time. * it to an unsigned value using ">>>"; offMax was already converted at segment load time.
*/ */
if ((off >>> 0) + cb - 1 > this.limit) { if ((off >>> 0) + cb > this.offMax) {
return (this.base + off)|0; return (this.base + off)|0;
} }
return this.checkWriteProtDisallowed(off, cb, fSuppress); return this.checkWriteProtDisallowed(off, cb, fSuppress);
@ -534,11 +525,11 @@ X86Seg.prototype.loadAcc = function(sel, fGDT)
addrDTLimit = cpu.addrGDTLimit; addrDTLimit = cpu.addrGDTLimit;
} else if (!fGDT) { } else if (!fGDT) {
addrDT = cpu.segLDT.base; addrDT = cpu.segLDT.base;
addrDTLimit = addrDT + cpu.segLDT.limit; addrDTLimit = (addrDT + cpu.segLDT.limit)|0;
} }
if (addrDT !== undefined) { if (addrDT !== undefined) {
var addrDesc = addrDT + (sel & X86.SEL.MASK); var addrDesc = (addrDT + (sel & X86.SEL.MASK))|0;
if (addrDesc + 7 <= addrDTLimit) { if (((addrDTLimit - addrDesc)|0) >= 7) {
return cpu.getShort(addrDesc + X86.DESC.ACC.OFFSET); return cpu.getShort(addrDesc + X86.DESC.ACC.OFFSET);
} }
} }
@ -570,6 +561,7 @@ X86Seg.prototype.loadDesc6 = function(addrDesc, sel)
this.sel = sel; this.sel = sel;
this.base = base; this.base = base;
this.limit = limit; this.limit = limit;
this.offMax = (limit >>> 0) + 1;
this.acc = acc & X86.DESC.ACC.MASK; this.acc = acc & X86.DESC.ACC.MASK;
this.type = (acc & X86.DESC.ACC.TYPE.MASK); this.type = (acc & X86.DESC.ACC.TYPE.MASK);
this.ext = 0; this.ext = 0;
@ -807,11 +799,8 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
} }
this.sel = sel; this.sel = sel;
this.base = base; this.base = base;
/* this.limit = limit;
* Since limit could be a 32-bit value with the sign bit (bit 31) set, we convert this.offMax = (limit >>> 0) + 1;
* it to an unsigned value now, using ">>>", so that the limit checks won't have to.
*/
this.limit = (limit >>> 0);
this.acc = acc; this.acc = acc;
this.type = type; this.type = type;
this.ext = ext; this.ext = ext;
@ -855,7 +844,23 @@ X86Seg.prototype.setBase = function(addr)
*/ */
X86Seg.prototype.save = function() X86Seg.prototype.save = function()
{ {
return [this.sel, this.base, this.limit, this.acc, this.id, this.sName, this.cpl, this.dpl, this.addrDesc]; return [
this.sel,
this.base,
this.limit,
this.acc,
this.id,
this.sName,
this.cpl,
this.dpl,
this.addrDesc,
this.addrSize,
this.addrMask,
this.dataSize,
this.dataMask,
this.type,
this.offMax
];
}; };
/** /**
@ -881,6 +886,12 @@ X86Seg.prototype.restore = function(a)
this.cpl = a[6]; this.cpl = a[6];
this.dpl = a[7]; this.dpl = a[7];
this.addrDesc = a[8]; this.addrDesc = a[8];
this.addrSize = a[9];
this.addrMask = a[10];
this.dataSize = a[11];
this.dataMask = a[12];
this.type = a[13];
this.offMax = a[14];
} }
}; };
@ -957,19 +968,6 @@ X86Seg.prototype.updateMode = function(fLoad, fProt)
this.loadIDT = this.loadIDTReal; this.loadIDT = this.loadIDTReal;
this.checkRead = this.checkReadReal; this.checkRead = this.checkReadReal;
this.checkWrite = this.checkWriteReal; this.checkWrite = this.checkWriteReal;
/*
* We don't want to mess with the limit, so that features like "Unreal" mode (or "Big Real" mode
* as it's called in the HIMEM source code) will work, at least until the next explicit segment load.
*
* See http://www.os2museum.com/wp/himem-sys-unreal-mode-and-loadall/ for more details.
*
* Ditto for other attributes such as acc, type, and ext (and the OPERAND and ADDRESS sizes, which
* are derived from the ext property). Even an explicit segment load in real-mode does not alter those
* properties.
*
* TODO: The checkReadReal() and checkWriteReal() functions need to generate GP faults for offsets
* beyond the current real-mode limit.
*/
this.cpl = this.dpl = 0; this.cpl = this.dpl = 0;
this.addrDesc = X86.ADDR_INVALID; this.addrDesc = X86.ADDR_INVALID;
} }