User:Johnburger/Demo/Exec/Alloc/TSS
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 CALL
ed 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
) beforeHLT
is called. After all, that's what System Code is supposed to do - ensure that User Code can't get the system into strife!
- To protect against this scenario, the
- 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