User:Johnburger/Demo/Exec/Alloc/TSS

From OSDev Wiki
Jump to navigation Jump to search

This module provides two routines to Allocate a new TSS.

Allocating the memory for a new TSS is easy, but the code assumes that the TSS will need some tailoring done to it before it will become a true TSS. For that reason Exec.Alloc.TSS returns a Data Segment, and expects Exec.Alloc.TSS.Enable to be CALLed when finished so that it can convert it to a true TSS.

You'll note that the code specifies that it initialises three significant fields: EFlags, IOMap and LDT. The last two are obvious - so why is EFlags important?

The two bits that are initialised are the Always-1 bit (which isn't strictly necessary: even if it was 0, it would be set to 1 by the CPU) - and the Interrupt Flag (IF) bit. That means that this Task starts with interrupts enabled. What would happen if it started with them disabled? Since a User Task can't use the CLI or STI instructions, it would effectively mean that the User Task would be running with interrupts permanently disabled: not a good thing for an OS! Try it!

  • If the User Code uses User.Halt to wait for its timeslice to end, then the CPU will be HALTed with interrupts disabled - effectively zombied. VMWare detects this and stops the emulation. VirtualBox just locks up.
    • To protect against this scenario, the User.Halt system implementation should ensure that interrupts are enabled (STI) before HLT is called. After all, that's what System Code is supposed to do - ensure that User Code can't get the system into strife!
  • If the User Code uses User.Yield to end its timeslice prematurely, then the next Task will get its turn - but with no IRQs, neither the Timer nor the Keyboard can interrupt the system. Runaway!
  • If the User Code simply does nothing to yield, or just twiddles its thumbs between bouncing updates, then it will be the only task that ever runs - very, very fast!

Another thing to try here is to add | x86.EFlags.TF That will start the Task with Single Step (Trace Flag (TF)) on, and nothing will happen in the task until <Space> is pressed - which will only execute one instruction and then pause again. Pressing <Enter> will remove the Trace Flag and allow the Task to run normally.

Demo/Exec/Alloc/TSS.inc

;
; Exec/Alloc/TSS.inc
;

; This module provides functions to allocate and configure Task State Segments.

;-------------------------------------------------------------------------------
; This function allocates sufficient RAM for a TSS, and fills in the most
; important entries: EFlags, IOMap and LDT.
;
; It then returns a Data descriptor, ready for more customisation. To actually
; turn it into a TSS, call Exec.Alloc.TSS.Enable.
;
; Input:  AX  = LDT to use for the TSS
; Output: AX  = GDT Descriptor for (future) TSS, or zero on error
;         ES  = GDT Descriptor unless AX is zero
;         EAX = Memory Base
;         ECX = Memory Size
Exec.Alloc.TSS:

Exec.TSS.EFlags EQU             x86.EFlags.IF | x86.EFlags.1 ; Enable interrupts

%push Alloc.TSS ; Let's not leave these %defines just lying around...
%define         %$LDT           EBP - 4
%define         %$Base          EBP - 8
%define         %$Size          EBP - 12

                ENTER           12, 0

                MOV             [%$LDT],EAX

                MOV             ECX,x86.TSS_size ; Size to allocate
                CALL            Exec.Alloc.RAM
                TEST            EAX,EAX          ; Any left?
                JZ              .End             ; Pity that!

                MOV             [%$Base],EAX
                MOV             [%$Size],ECX

                MOV             DL,Type.Mem(Data, DPL0, RW)
                MOV             DH,Gran.Mem(Byte, Small)
                CALL            Exec.Alloc.GDT.Mem ; Get Descriptor to use
                TEST            EAX,EAX          ; Any left?
                JZ              .End             ; Pity that! **** Memory leak! ****

                MOV             ES,AX            ; Fresh Descriptor!
                MOV             ECX,[%$Size]     ; Number bytes to Zero
                XOR             EDI,EDI          ; Zero everything
                CALL            Exec.Alloc.RAM.Zero

                MOV             AX,[%$LDT]
                MOV             ECX,[%$Size]
                MOV             [ES:x86.TSS.LDT],AX
                MOV             [ES:x86.TSS.IOMap],CX
                MOV    DWORD    [ES:x86.TSS.EFlags],Exec.TSS.EFlags

                MOV             EAX,[%$Base]
.End:
                LEAVE
%pop
                RET

;-------------------------------------------------------------------------------
; This function converts a Segment previously allocated with Alloc.TSS (above)
; into a true TSS. Note that as soon as this happens, the TSS is a candidate for
; switching.
;
; Input:  ES  = Descriptor to modify
;         AH  = System.TSS if TSS is to be a System TSS. Zero otherwise.
; Output: EBX = TSS Descriptor
;         ES modified
Exec.Alloc.TSS.Enable:

; Note that I could use GS: overrides to access the GDT, but ES points to the
; soon-to-be TSS - a Bad Thing. This fixes that!
                MOV             EBX, ES         ; Get Descriptor to modify

                PUSH            GS              ; Get GDT into ES
                POP             ES

                MOV             [ES:EBX+x86.Desc.Type],BYTE Type.Sys(TSS, DPL0, 386)
                OR              [ES:EBX+x86.Desc.Mem.Gran],AH ; OR in System.TSS

                RET