Page 1 of 1

Interrupt during function call

Posted: Mon Oct 27, 2014 8:58 am
by kemosparc
Hi,

I have a problem with my interrupt return. First of all, here is my code:

Code: Select all

extern "C" void idt_handler(struct interrupted_context * ic)
{

        if (ic->intNo >= 40)
        {
            Ports::outportb(PIC2_COMMAND, 0x20);
        }
        Ports::outportb(PIC1_COMMAND, 0x20);


    if (ic->intNo]== 32 )
    {
	ticks ++;
	video.setPosition(30,10);
	video.putString("Ticks: ",COLOR_RED,COLOR_LIGHT_BROWN);
	video.putHexa(ticks,COLOR_RED,COLOR_LIGHT_BROWN);
	video.putString("]\n",COLOR_RED,COLOR_LIGHT_BROWN);
    }
    else
    {
        video.setPosition(30,10);
        if ( ic->intNo < IRQ0)
        {
            video.putString(exception_messages[ic->intNo],COLOR_RED,COLOR_LIGHT_BROWN);
            video.putString("[",COLOR_RED,COLOR_LIGHT_BROWN);
            video.putHexa(ic->intNo,COLOR_RED,COLOR_LIGHT_BROWN);
            video.putString("]\n",COLOR_RED,COLOR_LIGHT_BROWN);
        }
        else
        {
            video.putString("Unhandled Interrupt",COLOR_RED,COLOR_LIGHT_BROWN);
            video.putString("[",COLOR_RED,COLOR_LIGHT_BROWN);
            video.putDecimal(ic->intNo,COLOR_RED,COLOR_LIGHT_BROWN);
            video.putString("]\n",COLOR_RED,COLOR_LIGHT_BROWN);
        }
    }
}

void iloop_function ()
{
	for (uint64_t i = 0 ; ; i++ )
	{
		video.setPosition(45,5);
		video.putString("iloop_function: ",COLOR_LIGHT_BROWN,COLOR_RED);
		video.putHexa(i,COLOR_LIGHT_BROWN,COLOR_RED);
		video.putString("\n",COLOR_LIGHT_BROWN,COLOR_RED);
		i ++;
	}
}


extern "C" void kernel_main(uint64_t initial_stack_start_address,uint64_t initial_stack_end_address)
{
    video.initialize();
    video.clearScreen(COLOR_BLACK);
    video.putString("C++ Code\n",COLOR_LIGHT_BROWN,COLOR_RED);

    interruptManager = new InterruptManager();
    interruptManager->registerInterrupt(32,pitScheduler);
    iloop_function();
}

Code: Select all

[BITS 64]
global idtInit
extern idtP
extern interruptDescriptorTablePointer;


idtInit:
   lidt [interruptDescriptorTablePointer]
   ret

extern idt_handler

idt_common_stub:
    mov ax, ds 
    push rax
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov rdi,rsp
    mov rax,idt_handler
    call rax

    pop rbx
    mov ds, bx
    mov es, bx
    mov fs, bx
    mov gs, bx
    add rsp,16
    sti
    iretq

%macro ISR_NOERRCODE 1
  [GLOBAL isr%1]
  isr%1:
    cli
    push byte 0
    push byte %1
    jmp idt_common_stub
%endmacro

%macro ISR_ERRCODE 1
  [GLOBAL isr%1]
  isr%1:
    cli
    push byte %1
    jmp idt_common_stub
%endmacro

%macro IRQ 2
  global irq%1
  irq%1:
    cli
    push byte 0
    push byte %2
    jmp idt_common_stub
%endmacro

ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
ISR_NOERRCODE 4
ISR_NOERRCODE 5
ISR_NOERRCODE 6
ISR_NOERRCODE 7
ISR_ERRCODE   8
ISR_NOERRCODE 9
ISR_ERRCODE   10
ISR_ERRCODE   11
ISR_ERRCODE   12
ISR_ERRCODE   13
ISR_ERRCODE   14
ISR_NOERRCODE 15
ISR_NOERRCODE 16
ISR_NOERRCODE 17
ISR_NOERRCODE 18
ISR_NOERRCODE 19
ISR_NOERRCODE 20
ISR_NOERRCODE 21
ISR_NOERRCODE 22
ISR_NOERRCODE 23
ISR_NOERRCODE 24
ISR_NOERRCODE 25
ISR_NOERRCODE 26
ISR_NOERRCODE 27
ISR_NOERRCODE 28
ISR_NOERRCODE 29
ISR_NOERRCODE 30
ISR_NOERRCODE 31
IRQ   0,    32
IRQ   1,    33
IRQ   2,    34
IRQ   3,    35
IRQ   4,    36
IRQ   5,    37
IRQ   6,    38
IRQ   7,    39
IRQ   8,    40
IRQ   9,    41
IRQ  10,    42
IRQ  11,    43
IRQ  12,    44
IRQ  13,    45
IRQ  14,    46
IRQ  15,    47

Code: Select all

[BITS 64]
SECTION .text
global _start
_start:
jmp Main64

Main64:
	cli                           ; Clear the interrupt flag.

	mov rsp,_stack_start
	mov rdi,rsp
	mov rsi,_stack_end
	extern kernel_main
	call kernel_main

khalt:
    hlt
    jmp khalt
SECTION .bss
_stack_end:
    resb    8192
_stack_start:


That above code was originally working without the iloop_function, and it used to work fine and whenever I start it in qemu it keeps on incrementing the ticks on the screen normally.

Basically when the interrupt handler of the PIT fires it executes the increment and the print of the ticks variable and it return to the address of the khalt label so I noticed no problem.

My problem started when I introducted the call to the iloop_function. What happens is that when the interrupt fires, it executes and return back to the correct address but the counter of the loop is rest to zero.

I expect that there is something wrong with the stack as the local variables are not being preserved after the interrupt return.

If any one came accross a similar problem, kindly let me know how to solve.


Thanks
Karim.

Re: Interrupt during function call

Posted: Mon Oct 27, 2014 9:26 am
by Brendan
Hi,

Code: Select all

idt_common_stub:
    mov ax, ds 
    push rax
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov rdi,rsp
    mov rax,idt_handler
    call rax

    pop rbx
    mov ds, bx
    mov es, bx
    mov fs, bx
    mov gs, bx
    add rsp,16
    sti
    iretq
This code:
  • trashes the interrupted code's RAX
  • trashes the interrupted code's DS, ES, FS and GS
  • trashes the interrupted code's RDI and RBX
In addition; for whichever calling convention your compiler is using, some registers are guaranteed to be preserved by the called code and some are not. Registers that are not guaranteed to be preserved should also be considered "possibly trashed".

So... some innocent code is happily running, relying on being able to store values in registers; then an IRQ occurs and your IRQ handler trashes all the registers that the interrupted code was relying on; then the IRQ handler returns to the code that it interrupted and...


Cheers,

Brendan

Re: Interrupt during function call

Posted: Thu Oct 30, 2014 6:03 am
by kemosparc
Thanks a lot for your reply.

I did some changes to the code you poin pointed. Basically pushed all registers at the begining and poped them before return.

Thanks a lot again