User:Johnburger/Demo/Ints/NoSeg

From OSDev Wiki
Jump to navigation Jump to search

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 POPs CS, 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