Make far calls restartable when they trigger a fault pushing the return address onto the stack
This commit is contained in:
parent
b944cff3b5
commit
b0ada4229d
4 changed files with 68 additions and 31 deletions
|
|
@ -1397,6 +1397,7 @@ X86CPU.prototype.resetRegs = function()
|
||||||
* currently opLIP is updated prior to every instruction, but opLSP is updated only for instructions
|
* currently opLIP is updated prior to every instruction, but opLSP is updated only for instructions
|
||||||
* that read/write the stack (eg, RETF) and should otherwise remain set to X86.ADDR_INVALID.
|
* that read/write the stack (eg, RETF) and should otherwise remain set to X86.ADDR_INVALID.
|
||||||
*/
|
*/
|
||||||
|
this.opCS = -1;
|
||||||
this.opLIP = this.opLSP = X86.ADDR_INVALID;
|
this.opLIP = this.opLSP = X86.ADDR_INVALID;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -558,21 +558,20 @@ X86.fnCALLw = function(dst, src)
|
||||||
X86.fnCALLF = function(off, sel)
|
X86.fnCALLF = function(off, sel)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Originally, we would snapshot regLSP into opLSP because setCSIP() could trigger a segment fault,
|
* Since we always push the return address AFTER calling setCSIP(), and since either push could trigger
|
||||||
* but additionally, the stack segment could trigger either a segment fault or a page fault; indeed,
|
* fault (eg, segment fault, page fault, etc), we must not only snapshot regLSP into opLSP, but also the
|
||||||
* any operation that performs multiple stack modifications must take this precaution and snapshot regLSP.
|
* current CS into opCS, so that fnFault() can make this CALL restartable.
|
||||||
*/
|
*/
|
||||||
|
this.opCS = this.getCS();
|
||||||
this.opLSP = this.regLSP;
|
this.opLSP = this.regLSP;
|
||||||
|
|
||||||
var oldCS = this.getCS();
|
|
||||||
var oldIP = this.getIP();
|
var oldIP = this.getIP();
|
||||||
var oldSize = (I386? this.sizeData : 2);
|
var oldSize = (I386? this.sizeData : 2);
|
||||||
if (this.setCSIP(off, sel, true) != null) {
|
if (this.setCSIP(off, sel, true) != null) {
|
||||||
this.pushData(oldCS, oldSize);
|
this.pushData(this.opCS, oldSize);
|
||||||
this.pushData(oldIP, oldSize);
|
this.pushData(oldIP, oldSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.opLSP = X86.ADDR_INVALID;
|
this.opLSP = X86.ADDR_INVALID;
|
||||||
|
this.opCS = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -768,7 +767,7 @@ X86.fnShr64 = function(dst)
|
||||||
*
|
*
|
||||||
* This sets regMDLo to dstHi:dstLo / src, and regMDHi to dstHi:dstLo % src; all inputs are treated as unsigned.
|
* This sets regMDLo to dstHi:dstLo / src, and regMDHi to dstHi:dstLo % src; all inputs are treated as unsigned.
|
||||||
*
|
*
|
||||||
* If fMDset is not set, however, then there was a divide exception (ie, the divisor was either zero or too small).
|
* If fMDSet is not set, however, then there was a divide exception (ie, the divisor was either zero or too small).
|
||||||
*
|
*
|
||||||
* Refer to: http://lxr.linux.no/linux+v2.6.22/lib/div64.c
|
* Refer to: http://lxr.linux.no/linux+v2.6.22/lib/div64.c
|
||||||
*
|
*
|
||||||
|
|
@ -799,8 +798,8 @@ X86.fnDIV32 = function(dstLo, dstHi, src)
|
||||||
result += bit;
|
result += bit;
|
||||||
}
|
}
|
||||||
X86.fnShr64(div);
|
X86.fnShr64(div);
|
||||||
bit >>>= 1;
|
bit /= 2;
|
||||||
} while (bit);
|
} while (bit >= 1);
|
||||||
|
|
||||||
this.assert(result <= 0xffffffff && !rem[1]);
|
this.assert(result <= 0xffffffff && !rem[1]);
|
||||||
|
|
||||||
|
|
@ -814,7 +813,7 @@ X86.fnDIV32 = function(dstLo, dstHi, src)
|
||||||
*
|
*
|
||||||
* This sets regMDLo to dstHi:dstLo / src, and regMDHi to dstHi:dstLo % src; all inputs are treated as signed.
|
* This sets regMDLo to dstHi:dstLo / src, and regMDHi to dstHi:dstLo % src; all inputs are treated as signed.
|
||||||
*
|
*
|
||||||
* If fMDset is not set, however, then there was a divide exception (ie, the divisor was either zero or too small).
|
* If fMDSet is not set, however, then there was a divide exception (ie, the divisor was either zero or too small).
|
||||||
*
|
*
|
||||||
* Refer to: http://lxr.linux.no/linux+v2.6.22/lib/div64.c
|
* Refer to: http://lxr.linux.no/linux+v2.6.22/lib/div64.c
|
||||||
*
|
*
|
||||||
|
|
@ -3980,6 +3979,10 @@ X86.fnFault = function(nFault, nError, nCycles, fHalt)
|
||||||
* the current instruction contains an OPERAND size override).
|
* the current instruction contains an OPERAND size override).
|
||||||
*/
|
*/
|
||||||
this.resetSizes();
|
this.resetSizes();
|
||||||
|
if (this.opCS != -1) {
|
||||||
|
this.setCS(this.opCS);
|
||||||
|
this.opCS = -1;
|
||||||
|
}
|
||||||
this.setIP(this.opLIP - this.segCS.base);
|
this.setIP(this.opLIP - this.segCS.base);
|
||||||
if (this.opLSP != X86.ADDR_INVALID) {
|
if (this.opLSP != X86.ADDR_INVALID) {
|
||||||
this.setSP((this.regESP & ~this.segSS.maskAddr) | (this.opLSP - this.segSS.base));
|
this.setSP((this.regESP & ~this.segSS.maskAddr) | (this.opLSP - this.segSS.base));
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,9 @@ DSEG_PROT16 equ 0x0018
|
||||||
DSEG_PROT32 equ 0x0020
|
DSEG_PROT32 equ 0x0020
|
||||||
SSEG_PROT32 equ 0x0028
|
SSEG_PROT32 equ 0x0028
|
||||||
|
|
||||||
|
;
|
||||||
|
; We set our exception handlers at fixed addresses to simplify interrupt gate descriptor initialization.
|
||||||
|
;
|
||||||
OFF_INTDIVERR equ 0xe000
|
OFF_INTDIVERR equ 0xe000
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
@ -223,6 +226,10 @@ addrIDT:dw myIDTEnd - myIDT - 1 ; 16-bit limit of myIDT
|
||||||
myIDT: defGate CSEG_PROT32,OFF_INTDIVERR
|
myIDT: defGate CSEG_PROT32,OFF_INTDIVERR
|
||||||
myIDTEnd:
|
myIDTEnd:
|
||||||
|
|
||||||
|
addrIDTReal:
|
||||||
|
dw 0x3FF ; 16-bit limit of real-mode IDT
|
||||||
|
dd 0x00000000 ; 32-bit base address of real-mode IDT
|
||||||
|
|
||||||
initGDT:
|
initGDT:
|
||||||
%ifdef RAM_GDT
|
%ifdef RAM_GDT
|
||||||
set edi,RAM_GDT
|
set edi,RAM_GDT
|
||||||
|
|
@ -746,7 +753,8 @@ printVal:
|
||||||
TYPE_ARITH equ 0
|
TYPE_ARITH equ 0
|
||||||
TYPE_ARITH1 equ 1
|
TYPE_ARITH1 equ 1
|
||||||
TYPE_LOGIC equ 2
|
TYPE_LOGIC equ 2
|
||||||
TYPE_MULDIV equ 3
|
TYPE_MULTIPLY equ 3
|
||||||
|
TYPE_DIVIDE equ 4
|
||||||
|
|
||||||
SIZE_BYTE equ 0
|
SIZE_BYTE equ 0
|
||||||
SIZE_SHORT equ 1
|
SIZE_SHORT equ 1
|
||||||
|
|
@ -816,18 +824,32 @@ tableOps:
|
||||||
defOp "DEC",dec,al,none,none,TYPE_ARITH1
|
defOp "DEC",dec,al,none,none,TYPE_ARITH1
|
||||||
defOp "DEC",dec,ax,none,none,TYPE_ARITH1
|
defOp "DEC",dec,ax,none,none,TYPE_ARITH1
|
||||||
defOp "DEC",dec,eax,none,none,TYPE_ARITH1
|
defOp "DEC",dec,eax,none,none,TYPE_ARITH1
|
||||||
defOp "IMULA",imul,dl,none,none,TYPE_MULDIV
|
defOp "MULA",mul,dl,none,none,TYPE_MULTIPLY
|
||||||
defOp "IMULA",imul,dx,none,none,TYPE_MULDIV
|
defOp "MULA",mul,dx,none,none,TYPE_MULTIPLY
|
||||||
defOp "IMULA",imul,edx,none,none,TYPE_MULDIV
|
defOp "MULA",mul,edx,none,none,TYPE_MULTIPLY
|
||||||
defOp "IMUL",imul,ax,dx,none,TYPE_MULDIV
|
defOp "IMULA",imul,dl,none,none,TYPE_MULTIPLY
|
||||||
defOp "IMUL",imul,eax,edx,none,TYPE_MULDIV
|
defOp "IMULA",imul,dx,none,none,TYPE_MULTIPLY
|
||||||
defOp "IMUL8",imul,ax,dx,0x77,TYPE_ARITH1
|
defOp "IMULA",imul,edx,none,none,TYPE_MULTIPLY
|
||||||
defOp "IMUL8",imul,ax,dx,-0x77,TYPE_ARITH1
|
defOp "IMUL",imul,ax,dx,none,TYPE_MULTIPLY
|
||||||
defOp "IMUL8",imul,eax,edx,0x77,TYPE_ARITH1
|
defOp "IMUL",imul,eax,edx,none,TYPE_MULTIPLY
|
||||||
defOp "IMUL8",imul,eax,edx,-0x77,TYPE_ARITH1
|
defOp "IMUL8",imul,ax,dx,0x77,TYPE_MULTIPLY
|
||||||
defOp "IMUL16",imul,ax,0x777,none,TYPE_ARITH1
|
defOp "IMUL8",imul,ax,dx,-0x77,TYPE_MULTIPLY
|
||||||
defOp "IMUL32",imul,eax,0x777777,none,TYPE_ARITH1
|
defOp "IMUL8",imul,eax,edx,0x77,TYPE_MULTIPLY
|
||||||
defOp "IDIVA",idiv,dl,none,none,TYPE_MULDIV
|
defOp "IMUL8",imul,eax,edx,-0x77,TYPE_MULTIPLY
|
||||||
|
defOp "IMUL16",imul,ax,0x777,none,TYPE_MULTIPLY
|
||||||
|
defOp "IMUL32",imul,eax,0x777777,none,TYPE_MULTIPLY
|
||||||
|
defOp "DIVDL",div,dl,none,none,TYPE_DIVIDE
|
||||||
|
defOp "DIVDX",div,dx,none,none,TYPE_DIVIDE
|
||||||
|
defOp "DIVEDX",div,edx,none,none,TYPE_DIVIDE
|
||||||
|
defOp "DIVAL",div,al,none,none,TYPE_DIVIDE
|
||||||
|
defOp "DIVAX",div,ax,none,none,TYPE_DIVIDE
|
||||||
|
defOp "DIVEAX",div,eax,none,none,TYPE_DIVIDE
|
||||||
|
defOp "IDIVDL",idiv,dl,none,none,TYPE_DIVIDE
|
||||||
|
defOp "IDIVDX",idiv,dx,none,none,TYPE_DIVIDE
|
||||||
|
defOp "IDIVEDX",idiv,edx,none,none,TYPE_DIVIDE
|
||||||
|
defOp "IDIVAL",idiv,al,none,none,TYPE_DIVIDE
|
||||||
|
defOp "IDIVAX",idiv,ax,none,none,TYPE_DIVIDE
|
||||||
|
defOp "IDIVEAX",idiv,eax,none,none,TYPE_DIVIDE
|
||||||
db 0
|
db 0
|
||||||
|
|
||||||
align 4
|
align 4
|
||||||
|
|
@ -836,7 +858,8 @@ typeMasks:
|
||||||
dd PS_ARITH
|
dd PS_ARITH
|
||||||
dd PS_ARITH
|
dd PS_ARITH
|
||||||
dd PS_LOGIC
|
dd PS_LOGIC
|
||||||
dd PS_MULDIV
|
dd PS_MULTIPLY
|
||||||
|
dd PS_DIVIDE
|
||||||
|
|
||||||
arithValues:
|
arithValues:
|
||||||
.bvals: dd 0x00,0x01,0x02,0x7E,0x7F,0x80,0x81,0xFE,0xFF
|
.bvals: dd 0x00,0x01,0x02,0x7E,0x7F,0x80,0x81,0xFE,0xFF
|
||||||
|
|
@ -881,7 +904,14 @@ typeValues:
|
||||||
dd ARITH_BYTES+ARITH_WORDS+ARITH_DWORDS,arithValues,ARITH_BYTES+ARITH_WORDS+ARITH_DWORDS,arithValues
|
dd ARITH_BYTES+ARITH_WORDS+ARITH_DWORDS,arithValues,ARITH_BYTES+ARITH_WORDS+ARITH_DWORDS,arithValues
|
||||||
dd 0,0,0,0
|
dd 0,0,0,0
|
||||||
;
|
;
|
||||||
; Values for TYPE_MULDIV (a superset of ARITH values)
|
; Values for TYPE_MULTIPLY (a superset of ARITH values)
|
||||||
|
;
|
||||||
|
dd MULDIV_BYTES,muldivValues,MULDIV_BYTES,muldivValues
|
||||||
|
dd MULDIV_BYTES+MULDIV_WORDS,muldivValues,MULDIV_BYTES+MULDIV_WORDS,muldivValues
|
||||||
|
dd MULDIV_BYTES+MULDIV_WORDS+MULDIV_DWORDS,muldivValues,MULDIV_BYTES+MULDIV_WORDS+MULDIV_DWORDS,muldivValues
|
||||||
|
dd 0,0,0,0
|
||||||
|
;
|
||||||
|
; Values for TYPE_DIVIDE
|
||||||
;
|
;
|
||||||
dd MULDIV_BYTES,muldivValues,MULDIV_BYTES,muldivValues
|
dd MULDIV_BYTES,muldivValues,MULDIV_BYTES,muldivValues
|
||||||
dd MULDIV_BYTES+MULDIV_WORDS,muldivValues,MULDIV_BYTES+MULDIV_WORDS,muldivValues
|
dd MULDIV_BYTES+MULDIV_WORDS,muldivValues,MULDIV_BYTES+MULDIV_WORDS,muldivValues
|
||||||
|
|
@ -899,8 +929,8 @@ intDivErr:
|
||||||
pop esi
|
pop esi
|
||||||
;
|
;
|
||||||
; It's rather annoying that the 80386 treats #DE as a fault rather than a trap, leaving CS:EIP pointing to the
|
; It's rather annoying that the 80386 treats #DE as a fault rather than a trap, leaving CS:EIP pointing to the
|
||||||
; faulting instruction. So we must "patch" the EIP on the stack to point to a RET; it's easier to use our own RET
|
; faulting instruction instead of the RET we conveniently placed after it. So, instead of trying to calculate where
|
||||||
; rather than figuring out how long the DIV instruction is.
|
; that RET is, we simply set EIP on the stack to point to our own RET.
|
||||||
;
|
;
|
||||||
mov dword [esp],intDivRet
|
mov dword [esp],intDivRet
|
||||||
iretd
|
iretd
|
||||||
|
|
@ -914,14 +944,16 @@ doneProt:
|
||||||
|
|
||||||
%ifndef REAL32
|
%ifndef REAL32
|
||||||
;
|
;
|
||||||
; Return to real-mode now, after first loading CS with a 16-bit code segment
|
; Return to real-mode, after first resetting the IDTR and loading CS with a 16-bit code segment
|
||||||
;
|
;
|
||||||
|
o32 lidt [cs:addrIDTReal]
|
||||||
jmp CSEG_PROT16:toProt16
|
jmp CSEG_PROT16:toProt16
|
||||||
toProt16:
|
toProt16:
|
||||||
bits 16
|
bits 16
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
goReal: mov eax,cr0
|
goReal:
|
||||||
|
mov eax,cr0
|
||||||
and eax,~(CR0_MSW_PE | CR0_PG) & 0xffffffff
|
and eax,~(CR0_MSW_PE | CR0_PG) & 0xffffffff
|
||||||
mov cr0,eax
|
mov cr0,eax
|
||||||
jmpReal:
|
jmpReal:
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ PS_DF equ 0x0400
|
||||||
PS_OF equ 0x0800
|
PS_OF equ 0x0800
|
||||||
PS_ARITH equ (PS_CF | PS_PF | PS_AF | PS_ZF | PS_SF | PS_OF)
|
PS_ARITH equ (PS_CF | PS_PF | PS_AF | PS_ZF | PS_SF | PS_OF)
|
||||||
PS_LOGIC equ (PS_CF | PS_PF | PS_ZF | PS_SF | PS_OF)
|
PS_LOGIC equ (PS_CF | PS_PF | PS_ZF | PS_SF | PS_OF)
|
||||||
PS_MULDIV equ (PS_CF | PS_OF)
|
PS_MULTIPLY equ (PS_CF | PS_OF) ; only CF and OF are "defined" following MUL or IMUL
|
||||||
|
PS_DIVIDE equ 0 ; none of the Processor Status flags are "defined" following DIV or IDIV
|
||||||
|
|
||||||
CR0_MSW_PE equ 0x0001
|
CR0_MSW_PE equ 0x0001
|
||||||
CR0_PG equ 0x80000000 ; set if paging enabled
|
CR0_PG equ 0x80000000 ; set if paging enabled
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue