Getting the Interrupt Number

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
purevoid

Getting the Interrupt Number

Post by purevoid »

Hi,

How can I find the current interrupt number inside my ISR?

Basically I want to do something like:

Code: Select all

void isr_wrapper() {
  uint8 int_num = get_current_interrupt_number();
  actual_interrupt_handler(int_num);
}

void actual_interrupt_handler(uint8 num) {
  switch (num) {
     case 0: ... break;
     case 1: ... break;
     /* etc. 8?
  }
}
However, the difference from the above is that I want to manage interrupt handling in OCaml (yes, we've got OCaml support in our kernel); I think it would be easier to handle interrupts in OCaml if could pass the interrupt number to it, otherwise it seems like a wasted effort.
HOS

Re:Getting the Interrupt Number

Post by HOS »

basically you have to have a different label in each IDT entry (AFAIK)

here is what i use:

Code: Select all

;idt.inc
;Author: Josh Holtrop
;Date: 10/30/03
;Modified: 07/06/04

idtr:
   dw   50*8-1      ;size of idt
   dd   IDT_V      ;address of idt


%macro isr_label 1
isr_%1:
   push   eax
   mov   eax, %1
   jmp   isr_main
%endmacro

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

isr_main:
   pusha
   push   ds
   push   es

   push   eax   ;interrupt number
   call   _isr
   add   esp, 4

   pop   es
   pop   ds
   popa
   pop   eax      ;original saved eax

   iret

this defines the labels, you would need to use these to populate the IDT to point to them

then define a C function called isr that takes an int argument (the interrupt number) and thats it...

i'm sure someone can point out how to improve on this or make it more elegant

ps this is nasm code...
Curufir

Re:Getting the Interrupt Number

Post by Curufir »

Two ways off the top of my head.

a) Use the stack values for the return address to look back at the previous instruction and interpret the machine code for the interrupt (0xCD 0xNN from memory). This will only work for software interrupts of course (Although with some finesse it could be extended to IRQs as well).

b) Just make your IDT entries equivalent to gotos in your ISR wrapper.

For example:

Code: Select all

void isr_wrapper() {
  uint8 int_num = 0;

isr0:
  int_num++;
isr1:
  int_num++;
isr2:
  int_num++;

....

  actual_interrupt_handler(int_num);
}
Be aware that this will make some C people very annoyed though ;D.

c) (For fun and amusement) Create 256 identical GDT entries and arrange your IDT to load CS with the value of int_num*8.
purevoid

Re:Getting the Interrupt Number

Post by purevoid »

A simple question about NASM...

I'm creating an elf32 kernel image, so do I specify elf as output format, or should it be aout?
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Getting the Interrupt Number

Post by Candy »

Curufir wrote: a) Use the stack values for the return address to look back at the previous instruction and interpret the machine code for the interrupt (0xCD 0xNN from memory). This will only work for software interrupts of course (Although with some finesse it could be extended to IRQs as well).
No, you can't extend that to irq's by their nature. For software interrupts it's plausible though...
b) Just make your IDT entries equivalent to gotos in your ISR wrapper.

For example:

Code: Select all

void isr_wrapper() {
  uint8 int_num = 0;

isr0:
  int_num++;
isr1:
  int_num++;
isr2:
  int_num++;

....

  actual_interrupt_handler(int_num);
}
Be aware that this will make some C people very annoyed though ;D.
No it won't... it's equivalent to switch fallthrough cases. PS: if you number them down the number you keep left is equal to the number of the interrupt instead of the inverse.
c) (For fun and amusement) Create 256 identical GDT entries and arrange your IDT to load CS with the value of int_num*8.
In a way, you *could* just push one register and dunk the int number in there, saving you 2k on GDT area. Or make an engine which dynamically writes code for interrupts as you register them :D
Curufir

Re:Getting the Interrupt Number

Post by Curufir »

Candy wrote:
Curufir wrote: a) Use the stack values for the return address to look back at the previous instruction and interpret the machine code for the interrupt (0xCD 0xNN from memory). This will only work for software interrupts of course (Although with some finesse it could be extended to IRQs as well).
No, you can't extend that to irq's by their nature. For software interrupts it's plausible though...
It's something that sprang to mind when I considered reading the interrupt instruction. You'd do it for IRQs by reading the In Service Register on the PIC(s) instead of backtracking to the software interrupt. It'd be a real clumsy way of doing things, but I'm sure it's possible.

TBH I'm not sure why you'd want the information in the first place. If an ISR is to be run when an interrupt is called then hook it up in the IDT (An ISR should know which interrupt it corresponds to). Any interrupt that doesn't have a corresponding ISR should return an error. I don't see the actual interrupt number as useful information.
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Re:Getting the Interrupt Number

Post by Colonel Kernel »

Candy wrote: Or make an engine which dynamically writes code for interrupts as you register them :D
I think there's actually a precedent for that in NT.

From Inside Windows NT 2nd Ed. (p. 89, 2nd paragraph):
The kernel provides a portable mechanism - a kernel control object called an interrupt object - that allows device drivers to register ISRs for their devices. An interrupt object contains all the information the kernel needs to associate a device ISR with a particular level of interrupt, including the address of the ISR, the IRQL at which the device interrupts, and the entry in the kernel's IDT with which the ISR should be associated. When an interrupt object is initialized, a few instructions of assembly language code, called the dispatch code, are stored in the object. When an interrupt occurs, this code is executed. This interrupt-object resident code calls the real interrupt dispatcher, passing it a pointer to the interrupt object. The interrupt object contains information this second dispatcher routine needs in order to locate and properly call the ISR the device driver provides. This two-step process is required because there is no way to pass a pointer to the interrupt object (or any other argument for that matter) on the initial dispatch since the initial dispatch is done by hardware.
Kinda neat. I think the mechanism they describe was also used in the Alpha AXP version of NT.

<edit>Unless of course you mean dynamically generate the entire handler, and not just the code that dispatches to it. ;)</edit>
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
purevoid

Re:Getting the Interrupt Number

Post by purevoid »

That dynamically generating dispatcher code sounds almost like what we'd need. How to do that, I have no idea...

But we've taken a slightly different approach, plus my understanding of interrupts was a little fuzzy before now.

For anyone interested in what's trying to be done, and maybe able to shed an extra bit of light on... we're writing our kernel in OCaml, and want to write interrupt handlers in OCaml too.

The biggest problem we face is calling into the OCaml function. Since we need to use a C callback function to call the actual OCaml function, I'm at a loss of how to put this information into the IDT, since you can't pass parameters easily.
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Getting the Interrupt Number

Post by Candy »

purevoid wrote: That dynamically generating dispatcher code sounds almost like what we'd need. How to do that, I have no idea...
Make it less dynamic and within the reasonability bounds. The more you constrain it the easier it gets.

My personal easiest example: restrict target address + interrupt number to predefined numbers

Code: Select all

/*
code in bytes is equivalent to push $x (5b); call $y (5b); add esp, 4 (3b); iret (1b);
*/
char code[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x83, 0xC4, 0x04, 0xCF, 0x00, 0x00};
char *newarray = (char *)malloc(16);
memcpy(newarray, code, 16);
newarray[1] = int_number;
newarray[6] = target_addr & 0xFF;
newarray[7] = (target_addr >> 8) & 0xFF;
newarray[8] = (target_addr >> 16) & 0xFF;
newarray[9] = (target_addr >> 24) & 0xFF;
This generates code to jump to $j with $i as parameter.

Code: Select all

But we've taken a slightly different approach, plus my understanding of interrupts was a little fuzzy before now.

For anyone interested in what's trying to be done, and maybe able to shed an extra bit of light on... we're writing our kernel in OCaml, and want to write interrupt handlers in OCaml too.

The biggest problem we face is calling into the OCaml function. Since we need to use a C callback function to call the actual OCaml function, I'm at a loss of how to put this information into the IDT, since you can't pass parameters easily.
[quote][/quote]
Use the code generator to generate an asm function to call C, and make some C wrapper that allows you to use it elegantly.

PS: no guarantees on the correctness of the code, you might want to ram some generated code through a disassembler first.
purevoid

Re:Getting the Interrupt Number

Post by purevoid »

I think this dynamic code generation isn't quite so hard afterall.

Anyways, here is some semi-pseudo code of how the ISR would look:

Code: Select all

/* asm */
pusha
push gs
push fs
push es
push ds
/* C */
callback(*closure, Val_unit); /* ignoring result */
/* asm */
pop ds
pop es
pop fs
pop gs
popa
iret
Where closure would be the only variable argument. Now to dynamically generate the code, I'd need to use some C code to get the address of callback, and Val_unit right? Insert those into the ASM, and then the address of closure? And generate a call instruction...

Hope I'm on the right track... and think I remember how C calling convention works.

And to finish off, I'd pass the address of the starting instruction to my set_vector function for IDT... so if I had a fixed size array for the char* representing the machine code, I'd just pass the address of an entry in this array, after filling in the necessary blanks in the char*?

Sounds a little too easy to be true...
purevoid

Re:Getting the Interrupt Number

Post by purevoid »

Been doing some reading of the Intel manuals, and here's the code I've come up with....

Code: Select all

char *isr_wrapper = {
  /* pusha, push gs, fs, es, ds */
  0x60, 0x0F, 0xA8, 0x0F, 0xA0, 0x06, 0x1E,
  /* push parameter 2 onto stack */
  0x68, 0x00, 0x00, 0x00, 0x00,
  /* push parameter 1 onto stack */
  0x68, 0x00, 0x00, 0x00, 0x00,
  /* call function */
  0x9A, 0x00, 0x00, 0x00, 0x00,
  /* pop ds, es, fs, gs, popa, iret */
  0x1F, 0x07, 0x0F, 0xA1, 0x0F, 0x09, 0x61, 0xCF
};
Where I'd replace the first lot of 0x00s with address of Val_unit, the second lot with address of *closure, and the last with address of the function `callback'.

It's just the call that I'm a little confused about, since there are a few different opcodes. From the Intel manual, I decided to use:

Opcode: 9A cd, Instruction: CALL ptr16:16, Description: Call far, absolute, address given in operand.

Assuming that's all correct, I'd just have to write the code to actually do all this work.
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Getting the Interrupt Number

Post by Candy »

purevoid wrote: Been doing some reading of the Intel manuals, and here's the code I've come up with....

Code: Select all

char *isr_wrapper = {
  /* pusha, push gs, fs, es, ds */
  0x60, 0x0F, 0xA8, 0x0F, 0xA0, 0x06, 0x1E,
  /* push parameter 2 onto stack */
  0x68, 0x00, 0x00, 0x00, 0x00,
  /* push parameter 1 onto stack */
  0x68, 0x00, 0x00, 0x00, 0x00,
  /* call function */
  0x9A, 0x00, 0x00, 0x00, 0x00,
  /* pop ds, es, fs, gs, popa, iret */
  0x1F, 0x07, 0x0F, 0xA1, 0x0F, 0x09, 0x61, 0xCF
};
Where I'd replace the first lot of 0x00s with address of Val_unit, the second lot with address of *closure, and the last with address of the function `callback'.

It's just the call that I'm a little confused about, since there are a few different opcodes. From the Intel manual, I decided to use:

Opcode: 9A cd, Instruction: CALL ptr16:16, Description: Call far, absolute, address given in operand.

Assuming that's all correct, I'd just have to write the code to actually do all this work.
They are pretty much OK, all of them.

Except:

1. You indeed have the wrong jump. In 32-bits Pmode you need either a 16:32 jump (which you do have, but you need two more bytes), or an absolute jump with a 32-bit offset (as is done by FF /2). Page 3-65 in my manual.
2. You need to get those two arguments off the stack before returning.
Post Reply