Page 1 of 1
IRQ hell in Context Switching
Posted: Sun May 06, 2007 1:29 am
by XCHG
I am doing software context switching. I have 1 main process for the kernel which is only given control when there are no processes left in the process queue. So the kernel’s process is only executed when the CPU is idle. Now the good news is that all processes work simultaneously as long as there is only IRQ0 fired. After for example 40 to 50 IRQ12 (for mouse), I will get a general protection fault with this message:
Code: Select all
00033103936e[CPU0 ] branch_far: EIP > limit
What I am doing is that I am saving the state of the current task when any of the IRQs is fired. In all of the IRQs, I first disable all other IRQs and then re enable them right before the task switch.
I really don’t know if what I think is correct or not but I’d really appreciate it if somebody could confirm the accuracy of this:
When IRQ0 is fired from CPL/DPL 0 to CPL/DPL 0 again, the stack’s contents will look like this (From top to bottom):
When IRQ0 is fired from CPL/DPL 3 to CPL/DPL 0, the processor will fetch SS0:ESP0 from the TSS and then sets them as the current SS:ESP. The processor will then push these values into the new stack:
Code: Select all
SS (of the process)
ESP (of the process)
EFLAGS (of the process)
CS (of the process)
EIP (of the process)
…
Therefore when IRQ0 is fired, I should save this information in the current process’s structure. Now if anyone can give me a hint on what I might be doing wrong, I’d really appreciate it. If you need the source code to be able to find the error’s source, I can put the source code here. Just tell me what should be put here so that someone can find the problem because I have been struggling with it for days and I can’t find it out and it is really frustrating. Thanks in advance.
Posted: Sun May 06, 2007 1:53 am
by XCHG
Holy golly. I fixed this. I had this at the end of my IRQ12 handler procedure:
Code: Select all
.SendEOI:
STI
INVOKE __EnableAllIRQPulses
POP_ALL_REGISTERS
IRET
I just changed the position of the STI instruction to the end of the IRQ12 handler just before the IRET like this:
Code: Select all
.SendEOI:
INVOKE __EnableAllIRQPulses
POP_ALL_REGISTERS
STI
IRET
And now everything is working fine. Now is this the solution or am I just disabling the interrupts so that I won't see them? Does the Interrupt Flag have to be enabled at the end of IRQ handlers?
Posted: Sun May 06, 2007 2:20 am
by pcmattman
You shouldn't enable interrupts in an IRQ because another IRQ will fire and you'll pretty much trash the stack.
The iret instruction sets the interrupt flag to the value of EFLAGS pushed onto the stack automatically (ie. when the IRQ fired).
Posted: Sun May 06, 2007 5:01 am
by XCHG
pcmattman,
Yeah I just realized and fixed that. Thank you for you help. Appreciations. Over and out.
Posted: Sun May 06, 2007 5:34 am
by jnc100
Check out the difference between trap gates and interrupt gates in the intel docs. Basically, interrupt gates clear IF on entry to the procedure whereas trap gates don't. If you're using interrupt gates then the processor ensures your kernel is not preemptable.
Regards,
John.
Posted: Sun May 06, 2007 5:05 pm
by pcmattman
pcmattman wrote:You shouldn't enable interrupts in an IRQ because another IRQ will fire and you'll pretty much trash the stack.
The iret instruction sets the interrupt flag to the value of EFLAGS pushed onto the stack automatically (ie. when the IRQ fired).
As a side-note, this is a great way of multitasking - you just save the ESP field into a global structure that your IRQ entry/exit functions reference. For those who don't understand:
Code: Select all
# handles irq calls - two bytes already pushed - error code and interrupt number
# handles irq calls - two bytes already pushed - error code and interrupt number
irq_common_stub:
# push all general registers (eax et al)
pusha
# push segments
push %ds
push %es
push %fs
push %gs
# use kernel segments (because we may have just come from ring3 and they do things... differently)
movw $0x10,%ax
movw %ax,%ds
movw %ax,%es
movw %ax,%fs
movw %ax,%gs
# save the current task's esp, also you could store the kstack, cr3 etc...
movl %esp, (_current_task)
# push ESP (more like save it)
movl %esp,%eax
push %eax
# call the IRQ handler
call _irq_handler
# restore EAX/ESP
pop %eax
# restore the new stack pointer (or, if the pointer wasn't changed, the old one)
movl (_current_task),%esp
# restore cr3 ...
# restore segments
pop %gs
pop %fs
pop %es
pop %ds
# restore general registers
popa
# skip over intnum and errcode
add $8,%esp
# return
iret
Basically, you have a structure in which the first element is an unsigned int, and call that the stack pointer. Initialize a stack, save the pointer into the element of the structure, and then on reschedules load it into a global structure 'current_task'.
I had too much trouble with this, I figured I may as well post it here for others to use. PM me if you need more help (or the Intel translation, I have both).
Good to hear you got your IRQ problems fixed.