Install the complex Debug handler, which (luckily) doesn't require its own context to run. In fact, it's humorous to play with the Debugger display while running in the context of the running Task!
Also, this is a perfect example of the difference between Descriptor Privilege Level (DPL) and Requested Privilege Level (RPL):
- If code wants to access a Selector, it must Request a Privilege Level as part of the Selector - the lowest two bits. The CPU will check that RPL against the Current Privilege Level (CPL) - which is the lowest two bits of the current Code Segment (
CS). If the RPL is invalid for the current CPL, that's a GPF.
- Assuming the CPL check succeeds, it then checks against the Privilege Level recorded in the Descriptor - the DPL. If that's less than the CPL, then that's also a GPF.
So what we have here is the Debugger code that resides at Ring0. To access that code requires an RPL of 0, but that implies that the User-mode Task cannot invoke the Debugger. That may be what you want - but what if it isn't? How do you let a User-mode Task call the Debugger?
That's what Trap Gates can do. The Trap Gate doesn't need the same DPL as the Selector that it calls: it can have its own DPL. Protection is maintained since it does need to use the correct RPL for the Selector it wants to call - it's assumed that the Descriptor Table Entries are correct and define what the system designer wants!
; ; Exec/Ints/Debug.inc ; ; This module installs a Debug handler Exec.Ints.Debug: MOV EAX,Ints.Debug MOV EBX,IDT.Debug MOV CX,Selector(GDT.Ints, GDT, RPL0) ; Note needs to RPL0 MOV DL,Type.Sys(Trap, DPL3, 386) ; Allow User-mode Programs CALL Exec.Alloc.IDT RET