What to do on interrupt stubs?

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.
Post Reply
earlz
Member
Member
Posts: 1546
Joined: Thu Jul 07, 2005 11:00 pm
Contact:

What to do on interrupt stubs?

Post by earlz »

I use to think that interrupt stubs should just push registers, push segments, and jmp to the interrupt handler and then just pop everything else

but really there is more to it but I'm not for sure what all, I want to be able to have interrupts in interrupts and I want to be able use a different code, data and stack segment for interrupt handlers

Also I will be doing code "templating" like I will copy 1 template 256 times(for each interrupt) and then just replace a dword(which is after call) so that it will call the appropriate interrupt handler
and one more thing I want to be able to do is be able to access everything that is saved(on the stack) in the interrupt handler(just push the stack pointer..)

right now I have
;stuff pushed by int is eip,flags, and CS, user_esp, and SS
push_segments
pusha
well..bout here I can't figure out a half good method for saving segments..
earlz
Member
Member
Posts: 1546
Joined: Thu Jul 07, 2005 11:00 pm
Contact:

Post by earlz »

ok, well i don't need help any more(I hope)
I haven't been able to test it yet, but it does compile:

I will copy the template 256 times and then just modify it(runtime modified code..) to install an interrupt handler..

and the final interrupt handler will just be void MyInterrupt(regs *r);
with regs being...very simple for the interrupt handler, very complicated for me..

Code: Select all

typedef struct
{
    unsigned int ds, es, fs, gs;      /* pushed the segs last */
    unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax;  /* pushed by 'pusha' */
    unsigned int int_no, err_code;    /* our 'push byte #' and ecodes do this */
    unsigned int eip, cs, eflags, useresp, ss;   /* pushed by the processor automatically */
}
regs;

Code: Select all

far_template:
mov ebp,esp
add ebp,12 ;skip the pushed ip and cs and then it's where we pushed esp
mov eax,[ss:ebp]
push eax ;push the stack pointer again..
mov eax,0x20000000 ;the value is at offset 0xE
call eax ;this is templated! 0x200 should be what the interrupt handler is ;this is templated! 0x200 should be what the interrupt handler is
pop eax
retf ;<--end total size of far_template is 22 bytes

int_template:
cli
push eax
mov al,0 ;this is templated! this should be 0 for most interrupts, or 1 if a value is already pushed.. -at 0x3
cmp al,1
jz no_pushing
jmp pushing
no_pushing:
jmp continue_1
pushing:
push 0 ;this is for a dummy error code..
continue_1:
pop eax
push 20 ;this is templated! 20 should be replaced with the interrupt number -at 0x19
pusha
push ds
push es
push fs
push gs
mov ecx,0
mov cx,ss
mov edx,esp ;store them for a bit..
mov ax,0x8 ;this is templated! 0x8 should be replaced with interrupt stack segment -0x30
mov ss,ax
mov esp,0x100 ;this is templated! 0x100 should be replaced with interrupt stack offset -at 0x35
push ecx ;push our saved ss
push edx ;push our saved esp
mov ax,0x08 ;this is templated! 0x08 should be replaced with interrupt data segment -at 0x3D
mov ds,ax ;setup data segments..
mov fs,ax
mov gs,ax

mov ax,cx
add edx,21*4 ;increment stack ptr to the first part of our interrupt..
mov es,ax ;set our es to the old ss
mov ecx,21
copy_loop: ;copy the data in the application stack to our interrupt stack
mov ebx,edx
mov esi,[es:ebx]
push esi
sub edx,4
loop copy_loop

ending_:
mov ax,ds
mov es,ax
mov eax,esp
push eax ;push the stack pointer so we can use it as an argument in our called function
;mov ebx,0x20 ;This is templated! 0x20 should be replaced with interrupt code offset
;crap...we have to have a far ret..hmmm
;only good solution I can think of is you have to tell a spot of memory in the segment(address space) that we can load a far ret capable thing..
;and just have to write something like push esp+8(I think)\ncall int_handler\n retf\n ..or something to deal with that call..
call 0x10:0x14000 ;This is templated! 0x10 should be replaced with interrupt code segment and 0x200 should be replaced with the farcall stub
;offset is at 0x6C, segment at 0x70
pop eax ;trash from the argument
pop edx ;our earlier pushed esp
pop esi ;our earlier pushed SS
mov ss,si
mov edx,esp
pop gs
pop fs
pop es
pop ds
mov al,0 ;this is templated! this should be 0(or anything) for normal interrupt, 1 for master irq, 2 for slave irq -at 0x80
cmp al,1
jz master_irq
cmp al,2
jz slave_irq
return_here:
popa
add esp,8 ;we do this cause we can't use a temp register basically it's just pop [temp] and pop [temp] again
iret

master_irq:
mov al,0x20
out 0x20, al
jmp return_here

slave_irq:
mov al,0x20
out 0xA0, al
out 0x20, al
jmp return_here

hlt ;<--End template size is 160 bytes
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Post by Brendan »

Hi,

Hmm - 160 bytes that are repeated 256 times adds up to 40 KB of stubs! ;)

Some questions:
  • - do you actually need 256 stubs, or is 49 enough (32 for exceptions, 16 for IRQs and one for the kernel API)?
    - can you use 4 different stub types (one for exceptions without error codes, one for exceptions with error codes, one for IRQs and the last for the kernel API)?
    - would it be better to send the EOI to the PIC in C code, so that it's easier to support APICs later?
    - can you recycle code, so that the entry code just pushes some info onto the stack and jumps to generic exit code?
    - do you need templates, or are macros easier?
For example, you could have something like these macros:

Code: Select all

%macro EXCEPTION_STUB_NO_ERROR_CODE 1
    section .text
    align 4
%%1:
    push 0x00000000  ;Put a fake error code on the stack
    push %1                 ;Put the exception number on the stack
    jmp GENERIC_EXCEPTION_STUB
    section .data
    dd %%1
    section .text
%endmacro

%macro EXCEPTION_STUB_WITH_ERROR_CODE 1
    section .text
    align 4
%%1:
    push %1                 ;Put the exception number on the stack
    jmp GENERIC_EXCEPTION_STUB
    section .data
    dd %%1
    section .text
%endmacro


%macro IRQ_STUB 1
    section .text
    align 4
%%1:
    push 0x00000000  ;Put a fake error code on the stack
    push %1                 ;Put the IRQ number on the stack
    jmp GENERIC_IRQ_STUB
    section .data
    dd %%1
    section .text
%endmacro
Followed by some "once only" code like this:

Code: Select all

    section .data
    align 4
interrupt_setup_table:
    EXCEPTION_STUB_NO_ERROR_CODE 0
    EXCEPTION_STUB_NO_ERROR_CODE 1
    EXCEPTION_STUB_NO_ERROR_CODE 2
    EXCEPTION_STUB_NO_ERROR_CODE 3
    EXCEPTION_STUB_NO_ERROR_CODE 4
    EXCEPTION_STUB_NO_ERROR_CODE 5
    EXCEPTION_STUB_NO_ERROR_CODE 6
    EXCEPTION_STUB_NO_ERROR_CODE 7
    EXCEPTION_STUB_WITH_ERROR_CODE 8
    EXCEPTION_STUB_NO_ERROR_CODE 9
    EXCEPTION_STUB_NO_ERROR_CODE 10
    EXCEPTION_STUB_NO_ERROR_CODE 11
    EXCEPTION_STUB_NO_ERROR_CODE 12
    EXCEPTION_STUB_WITH_ERROR_CODE 13
    EXCEPTION_STUB_WITH_ERROR_CODE 14
    EXCEPTION_STUB_NO_ERROR_CODE 15
    EXCEPTION_STUB_NO_ERROR_CODE 16
    EXCEPTION_STUB_WITH_ERROR_CODE 17
    EXCEPTION_STUB_NO_ERROR_CODE 18
    EXCEPTION_STUB_NO_ERROR_CODE 19
    EXCEPTION_STUB_NO_ERROR_CODE 20
    EXCEPTION_STUB_NO_ERROR_CODE 21
    EXCEPTION_STUB_NO_ERROR_CODE 22
    EXCEPTION_STUB_NO_ERROR_CODE 23
    EXCEPTION_STUB_NO_ERROR_CODE 24
    EXCEPTION_STUB_NO_ERROR_CODE 25
    EXCEPTION_STUB_NO_ERROR_CODE 26
    EXCEPTION_STUB_NO_ERROR_CODE 27
    EXCEPTION_STUB_NO_ERROR_CODE 28
    EXCEPTION_STUB_NO_ERROR_CODE 29
    EXCEPTION_STUB_NO_ERROR_CODE 30
    EXCEPTION_STUB_NO_ERROR_CODE 31
    IRQ_STUB 0
    IRQ_STUB 1
    IRQ_STUB 2
    IRQ_STUB 3
    IRQ_STUB 4
    IRQ_STUB 5
    IRQ_STUB 6
    IRQ_STUB 7
    IRQ_STUB 8
    IRQ_STUB 9
    IRQ_STUB 10
    IRQ_STUB 11
    IRQ_STUB 12
    IRQ_STUB 13
    IRQ_STUB 14
    IRQ_STUB 15


    align 4
KERNEL_API_STUB:
    sub esp,8                ;Make stack frame same as other interrupt sources
    pusha
    push ds
    push es
    push fs
    push gs
    call _kernel_API_entry_in_C_code
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp,8
    iret


    align 4
GENERIC_EXCEPTION_STUB:
    pusha
    push ds
    push es
    push fs
    push gs
    call _exception_handler_in_C_code
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp,8
    iret


    align 4
GENERIC_IRQ_STUB:
    pusha
    push ds
    push es
    push fs
    push gs
    call _IRQ_handler_in_C_code
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp,8
    iret
Then, all you'd need to do is construct the IDT using offsets in the table at "interrupt_setup_table" in the data section (plus add an IDT entry for the kernel API), and then provide C functions for "_kernel_API_entry_in_C_code(<lots>)", "_exception_handler_in_C_code(<lots>)" and "_IRQ_handler_in_C_code(<lots>)".

Just some thoughts...

BTW if an IRQ interrupts the kernel's code, there will be no userSS and userESP pushed on the stack by the CPU.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Post Reply