Okay let me tell you all what I’ve done up to this point:
1. I have coded a __CreateProcess function. It accepts the base address of a process and its property flags to be specified. It will then add the process to the Process Queue and creates SS, DS, ES, FS, GS and the CS for the process. Segment Descriptors for all of the above mentioned segment selectors have the limit of 0x0000FFFF and the DPL of 0x03 (Users).
2. The scheduler in IRQ0 can pick a process depending on its priority and also has the process’s base address. Now I want to switch to that process while having all of the required information but as soon as I, for example, change the SS in the kernel to SS of the process I get a general protection fault.
Can anybody tell me why I am getting the general protection fault and for the love of god please someone point me to an article or something that REALLY explains how task switching is done? I DO know that stacks should be switched and values should be pushed into and popped off of it but the question is HOW exactly is that done? Or am I doing something wrong with switching? Should the stack of the process have the DPL of 0x00 just like the kernel’s stack?
Oh and yes I have read Intel Manuals and as a matter of fact, they are the only reference that I trust but they have not been helpful in this matter as much as they are supposed to be. I’d really appreciate it if somebody could explain these to me.
P.S: This is the code that I have written so far for my __CreateProcess function:
Code: Select all
; ——————————————————————————————————————————————————
__CreateProcess:
; DWORD __CreateProcess (DWORD CreationFlags, DWORD ProcessASCII, void* BaseAddress); StdCall;
; Returns the Process ID of the process
; Returns 0x00000000 (Zero) upon failure
PUSH EBX
PUSH ECX
PUSH EDX
PUSH ESI
PUSH EBP
MOV EBP , ESP
; [EBP + 0x18] ; CreationFlags
; [EBP + 0x1C] ; ProcessASCII
; [EBP + 0x20] ; BaseAddress
XOR EAX , EAX
MOV EBX , OFFSET PQDTR
MOV ECX , DWORD PTR [EBX]
; See if the length of the PQDT is divisible by the length of the processes' structure -1
TEST ECX , PROCESS_STRUCTURE_LENGTH_IN_BYTES - 1
JZ .Continue1
JMP .EP
.Continue1:
; See how many processes can be put into the QUEUE
SHR ECX , 0x00000007
TEST ECX , ECX
JNZ .Continue2
JMP .EP
.Continue2:
; *EBX = PQDT
MOV EBX , DWORD PTR [EBX + 0x04]
.FindFirstFreeEntry:
MOV EDX , DWORD PTR [EBX]
TEST EDX , EDX
JZ .FoundFreeSlot
JMP .FindNextFreeEntry
; DWORD#0 = ProcessID
; DWORD#1 = CreationFlags
; DWORD#2 = Original and current time slices
; DWORD#3 = ProcessASCII
.FoundFreeSlot:
MOV DWORD PTR [EBX] , ECX
MOV EAX , DWORD PTR [EBP + 0x18] ; EAX = CreationFlags
MOV DWORD PTR [EBX + 0x04] , EAX
; Time Slice Formula = (PIT Frequency/16) * (Priority+1)
; We will only keep time slices up to 65535
MOV ECX , DWORD PTR [PITFrequency]
SHR ECX , 0x00000004
; EAX = [CreationFlags], Low 4 bits are for the priority
AND EAX , 0x0000000F
INC EAX
MUL ECX
; EAX = TimeSlice, Keep the time slice that is 1 up to 65535
AND EAX , 0x0000FFFF
SHL EAX , 0x00000010
; Current Time Slice = Number of time slices given to the current process
; For a process that is just being created this number should be zero (0x0000)s
; EAX = DWORD#2 of the process
MOV DWORD PTR [EBX + 0x08] , EAX
; Put the process ASCII in its place
MOV EAX , DWORD PTR [EBP + 0x01C] ; ProcessASCII
MOV DWORD PTR [EBX + 0x0C] , EAX
; *EBX = PQDT
; [EBP + 0x18] ; CreationFlags
; [EBP + 0x1C] ; ProcessASCII
; [EBP + 0x20] ; BaseAddress
; Create the code segment for the process
; DWORD#0 of the Code Segment Descriptor in the Global Descriptor Table
MOV EAX , DWORD PTR [EBP + 0x20] ; BaseAddress
MOV EDX , EAX
SHL EAX , 0x00000010
OR EAX , 0x0000FFFF ; Code segment limit = 0xFFFF
; EAX = DWORD#0 of the Code Segment Descriptor (CSD)
MOV ESI , EDX
SHR ESI , 0x00000010
AND EDX , 0xFF000000
OR EDX , ESI
OR EDX , 0x0040FA00
; EDX = DWORD#1 of the Data Segment Descriptor
INVOKE __AddGDTDescriptor , OFFSET GDTR , EAX , EDX
MOV WORD PTR [EBX + 16] , AX
; Create the Data Segment Descriptor for the process (DS, SS, ES, FS, GS)
MOV EAX , DWORD PTR [EBP + 0x20] ; BaseAddress
MOV EDX , EAX
SHL EAX , 0x00000010
OR EAX , 0x0000FFFF
; EAX = DWORD#0 of the Data Segment Descriptor of the new process
MOV ESI , EDX
SHR ESI , 0x00000010
AND EDX , 0xFF000000
OR EDX , 0x0040F200
INVOKE __AddGDTDescriptor , OFFSET GDTR , EAX , EDX
; AX = The segment selector of the DS, Get it in the upper WORD of the AX register
; This way we can put DS, GS, SS, ES and etc at the same time
MOV EDX , EAX
SHL EDX , 0x00000010
OR EAX , EDX
MOV WORD PTR [EBX + 18] , AX ; DS
MOV DWORD PTR [EBX + 20] , EAX ; SS, ES
MOV DWORD PTR [EBX + 24] , EAX ; GS, FS
; Set the EIP of the process to zero (The base address of the segment takes care of that)
XOR EAX , EAX
MOV DWORD PTR [EBX + 28] , EAX
; Set the general purpose registers to 0x00000000
XOR EAX , EAX
MOV DWORD PTR [EBX + 32] , EAX ; EAX
MOV DWORD PTR [EBX + 36] , EAX ; EBX
MOV DWORD PTR [EBX + 40] , EAX ; ECX
MOV DWORD PTR [EBX + 34] , EAX ; EDX
MOV DWORD PTR [EBX + 48] , EAX ; ESI
MOV DWORD PTR [EBX + 52] , EAX ; EDI
MOV DWORD PTR [EBX + 56] , EAX ; EBP
MOV DWORD PTR [EBX + 60] , 0x0000FFFF ; ESP
PUSHFD
POP EAX
MOV DWORD PTR [EBX + 64] , EAX ; EFLAGS
MOV EDX , DWORD PTR [EBP + 0x20]
MOV DWORD PTR [EBX + 68] , EDX ; BaseAddress
MOV EAX , EDX
INC DWORD PTR [ProcessesCount]
JMP .EP
.FindNextFreeEntry:
ADD EBX , PROCESS_STRUCTURE_LENGTH_IN_BYTES
DEC ECX
JNZ .FindFirstFreeEntry
.EP:
POP EBP
POP ESI
POP EDX
POP ECX
POP EBX
RET 0x0C
; ——————————————————————————————————————————————————