Multitasking Issues

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Aali
Member
Member
Posts: 58
Joined: Sat Apr 14, 2007 12:13 pm

Post by Aali »

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 :wink:
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post by XCHG »

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?
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.
urxae
Member
Member
Posts: 149
Joined: Sun Jul 30, 2006 8:16 am
Location: The Netherlands

Post by urxae »

(There have been some more posts since I started this one, but I've spent all this time typing now... :) )
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.
So far so good.
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?
This is why I mentioned that was not how I was actually doing it, just an example of how IRET works.

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 :P. (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.
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post by XCHG »

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.
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.
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post by XCHG »

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:

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
And it happens every time I want to switch to the process. The code for my new IRQ0Handler procedure is given below:

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
; ——————————————————————————————————————————————————
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.
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.
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post by XCHG »

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:

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
; ——————————————————————————————————————————————————
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.
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.
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post by XCHG »

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:

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
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.
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.
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post by mystran »

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.
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post by XCHG »

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.
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.
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post by mystran »

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?
Exactly.
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?
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..
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
urxae
Member
Member
Posts: 149
Joined: Sun Jul 30, 2006 8:16 am
Location: The Netherlands

Post by urxae »

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?
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.
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?
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.
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).
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post by XCHG »

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.
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.
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

You could have an idle thread that loops on a hlt instruction. It would only be called if nothing else could be scheduled.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

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
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

I have a function called NullProc. It's the first process created and does some basic initialization (starts the network stack, starts servers, and then starts shell task), then enters an infinite loop so when other processes don't use the processor there's always code to execute.
Post Reply