User:Johnburger/Demo/Ints/NoSeg
This is a simple example of what can be done with various Fault handlers in the system.
In this case, if a Descriptor in one of the Descriptor tables has its .Type.Present
bit zero, the CPU will raise a Segment Not Present exception so that the system can do something about it.
Testing
I have marked some of the User Task segments as Not Present, to invoke this handler. They're fully set up, so all that this handler has to do is mark the Descriptor as Present and return.
Not Stack Segments
A caution: this handler is not called for Stack Segments that are marked as Not Present. Since the fault revolves around a Stack, the CPU doesn't trust the stack it currently has and calls the Stack Fault exception handler instead. Intel recommends that that fault be handled by a full-blown Task rather than a mere routine.
Surprise!
And this is why I wrote this Demonstrator! Different environments (unsurprisingly) behave differently. It works in VMware, but not in VirtualBox, nor on a Core i7 directly. What happens is this:
- The TSS has one Descriptor marked as Not Present - say
CS
. - When the TSS is switched to, the CPU starts to load and validate the registers from the TSS.
- It notes that
CS
is Not Present, and calls this handler with an Error Code for the Selector. - This handler sets the Descriptor's
.Present
flag. - As this handler returns, it
POP
sCS
, which re-loads the (now-present) Descriptor. - The new Task resumes - but there's a problem! The TSS load was incomplete: it was interrupted by the Not Present Fault.
ES
in the TSS has a valid Selector (in the User Task case, a Selector to the Screen segment), but the actual Segment Register still has a zero in it! - The first attempt to use ES faults with a GPF.
Demo/Ints/NoSeg.inc
;
; Ints/NoSeg.inc
;
; This module gives an example of a "No Segment" Fault handler.
; Of course, a real handler would have to load the missing Segment from
; somewhere - this one merely assumes that we "forgot" to mark it as .Present,
; fixes it, and resumes.
Ints.NoSeg: ; Oops, forgot to mark as Present!
; PUSH 0 ; PUSH Error Code - no, already there!
PUSH EBX
PUSH DS
MOV BX,Selector(GDT.VGA, GDT, RPL0)
MOV DS,BX
INC BYTE [0016h] ; Dingle(tm) on screen - show we've done it1
MOV EBX,[ESP+8] ; Get error code
AND EBX,x86.Seg.TI ; Isolate which DT it's in
OR BX,DT.Alias ; Same Alias position in all DTs
MOV DS,BX ; Point to relevant DT Alias
MOV EBX,[ESP+8] ; Get error code (again)
AND BX,x86.Seg.Index ; Ignore extra bits
OR BYTE [BX+x86.Desc.Type],x86.Desc.Type.Present ; 16-bit index!
POP DS
POP EBX
ADD ESP,4 ; Skip Error code
IRETD ; And restart instruction