Manuel Angel Suarez Manuel Angel Suarez - 3 months ago 19
C Question

nasm macro doesn't work properly

I'm trying to set up the IDT of my kernel but I'm getting this linking error:

bin/obj/idt.o: In function `setup_idt':
idt.c:(.text+0x9b): undefined reference to `interrupt_handler_1'


The error says that interrupt_handler_1 isn't defined but it is a macro in interrupt_manager.asm:

%macro no_error_code_interrupt_handler 1
global interrupt_handler_%1
interrupt_handler_%1:
cli
push dword 0 ; push 0 as error code
push dword %1 ; push the interrupt number
jmp common_interrupt_handler ; jump to the common handler
%endmacro


Here is the setyup_idt function:

extern void interrupt_handler_1();

void setup_idt()
{
// Set the special idt_pointer
idt_pointer.limit = ( sizeof(struct InterruptDescriptorTableEntry) * 256 ) - 1; // Subsract 1 because sizeof doesn't start from 0
idt_pointer.address = (uint32)&idt;

// Clear the whole idt to zeros
memset(&idt, 0, sizeof(struct InterruptDescriptorTableEntry) * 256 );

for(unsigned int i = 0; i < 256; i++)
{
idt_set_gate(i, (uint32)&interrupt_handler_1, 0x8, 0x8E);
}

__asm__ __volatile__("lidt %0": :"m"(idt_pointer));

}


What did I made wrong?

Extra question: Is there a macro/another way to link automatically the i entry of the GDT to the i interrupt handler, let me try to explain myself better:

What i want to do is something like this:

for(unsigned int i = 0; i < 256; i++)
{
idt_set_gate(i, (uint32)&interrupt_handler_[i], 0x8, 0x8E);
}


Where interrupt_handler[i] would be the interrupt handler_[i] would be replaced by the nasm macro

Answer

You need to expand the macro in your NASM code. A macro definition itself doesn't generate any code. It needs to be explicitly used in the NASM code.

You can use the %rep directive to repeatedly expand the macro with different parameters. Something like this:

    extern common_interrupt_handler

%macro error_code_interrupt_handler 1
global interrupt_handler_%1
interrupt_handler_%1:
   push    dword %1                    ; push the interrupt number
   jmp     common_interrupt_handler    ; jump to the common handler
%endmacro

%macro no_error_code_interrupt_handler 1
global interrupt_handler_%1
interrupt_handler_%1:
   push    dword 0                     ; push 0 as error code
   push    dword %1                    ; push the interrupt number
   jmp     common_interrupt_handler    ; jump to the common handler
%endmacro

; Some CPU exceptions have error codes, some don't

%assign intnum 0
%rep 8 - intnum
    no_error_code_interrupt_handler intnum
    %assign intnum intnum + 1
%endrep

error_code_interrupt_handler 8
no_error_code_interrupt_handler 9

%assign intnum 10
%rep 16 - intnum
    error_code_interrupt_handler intnum
    %assign intnum intnum + 1
%endrep

no_error_code_interrupt_handler 16
error_code_interrupt_handler 17
no_error_code_interrupt_handler 18
no_error_code_interrupt_handler 19
no_error_code_interrupt_handler 20

%assign intnum 21   ; first (currently) unassigned CPU exception
%rep 32 - intnum
    no_error_code_interrupt_handler intnum
    %assign intnum intnum + 1
%endrep

%assign intnum 32   ; first user defined interrupt
%rep 256 - intnum
    no_error_code_interrupt_handler intnum
    %assign intnum intnum + 1
%endrep

; define a table of interrupt handlers for C code to use

    global interrupt_handler_table
interrupt_handler_table:

%assign intnum 0
%rep 256
    dd  interrupt_handler_ %+ intnum
    %assign intnum intnum + 1
%endrep

The code above creates a table of all the interrupt handlers that you can use in your C code like this:

extern uint32 interrupt_handler_table[256];

for(unsigned int i = 0; i < 256; i++)
{
    idt_set_gate(i, interrupt_handler_table[i], 0x8, 0x8E);   
}

Note that I've created an error_code_interrupt_handler macro for those CPU exceptions that do generate error codes. Also, I've removed the unnecessary CLI instruction from your code. Since you're using Interrupt Gates in the IDT, the interrupt enable flag is automatically cleared.

Comments