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):

Code: Select all

….
EFLAGS
CS
EIP

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.