; ********************************************************** ; Name: Task Switcher ; Autor: Toaster Burger ; Version: 1.00 ; Date: 26.11.2005 ; last Update: 26.11.2005 ; see document: ToasterOS.pdf ; ********************************************************** [bits 32] CPU 386 %define Type_System %include "interface.asm" %define Handle_Size 6 org Task_Switcher jmp dword Enable_Scheduler jmp dword Create_Task jmp dword Delete_Task jmp dword Destroy_Task jmp dword Check_Task_Handle jmp dword Dispatcher jmp dword Finite jmp dword Create_Task_System jmp dword Switch_Task jmp dword Get_Current_Process Enable_Scheduler: ; enable (start) the Scheduler ; API Enable_Scheduler Enter_System_Environment ; command CPUID available (test bit 21 of eflags to set) ? pushfd or [esp],dword 1000000000000000000000b popfd pushfd pop eax test eax,1000000000000000000000b jz No_FPU_MMX_SSE2_support ; check if the FPU, MMX, SSE (re)store command is available mov eax,1 CPU PentiumPro cpuid CPU 386 test edx,1000000000000000000000000b jz No_FPU_MMX_SSE2_support jmp Write_Task_Table No_FPU_MMX_SSE2_support: ; if here the fxsave/fxrstor opcodes are not supported, so delete them mov [Store_FPU_MMX_SSE_state],dword 90909090h mov [Store_FPU_MMX_SSE_state+4],word 9090h mov [Store_FPU_MMX_SSE_state+6],byte 90h mov [Restore_FPU_MMX_SSE_state],dword 90909090h mov [Restore_FPU_MMX_SSE_state+4],word 9090h mov [Restore_FPU_MMX_SSE_state+6],byte 90h Write_Task_Table: mov [Current_Task],dword Task_Table xor ecx,ecx ; loop counter = 65536 xor eax,eax mov edi,Task_Table rep stosw ; erase all words rep stosd ; erase all dwords Leave_System_Environment ret Create_Task_System: ; API Create_Task_System, Handle, Process, Stack, Address, Data, Size ; Handle = Process Handle (of the caller's one) ; Process = Process Handle of the Process to start the Task ; Stack = start stack (esp) of the new task (the value stack is from high to low) ; Address = start address (eip) of the new task ; Size = bytes to copy from Data (source) to the new Tasks stack (destination) ; NOTES: ; The Data is bytewise copied to the stack from low to high, and the new Task's ; stack pointer will point BEFORE the data. ; This function adds to the data the Task Handle, so esp+0 points to the Handle ; and esp+4 to the user sepcific data. ; The caller won't get the Handle. Enter_System_Environment ; set the correct Stack Pointer mov eax,[Param6] sub [Param3],eax ; search a free Task Handle mov esi,Task_Table xor ecx,ecx ; loop counter = 65536 Enter_Multitasking MT_Task_Switcher ; coordinate Multitasking access (enter) Search_free_System_Task: add esi,02h lodsd or eax,eax jz Task_System_found loop Search_free_System_Task ; if here, there is no free Task Handle Leave_Multitasking MT_Task_Switcher ; coordinate Multitasking access (leave) stc mov eax,No_free_Task jmp Create_Task_System_Exit Task_System_found: ; if comes here, a free Task Handle is found Leave_Multitasking MT_Task_Switcher ; coordinate Multitasking access (leave) lea edi,[esi-06h] ; store the Process Handle mov ax,[Param2] stosw ; store the Stack Pointer mov eax,[Param3] sub eax,12+(4*5)+32+108 stosd ; set the stack for the new process on the current stack ; push following values in the correct direction: ; gs, fs, es, ds, ad, FPU, iret, Handle (4, 4, 4, 4, 32, 108, 12, 4) sub esp,12+(4*5)+32+108 mov edi,esp mov ebx,esp ; set gs, fs, es, ds xor eax,eax stosd stosd mov eax,Data_Selector stosd stosd ; set edi, esi, ebp & esp (all points to the start of stack) mov eax,[Param3] stosd stosd stosd stosd ; xor eax, ebx, ecx & edx xor eax,eax stosd stosd stosd stosd ; FPU registers (use current) fsave [edi] add edi,108 ; store the iret (eip, cs, flags) mov eax,[Param4] stosd mov eax,Code_Selector stosd mov eax,User_EFLAG stosd ; store the Task Handle xor eax,eax sub eax,ecx ; 65536 - loop counter stosd ; check whether the data is to copy (or not) cmp [Param6],dword 0 je Copy_System_finished ; copy the data API Copy_Process_Data, [Param1], [Param2], [Param5], [Param3], [Param6] jc Create_Task_System_Exit Copy_System_finished: ; copy the (system) stack sub [Param3],dword (12+(4*5)+32+108) API Copy_Process_Data, [Param1], [Param2], ebx, [Param3], (12+(4*5)+32+108) ; reset the stack (delete temporary other process stack) add esp,12+(4*5)+32+108 Create_Task_System_Exit: Leave_System_Environment ret Create_Task: ; API Create_Task, Handle, Process, Stack, Address, Data, Size ; Handle = Process Handle (of the caller's one) ; Process = Process Handle of the Process to start the Task ; Stack = start stack (esp) of the new task (the value stack is from high to low) ; Address = start address (eip) of the new task ; Size = bytes to copy from Data (source) to the new Tasks stack (destination) ; NOTES: ; The Data is bytewise copied to the stack from low to high, and the new Task's ; stack pointer will point BEFORE the data. ; This function adds to the data the Task Handle, so esp+0 points to the Handle ; and esp+4 to the user sepcific data. ; The caller won't get the Handle. Enter_System_Environment ; set the correct Stack Pointer mov eax,[Param6] sub [Param3],eax ; search a free Task Handle mov esi,Task_Table xor ecx,ecx ; loop counter = 65536 Enter_Multitasking MT_Task_Switcher ; coordinate Multitasking access (enter) Search_free_Task: add esi,02h lodsd or eax,eax jz Task_found loop Search_free_Task ; if here, there is no free Task Handle Leave_Multitasking MT_Task_Switcher ; coordinate Multitasking access (leave) stc mov eax,No_free_Task jmp Create_Task_Exit Task_found: ; if comes here, a free Task Handle is found Leave_Multitasking MT_Task_Switcher ; coordinate Multitasking access (leave) lea edi,[esi-06h] ; store the Process Handle mov ax,[Param2] stosw ; store the Stack Pointer mov eax,[Param3] sub eax,12+(4*5)+32+108 stosd ; set the stack for the new process on the current stack ; push following values in the correct direction: ; gs, fs, es, ds, ad, FPU, iret, Handle (4, 4, 4, 4, 32, 108, 12, 4) sub esp,12+(4*5)+32+108 mov edi,esp mov ebx,esp ; set gs, fs, es, ds xor eax,eax stosd stosd mov eax,Data_Selector_User stosd stosd ; set edi, esi, ebp & esp (all points to the start of stack) mov eax,[Param3] stosd stosd stosd stosd ; xor eax, ebx, ecx & edx xor eax,eax stosd stosd stosd stosd ; FPU registers (use current) fsave [edi] add edi,108 ; store the iret (eip, cs, flags) mov eax,[Param4] stosd mov eax,Code_Selector_User stosd mov eax,User_EFLAG stosd ; store the Task Handle sub eax,ecx ; 65536 - loop counter stosd ; check whether the data is to copy (or not) cmp [Param6],dword 0 je Copy_finished ; copy the data API Copy_Process_Data, [Param1], [Param2], [Param5], [Param3], [Param6] jc Create_Task_Exit Copy_finished: ; copy the (system) stack sub [Param3],dword (12+(4*5)+32+108) API Copy_Process_Data, [Param1], [Param2], ebx, [Param3], (12+(4*5)+32+108) ; reset the stack (delete temporary other process stack) add esp,12+(4*5)+32+108 Create_Task_Exit: Leave_System_Environment ret Delete_Task: ; API Delete_Task, Handle ; Handle = Handle of the Task to delete; returned (on the stack) by Create_Task Enter_System_Environment ; edi = address of the Handle imul edi,[Param1],Handle_Size add edi,Task_Table xor eax,eax stosw stosd Leave_System_Environment ret Destroy_Task: ; API Destroy_Task, Task_ID ; (undocumented) Enter_System_Environment mov esi,Task_Table xor ecx,ecx ; loop counter = 65536 mov bx,[Param1] Destroy_free_Task: lodsw cmp ax,bx jne Destroy_free_Task_nt mov [esi-2],word 0 mov [esi],dword 0 Destroy_free_Task_nt: add esi,04h loop Destroy_free_Task Leave_System_Environment ret Finite: ; (undocumented) ; test register content ??? ; - esp ; test if current cr3 register/handle is in Task Table ??? Enter_MT dispatcher_state jmp Scheduler Dispatcher: Enter_MT dispatcher_state ; 1. store the current Task's state sub esp,108 fsave [esp] pushad mpush ds, es, fs, gs ; store special FPU, MMX and SSE state or do nop Store_FPU_MMX_SSE_state: CPU PentiumPro fxsave [FPU_MMX_SSE2_State] CPU 386 ; store the stack mov ebx,[Current_Task] mov [ebx+2],esp ; 2. execute the (internal) Scheduler Scheduler: add [Current_Task],dword Handle_Size cmp [Current_Task],dword Task_Table_End jl Scheduler_Next mov [Current_Task],dword Task_Table Scheduler_Next: mov ebx,[Current_Task] mov eax,[ebx+2] or eax,eax jz Scheduler ; restore the stack mov esp,[ebx+2] ; 3. restore the new Task's state movzx ecx,word [ebx] API Get_CR3, ecx mov cr3,eax ; restore special FPU, MMX and SSE state or do nop Restore_FPU_MMX_SSE_state: CPU PentiumPro fxrstor [FPU_MMX_SSE2_State] CPU 386 mpop ds, es, fs, gs popad frstor [esp] add esp,108 Leave_MT dispatcher_state ret Switch_Task: ; (undocumented) Enter_System ; set the return stack pushfd push cs push Switch_Task_ret ; switch the task call Dispatcher iret Switch_Task_ret: Leave_System ret Check_Task_Handle: ; API Check_Task_Handle, Handle, Process_Handle ; Handle = Handle of the Task to check of validity; returned (on the stack) by Create_Task ; Process Handle = Process Handle of the Task (have to be the same as by Create_Task) Enter_System_Environment ; ebx = address of the Handle imul ebx,[Param1],Handle_Size add edi,Task_Table ; check the boundarys cmp [Param1],dword Task_Table_End jnc Check_Task_Handle_Invalid ; check if the Task_Handle(current).Process_Handle is euqal to the as parameter's one mov ax,[ebx] cmp ax,[Param2] jne Check_Task_Handle_Invalid ; esp = 0 ? cmp [ebx+2],TPointer NULL je Check_Task_Handle_Invalid ; Process Handle valid? API Check_Process_Handle, TProcessHandle [Param2] jc Check_Task_Handle_Exit xor eax,eax clc jmp Check_Task_Handle_Exit Check_Task_Handle_Invalid: stc mov eax,Invalid_Handle Check_Task_Handle_Exit: Leave_System_Environment ret Get_Current_Process: ; (undocumented) Enter_System mov esi,[Current_Task] movzx eax,word [esi] Leave_System ret ; Current_Task is a direct pointer to the current Task into the Task Table Current_Task dd 0 FPU_MMX_SSE dd "no" dispatcher_state db 0 ; the Task Table contains information about the Tasks, the descriptor format: ; Process Handle (word) ; esp (dword) ; ------------------------------ ; a Task description 6 Bytes