Multitasking Issues
first of all, you should probably do the send_eoi part before you actually handle the interrupt (this way you know it will be done every time the interrupt fires)
second, unless you update esp0 in the TSS, the kernel stack will be 'reset' every time you switch from CPL 3 to CPL 0, so you dont have to worry about leaving stuff on the stack
third, you might want to save the registers from the old thread before you start a new one
second, unless you update esp0 in the TSS, the kernel stack will be 'reset' every time you switch from CPL 3 to CPL 0, so you dont have to worry about leaving stuff on the stack
third, you might want to save the registers from the old thread before you start a new one
Oh wait a minute! Am I supposed to change the CS/EIP/EFLAGS of the current IRQ0Handler as soon as I pick a process from the process queue? So when for example, IRQ0 Handler is called and no process is found in the process queue, the IRQ0 handler will quitely return to where it has to but if a process is found, I will just change the return address CS:EIP/EFLAGS of the IRQ0 handler so that when at the end of the IRQ0 Handler I issue the IRET instruction, the code will be redirected to the picked process. Is that right? Could somebody confirm this please?
Aali,
Oh I get it now. Although is it good to leave ESP0 hanging without being initialized in the TSS?
Aali,
Oh I get it now. Although is it good to leave ESP0 hanging without being initialized in the TSS?
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
(There have been some more posts since I started this one, but I've spent all this time typing now... )
First of all, about EOI: you should issue it before switching to anything else. Be sure to keep interrupts disabled to prevent the interrupt from re-entering prematurely.
Now, what I do:
4) The scheduler calls a function to switch stacks.
5) That function pushes all registers it needs to preserve for next time the thread is switched to (saving them to a task structure is also an option). In your case, this should be all registers that weren't pushed in your step (2) (because your thread/process expects them to be preserved) plus whatever registers your IRQ handler expects to be preserved after you return to it. The only registers that may need special handling are SS:ESP, since saving the stack pointer on the stack makes it hard to find afterwards . (This is not an issue if all registers are stored into a special task structure).
6) The function actually loads a new SS:ESP from wherever they were saved for the next thread, and loads all other preserved registers.
7) The function returns, automatically using the return address from the new stack. This hands control over to the thread being switched to.
To create a new ring 3 process:
1) Allocate a kernel stack
2) Set up the kernel stack (from the top) like this:
* IretData
* Address of a stub function that perhaps clears some more registers and then performs an IRET(D). (Will be used as return address by task switch function)
* Whatever data the task-switch function pushes onto the stack and expects to be present on the new stack (none if all registers are saved to a task structure).
3) Initialize a task structure with any registers saved into it by the task switch procedure (and whatever else it contains, obviously). Be sure to set SS:ESP to point to the lowest initialized entry in the new kernel stack (the last-pushed register or (if none) the return address).
4) Perform whatever other actions need to be done to set up a new process (address space initialization etc.)
5) Add it to the scheduler queue
When this new task is switched to, it'll initially be in ring 0. The task switch function treats it like it would any other task, "restoring" register values and "returning" to the stub function set up in step 3 above, which is then free to perform some last-minute register initialization and perform an IRETD to jump to the ring 3 code.
When you switch to a thread that has already run, the task switch function restores the register values and returns to whatever code called the task switch function last time the thread was run. If that was an interrupt handler it can then restore registers to the values expected by the user process (or kernel thread) interrupted and execute an IRETD to return control to it.
So far so good.XCHG wrote:Combuster,
That information really helped. Thank you so much.
Alrighty! I created a Call Gate in my GDT for the first time and actually demonstrated the fact that the control is given to my process. What I am confused about is this: suppose I am now in the kernel and IRQ0 is fired:
1) IRQ0 is fired and my IRQ0Handler procedure takes control.
2) In my IRQ0Handler, at the beginning, I am creating the stack frame by pushing all the registers that I know I am going to be destroying, into the stack. Therefore, the kernel's stack is now altered.
3) Now my scheduler, which resides in IRQ0Handler, picks one process up from the Process Queue and has now access to its structure that contains the process's CS/DS/ES/FS/GS/EIP/EFLAGS/Base Address and etc.
This is why I mentioned that was not how I was actually doing it, just an example of how IRET works.4) Now my scheduler in IRQ0Handler, pushes the process's SS, ESP, CS, EIP and EFLAGS respectively into the kernel's stack and issues an IRET. This IRET is issued before my IRQ0handler issues its main IRET that returns to the kernel. You see, I am issuing one IRET in order to switch to the process and one IRET is supposed to be issued at the end of the IRQ0Handler.
My problem and the confusion is that the IRET at the end of my IRQ0Handler will never be issued unless there are no proesses in the Process Queue to be given control to.
[snip]
In this case, You can see that if a process is picked to be given control to, the IRQ0Handler will never reach the .SendEOI label in order to pop the regisers it has already pushed into the kernel's stack. I am really confused. Is it just me or is multitasking complicated?
First of all, about EOI: you should issue it before switching to anything else. Be sure to keep interrupts disabled to prevent the interrupt from re-entering prematurely.
Now, what I do:
4) The scheduler calls a function to switch stacks.
5) That function pushes all registers it needs to preserve for next time the thread is switched to (saving them to a task structure is also an option). In your case, this should be all registers that weren't pushed in your step (2) (because your thread/process expects them to be preserved) plus whatever registers your IRQ handler expects to be preserved after you return to it. The only registers that may need special handling are SS:ESP, since saving the stack pointer on the stack makes it hard to find afterwards . (This is not an issue if all registers are stored into a special task structure).
6) The function actually loads a new SS:ESP from wherever they were saved for the next thread, and loads all other preserved registers.
7) The function returns, automatically using the return address from the new stack. This hands control over to the thread being switched to.
To create a new ring 3 process:
1) Allocate a kernel stack
2) Set up the kernel stack (from the top) like this:
* IretData
* Address of a stub function that perhaps clears some more registers and then performs an IRET(D). (Will be used as return address by task switch function)
* Whatever data the task-switch function pushes onto the stack and expects to be present on the new stack (none if all registers are saved to a task structure).
3) Initialize a task structure with any registers saved into it by the task switch procedure (and whatever else it contains, obviously). Be sure to set SS:ESP to point to the lowest initialized entry in the new kernel stack (the last-pushed register or (if none) the return address).
4) Perform whatever other actions need to be done to set up a new process (address space initialization etc.)
5) Add it to the scheduler queue
When this new task is switched to, it'll initially be in ring 0. The task switch function treats it like it would any other task, "restoring" register values and "returning" to the stub function set up in step 3 above, which is then free to perform some last-minute register initialization and perform an IRETD to jump to the ring 3 code.
When you switch to a thread that has already run, the task switch function restores the register values and returns to whatever code called the task switch function last time the thread was run. If that was an interrupt handler it can then restore registers to the values expected by the user process (or kernel thread) interrupted and execute an IRETD to return control to it.
Hallelujah! Now I guess I got it (Finally). urxae, thank you so much for your explanations. This is what I am going to do now:
1) In my [__CreateProcess] function, I will create a kernel stack (PVL/DPL 0) for each task and push the task's SS/ESP/EFLAGS/CS/EIP into it, respectively.
2) In my [__IRQ0Handler] procedure, I will, at the beginning, push all general purpose registers, segment registers and everything pertaining to the kernel, into the kernel's stack instead of just saving a few registers that I know I am going to be destroying.
3) When my [__IRQ0Handler] doesn't find a process in the Process Queue, it will quietly exit with its restored registers.
4) If the [__IRQ0Handler] procedure finds a process in the Process Queue, it will replace the kernel's SS:ESP with process's SS:ESP which is saved into the process's structure. Then it will push all the registers that my [__IRQ0Handler] procedure requires to pop off of the stack, into the process's stack. So the kernel will be faked into believing that the current stack is actually the kernel's stack. Just before I issue the IRET, I will change the DPL/PVL of the process's stack to 3.
There is still one thing I don't get. Suppose I successfully switched to the process and now IRQ0 is fired. I want my kernel's general purpose registers and all other registers that were changed during the task switch to be set to their default values (to the values that they had before the task switch). Now I know that when IRQ0 is fired when the control is given to a process, the CPU will fetch the CS:EIP of the IRQ0 handler from the IDT and it will fetch SS0:ESP0 from the TSS. But how will it restore my general purpose registers? Should I put all the general purpose registers of the kernel and all other segments and etc into the kernel's TSS before the task switch?
I'm thankful beyond words for your helps, urxae. Thank you everyone.
1) In my [__CreateProcess] function, I will create a kernel stack (PVL/DPL 0) for each task and push the task's SS/ESP/EFLAGS/CS/EIP into it, respectively.
2) In my [__IRQ0Handler] procedure, I will, at the beginning, push all general purpose registers, segment registers and everything pertaining to the kernel, into the kernel's stack instead of just saving a few registers that I know I am going to be destroying.
3) When my [__IRQ0Handler] doesn't find a process in the Process Queue, it will quietly exit with its restored registers.
4) If the [__IRQ0Handler] procedure finds a process in the Process Queue, it will replace the kernel's SS:ESP with process's SS:ESP which is saved into the process's structure. Then it will push all the registers that my [__IRQ0Handler] procedure requires to pop off of the stack, into the process's stack. So the kernel will be faked into believing that the current stack is actually the kernel's stack. Just before I issue the IRET, I will change the DPL/PVL of the process's stack to 3.
There is still one thing I don't get. Suppose I successfully switched to the process and now IRQ0 is fired. I want my kernel's general purpose registers and all other registers that were changed during the task switch to be set to their default values (to the values that they had before the task switch). Now I know that when IRQ0 is fired when the control is given to a process, the CPU will fetch the CS:EIP of the IRQ0 handler from the IDT and it will fetch SS0:ESP0 from the TSS. But how will it restore my general purpose registers? Should I put all the general purpose registers of the kernel and all other segments and etc into the kernel's TSS before the task switch?
I'm thankful beyond words for your helps, urxae. Thank you everyone.
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
Now this is a really strange problem: I created the DS/ES/FS/GS and the SS of the process with DPL/PVL 0 so that the process's stack would be a kernel stack. I then, in my IRQ0 handler procedure, detected a process in the process queue. I then changed the current kernel's stack to the process's stack (which has the DPL/PVL of 0) and then started pushing the process's general purpose registers and other information into the new stack. But the problem is that as soon as IRET is issued at the end of the IRQ0Handler procedure, Bochs reports thousands of error messages as shown below:
And it happens every time I want to switch to the process. The code for my new IRQ0Handler procedure is given below:
I have checked to see if I am pushing correct values into the process's stack and everything seems to be okay. Could somebody tell me what the problem might be? Thank you guys so much for your help.
Code: Select all
00010055912e[CPU0 ] read_virtual_checks(): read beyond limit
00010055919e[CPU0 ] read_virtual_checks(): read beyond limit
00010055926e[CPU0 ] read_virtual_checks(): read beyond limit
00010055933e[CPU0 ] read_virtual_checks(): read beyond limit
00010055940e[CPU0 ] read_virtual_checks(): read beyond limit
00010055947e[CPU0 ] read_virtual_checks(): read beyond limit
00010055954e[CPU0 ] read_virtual_checks(): read beyond limit
00010055996e[CPU0 ] read_virtual_checks(): read beyond limit
Code: Select all
; ——————————————————————————————————————————————————
__IRQ0Handler:
PUSH EAX
PUSH EBX
PUSH ECX
PUSH EDX
PUSH ESI
PUSH EDI
PUSH GS
PUSH DS
PUSH ES
PUSH FS
PUSHFD
PUSH EBP
MOV EBP , ESP
INVOKE __DisableIRQPulse , IRQ_PULSE_IRQ0
CLI
MOV EAX , PIC_EOI
OUT PIC1_COMMAND , AL
; See if there are any processes left to be processed
MOV EAX , DWORD PTR [ProcessesCount]
TEST EAX , EAX
JNZ .Continue1
JMP .SendEOI
.Continue1:
; Set the Timer Cycles and adjust it
LEA ESI , [TimerCycles]
MOV EAX , DWORD PTR [ESI]
MOV EDX , DWORD PTR [PITFrequency]
; EAX = TimerCycles
; EDX = PIT Frequency
INC EAX
CMP EAX , EDX
JBE .NoCyclcesAdjustmentNeeded
MOV EAX , 0x00000001
.NoCyclcesAdjustmentNeeded:
MOV DWORD PTR [ESI] , EAX
INVOKE __FindHighestPriorityProcess
TEST EAX , EAX
JNZ .Continue2
JMP .SendEOI
.Continue2:
; EDI = Pointer to the process's structure
MOV EDI , EAX
MOV WORD PTR [TSS_STRUCT + 8] , SS
MOV DWORD PTR [TSS_STRUCT + 4] , ESP
; Change the stack of the kernel with the stack of the process
MOV EAX , DWORD PTR [EDI + 20]
AND EAX , 0x0000FFFF
MOV SS , EAX
; Change the ESP of the kernel to the ESP of the process
MOV EAX , DWORD PTR [EDI + 60]
MOV ESP , EAX
; Now Start Pushing the SS/ESP/EFLAGS/CS/EIP of the process into the new stack (Process's stack with DPL/RPL 0)
MOV EAX , DWORD PTR [EDI + 20]
AND EAX , 0x0000FFFF
PUSH EAX ; SS of the process
MOV EAX , DWORD PTR [EDI + 60]
PUSH EAX ; ESP of the process
MOV EAX , DWORD PTR [EDI + 64]
PUSH EAX ; EFLAGS of the process
MOV EAX , DWORD PTR [EDI + 16]
AND EAX , 0x0000FFFF
PUSH EAX ; CS of the process
MOV EAX , DWORD PTR [EDI + 28]
PUSH EAX ; EIP of the process
; Now start pushing the registers from the process's structure to the current stack
; These should be the values that will be popped off of the stack at the end of this procedure
PUSH DWORD PTR [EDI + 32] ; EAX
PUSH DWORD PTR [EDI + 36] ; EBX
PUSH DWORD PTR [EDI + 40] ; ECX
PUSH DWORD PTR [EDI + 44] ; EDX
PUSH DWORD PTR [EDI + 48] ; ESI
PUSH DWORD PTR [EDI + 52] ; EDI
MOV EAX , DWORD PTR [EDI + 24]
AND EAX , 0x0000FFFF
PUSH EAX ; GS
MOV EAX , DWORD PTR [EDI + 16]
SHR EAX , 0x00000010
PUSH EAX ; DS
MOV EAX , DWORD PTR [EDI + 20]
SHR EAX , 0x00000010
PUSH EAX ; ES
MOV EAX , DWORD PTR [EDI + 24]
SHR EAX , 0x00000010
PUSH EAX ; FS
PUSH DWORD PTR [EDI + 64] ; EFLAGS
PUSH DWORD PTR [EDI + 56] ; EBP
MOV EAX , DWORD PTR [ESP + 0x34]
.SendEOI:
INVOKE __EnableIRQPulse , IRQ_PULSE_IRQ0
STI
POP EBP
POPFD
POP FS
POP ES
POP DS
POP GS
POP EDI
POP ESI
POP EDX
POP ECX
POP EBX
POP EAX
IRET
; ——————————————————————————————————————————————————
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
It looks pretty bad for me to reply to my own replies lol but here we go again. I fixed the above problem and now I am switching to a process with DPL/CPL/RPL 3 successfully. The problem is that as soon as my process takes control, none of the IRQs will be fired again. When I switch to the process, these flags are on:
1) The Interrupt Flag of the EFLAGS of the process.
2) The Interrupt Flag of the EFLAGS of the kernel saved in the TSS.
3) The Nested Task (NT) flag of the EFLAGS of the kernel saved in the TSS.
4) The Nested Task (NT) flag of the EFLAGS of the process.
But still no IRQs are fired. Can anybody tell me why? I have included the source of my [IRQ0Handler] procedure below. This procedure uses the kernel stack that is created for the process, pushes all the values into it and does all the initializations of the process:
Thanks in advance. If there is anything else you think you must know about the conditions of my process/kernel/code/etc in order to be able to answer my question, please let me know.
1) The Interrupt Flag of the EFLAGS of the process.
2) The Interrupt Flag of the EFLAGS of the kernel saved in the TSS.
3) The Nested Task (NT) flag of the EFLAGS of the kernel saved in the TSS.
4) The Nested Task (NT) flag of the EFLAGS of the process.
But still no IRQs are fired. Can anybody tell me why? I have included the source of my [IRQ0Handler] procedure below. This procedure uses the kernel stack that is created for the process, pushes all the values into it and does all the initializations of the process:
Code: Select all
; ——————————————————————————————————————————————————
__IRQ0Handler:
WRITE 'Here'
PUSH EAX
PUSH EBX
PUSH ECX
PUSH EDX
PUSH ESI
PUSH EDI
PUSH GS
PUSH DS
PUSH ES
PUSH FS
PUSHFD
PUSH EBP
MOV EBP , ESP
INVOKE __DisableIRQPulse , IRQ_PULSE_IRQ0
CLI
MOV EAX , PIC_EOI
OUT PIC1_COMMAND , AL
; See if there are any processes left to be processed
MOV EAX , DWORD PTR [ProcessesCount]
TEST EAX , EAX
JNZ .Continue1
JMP .SendEOI
.Continue1:
; Set the Timer Cycles and adjust it
LEA ESI , [TimerCycles]
MOV EAX , DWORD PTR [ESI]
MOV EDX , DWORD PTR [PITFrequency]
; EAX = TimerCycles
; EDX = PIT Frequency
INC EAX
CMP EAX , EDX
JBE .NoCyclcesAdjustmentNeeded
MOV EAX , 0x00000001
.NoCyclcesAdjustmentNeeded:
MOV DWORD PTR [ESI] , EAX
INVOKE __FindHighestPriorityProcess
TEST EAX , EAX
JNZ .Continue2
JMP .SendEOI
.Continue2:
; EDI = Pointer to the process's structure
MOV EDI , EAX
MOV WORD PTR [TSS_STRUCT + 8] , SS
MOV DWORD PTR [TSS_STRUCT + 4] , 0x0000FFFF
MOV DWORD PTR [TSS_STRUCT + 32] , OFFSET __IRQ0Handler ; EIP
PUSHFD
POP EAX
MOV DWORD PTR [TSS_STRUCT + 36] , EAX ; EFLAGS
MOV DWORD PTR [TSS_STRUCT + 40] , EAX
MOV DWORD PTR [TSS_STRUCT + 44] , ECX
MOV DWORD PTR [TSS_STRUCT + 48] , EDX
MOV DWORD PTR [TSS_STRUCT + 52] , EBX
MOV DWORD PTR [TSS_STRUCT + 56] , ESP
MOV DWORD PTR [TSS_STRUCT + 60] , EBP
MOV DWORD PTR [TSS_STRUCT + 64] , ESI
MOV DWORD PTR [TSS_STRUCT + 68] , EDI
MOV WORD PTR [TSS_STRUCT + 72] , ES
MOV WORD PTR [TSS_STRUCT + 76] , CS
MOV WORD PTR [TSS_STRUCT + 80] , SS
MOV WORD PTR [TSS_STRUCT + 84] , DS
MOV WORD PTR [TSS_STRUCT + 88] , FS
MOV WORD PTR [TSS_STRUCT + 92] , GS
; Change the stack of the kernel with the stack of the process
MOV EAX , DWORD PTR [EDI + 72]
AND EAX , 0x0000FFFF
MOV SS , EAX
; Change the ESP of the kernel to the ESP of the process
MOV EAX , DWORD PTR [EDI + 76]
MOV ESP , EAX
; Now Start Pushing the SS/ESP/EFLAGS/CS/EIP of the process into the new stack (Process's stack with DPL/RPL 0)
MOV EAX , DWORD PTR [EDI + 20]
AND EAX , 0x0000FFFF
PUSH EAX ; SS of the process
PUSH DWORD PTR [EDI + 60] ; ESP of the process
PUSH DWORD PTR [EDI + 64] ; EFLAGS of the process
MOV EAX , DWORD PTR [EDI + 16]
AND EAX , 0x0000FFFF
PUSH EAX ; CS of the process
PUSH DWORD PTR [EDI + 28] ; EIP of the process
; Now start pushing the registers from the process's structure to the current stack
; These should be the values that will be popped off of the stack at the end of this procedure
PUSH DWORD PTR [EDI + 32] ; EAX of the process
PUSH DWORD PTR [EDI + 36] ; EBX of the process
PUSH DWORD PTR [EDI + 40] ; ECX of the process
PUSH DWORD PTR [EDI + 44] ; EDX of the process
PUSH DWORD PTR [EDI + 48] ; ESI of the process
PUSH DWORD PTR [EDI + 52] ; EDI of the process
MOV EAX , DWORD PTR [EDI + 24]
AND EAX , 0x0000FFFF
PUSH EAX ; GS of the process
MOV EAX , DWORD PTR [EDI + 16]
SHR EAX , 0x00000010
PUSH EAX ; DS of the process
MOV EAX , DWORD PTR [EDI + 20]
SHR EAX , 0x00000010
PUSH EAX ; ES of the process
MOV EAX , DWORD PTR [EDI + 24]
SHR EAX , 0x00000010
PUSH EAX ; FS of the process
PUSH DWORD PTR [EDI + 64] ; EFLAGS of the process
PUSH DWORD PTR [EDI + 56] ; EBP of the process
.SendEOI:
INVOKE __EnableIRQPulse , IRQ_PULSE_IRQ0
STI
POP EBP
POPFD
POP FS
POP ES
POP DS
POP GS
POP EDI
POP ESI
POP EDX
POP ECX
POP EBX
POP EAX
IRET
; ——————————————————————————————————————————————————
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
Alright. I am getting better. Now I am sure that my processes run okay. What the problem is that the CPU tries to get to my IRQ0 handler procedure after switching to the process but more than a thousand errors happen while doing so. I am wondering if someone can tell me what the TSS does in all of this? I mean I have only one TSS and my kernel is NOT a process. I only add one process to my Process Queue and I set the SS0:ESP0: field of the kernel's TSS. Now I switch to the process and everything is okay. As soon as an interrupt happens, Bochs reports errors like these:
Intel manuals keep going on and on about hardware context switching but I am doing software context switching so they are not really useful for me. (They are but not as much as they should be). I really need to know where kernel's state of general purpose registers and things should be saved before switching to processes and when/how/where from will these information be restored when an interrupt occurs? what does the TSS do here? can someone, please, "enlighten" me with proper information? Thank you guys.
Code: Select all
00003460151e[CPU0 ] read_virtual_checks(): read beyond limit
00003460158e[CPU0 ] read_virtual_checks(): read beyond limit
00003460165e[CPU0 ] read_virtual_checks(): read beyond limit
00003460172e[CPU0 ] read_virtual_checks(): read beyond limit
00003460179e[CPU0 ] read_virtual_checks(): read beyond limit
00003460186e[CPU0 ] read_virtual_checks(): read beyond limit
00003460193e[CPU0 ] read_virtual_checks(): read beyond limit
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
First of all, IMHO it's easier to do multi-threading purely inside kernel, and handle userspace processes as a separate issue not really connected to scheduling. This would also let you have kernel threads that never go to userspace, which simplifies some stuff.
But in any case, you're supposed to structure your kernel as one big interrupt handler (with kernel multi-threading, only userspace threads really need to work this way, rest of the kernel can run it's own threads as it pleases). Interrupt handlers don't have a set of registers unless they are currently running (or blocked in some queue). If you need to store something, you store it in global variables (or well, usually mostly in data structures pointed to by global variables).
So basicly, when you come into kernel, you do what an interrupt handler would do: save all registers, set a sane DS/ES (CS you get from IDT and SS from TSS so those you don't need to load) and figure out what you need to do.
But in any case, you're supposed to structure your kernel as one big interrupt handler (with kernel multi-threading, only userspace threads really need to work this way, rest of the kernel can run it's own threads as it pleases). Interrupt handlers don't have a set of registers unless they are currently running (or blocked in some queue). If you need to store something, you store it in global variables (or well, usually mostly in data structures pointed to by global variables).
So basicly, when you come into kernel, you do what an interrupt handler would do: save all registers, set a sane DS/ES (CS you get from IDT and SS from TSS so those you don't need to load) and figure out what you need to do.
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
mystran,
Thank you so much for your help. I finally fixed my problem. The only problem was that I was not setting DS/ES/FS/GS of the kernel on each interrupt. I thought that the TSS was responsible for it. So basically, when doing software context switching, the TSS is only responsible for handing the SS0:ESP0 to the kernel, is that close on being on the money?
Now I have one big concern, when the process takes control and an IRQ0 is fired, I change the DS/ES/FS/GS of the kernel to their original (kernel) values and then everything works fine. Now I need to know where the information related to the process is saved! Will it be saved in the only TSS that I have created? Should I read it from the TSS when I get into my IRQ0 handler and then save it to my process’s structure?
Thank you guys so much. mystran, urxae, Aali and everyone. I appreciate it.
Thank you so much for your help. I finally fixed my problem. The only problem was that I was not setting DS/ES/FS/GS of the kernel on each interrupt. I thought that the TSS was responsible for it. So basically, when doing software context switching, the TSS is only responsible for handing the SS0:ESP0 to the kernel, is that close on being on the money?
Now I have one big concern, when the process takes control and an IRQ0 is fired, I change the DS/ES/FS/GS of the kernel to their original (kernel) values and then everything works fine. Now I need to know where the information related to the process is saved! Will it be saved in the only TSS that I have created? Should I read it from the TSS when I get into my IRQ0 handler and then save it to my process’s structure?
Thank you guys so much. mystran, urxae, Aali and everyone. I appreciate it.
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
Exactly.XCHG wrote: So basically, when doing software context switching, the TSS is only responsible for handing the SS0:ESP0 to the kernel, is that close on being on the money?
You have to save it somewhere yourself. Processor saves SS/ESP/CS/EIP/EFLAGS for you, and everything else is your problem. All the registers except SS/ESP/CS/EIP/EFLAGS are going be exactly as before the interrupt when you enter your kernel. First thing you do is push all of these registers into stack, and then load sane DS/ES. If you don't want to keep the registers in stack, you could move them elsewhere..Now I have one big concern, when the process takes control and an IRQ0 is fired, I change the DS/ES/FS/GS of the kernel to their original (kernel) values and then everything works fine. Now I need to know where the information related to the process is saved! Will it be saved in the only TSS that I have created? Should I read it from the TSS when I get into my IRQ0 handler and then save it to my process’s structure?
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
Yeah, pretty much. IIRC the only important fields in the TSS when software task switching are SS0:ESP0 and the I/O permission bitmap base address. If you're not using the I/O permission bitmap, make sure the base address is beyond the TSS segment limit.XCHG wrote:mystran,
Thank you so much for your help. I finally fixed my problem. The only problem was that I was not setting DS/ES/FS/GS of the kernel on each interrupt. I thought that the TSS was responsible for it. So basically, when doing software context switching, the TSS is only responsible for handing the SS0:ESP0 to the kernel, is that close on being on the money?
AFAIK, the only information automatically saved on an IRQ are ss:esp, cs:eip and eflags. Any other information you need to restore needs to be saved explicitly.Now I have one big concern, when the process takes control and an IRQ0 is fired, I change the DS/ES/FS/GS of the kernel to their original (kernel) values and then everything works fine. Now I need to know where the information related to the process is saved! Will it be saved in the only TSS that I have created? Should I read it from the TSS when I get into my IRQ0 handler and then save it to my process’s structure?
So things like segment registers (other than cs and ss) and general-purpose registers (other than esp) should be pushed onto the stack (or saved elsewhere) and restored before IRET if you're going to change them.
You may also want to change some bits in EFLAGS (such as the direction flag if your kernel ever uses string instructions, see CLD).
Alright thank you guys. I thought of a process structure for the kernel. So the kernel is a process itself but my main concern is what the base address of the kernel's process should be? Should the kernel's process be scheduled? What will happen to the kernel itself after processes are created? Should the kernel just sit IDLE and just listen to interrupts/IRQs?
I would also like to know what you guys think of this:
Support your scheduler has scheduled all the processes in the Process Queue and now it has some time to sit idle. What should the scheduler do now? What CS:EIP should it jump to if any?
I'd appreciate it if someone could clear these points for me.
I would also like to know what you guys think of this:
Support your scheduler has scheduled all the processes in the Process Queue and now it has some time to sit idle. What should the scheduler do now? What CS:EIP should it jump to if any?
I'd appreciate it if someone could clear these points for me.
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
One way of doing it (which is what I do at the moment) would be to have the idle loop as part of your kernel. This means that your kernel just runs on in to a 'hlt' loop once it has initialised the system. If there's nothing else to schedule, we go in to this loop.
You may like to arrange things so that the kernel does things during this idle time - heap block merging / checking memory metrics and integrity etc. Of course, if you put anything in the idle loop which is too mission critical and it never gets scheduled that could cause more problems...
I'm afraid it's one of those 'Do it however you want to...' situations.
Cheers,
Adam
You may like to arrange things so that the kernel does things during this idle time - heap block merging / checking memory metrics and integrity etc. Of course, if you put anything in the idle loop which is too mission critical and it never gets scheduled that could cause more problems...
I'm afraid it's one of those 'Do it however you want to...' situations.
Cheers,
Adam