Page 1 of 1
Nasm - Useful IDT Interface
Posted: Sat Jan 19, 2008 7:48 pm
by Mike
Hey everyone,
In one of my previous bootloaders I have written some code to install an IDT, and allow easy interrupt handling management. It is written using NASM, and can be very useful when debugging the bootloader.
I decided to post it here in hopes that it will help anyone out with their code.
Here it is...
Code: Select all
%ifndef __IDT_INC_67343546FDCC56AAB872_INCLUDED__
%define __IDT_INC_67343546FDCC56AAB872_INCLUDED__
bits 32
struc idt_entry
.m_baseLow resw 1
.m_selector resw 1
.m_reserved resb 1
.m_flags resb 1
.m_baseHi resw 1
endstruc
struc idt_ptr
.m_size resw 1
.m_base resd 1
endstruc
_IDT:
%rep 256
istruc idt_entry
at idt_entry.m_baseLow, dw 0
at idt_entry.m_selector, dw 0x8
at idt_entry.m_reserved, db 0
at idt_entry.m_flags, db 010001111b
at idt_entry.m_baseHi, dw 0
iend
%endrep
_IDT_End:
_IDT_Size db _IDT_End - _IDT
_Desc_Size db 8
_IDT_Ptr:
istruc idt_ptr
at idt_ptr.m_size, dw 0
at idt_ptr.m_base, dd 0
iend
;*********************
; Loads idt into idtr
;*********************
IDT_Load:
cli
lidt [_IDT_Ptr]
ret
;********************
; Installs idt
;********************
IDT_Install:
pusha
cli
mov word [_IDT_Ptr+idt_ptr.m_size], _IDT_Size - 1
mov dword [_IDT_Ptr+idt_ptr.m_base], _IDT
call IDT_Load
popa
ret
;***********************
; Install interrupt gate
; EAX=>Interrupt Number
; EBX=>Base address of ir
;***********************
IDT_SetGate:
pusha
mov edx, 8
mul dx
add eax, _IDT
mov word [eax+idt_entry.m_baseLow], bx
shr ebx, 16
mov word [eax+idt_entry.m_baseHi], bx
popa
ret
%endif
This code must be used within protected mode running in ring 0.
To use it, simply install your IRQs:
Code: Select all
; install idt
call IDT_Install
; install our IRQs
mov eax, 0
mov ebx, IRQ_0
call IDT_SetGate ; installs interrupt 0 handler
mov eax, 1
mov ebx, IRQ_1
call IDT_SetGate ; installs interrupt 1 handler
mov eax, 2
mov ebx, IRQ_2
call IDT_SetGate ; installs interrupt 2 handler
; etc up to 255th handler. This can be looped though
Hope this is helpful
In the series, We will be using C for setting up the IDT and interrupts, so this code might not be used. Who knows...Mabey we might put this in our bootloader...
Posted: Mon Jan 21, 2008 6:40 pm
by pathos
Code: Select all
; install our IRQs
mov eax, 0
mov ebx, IRQ_0
call IDT_SetGate ; installs interrupt 0 handler
mov eax, 1
mov ebx, IRQ_1
call IDT_SetGate ; installs interrupt 1 handler
mov eax, 2
mov ebx, IRQ_2
call IDT_SetGate ; installs interrupt 2 handler
; etc up to 255th handler. This can be looped though
How can this be looped? (That wording sounds like I don't believe you, but that's not the case; I just don't know how to do it.)
Posted: Mon Jan 21, 2008 9:14 pm
by Andyhhp
First you need an array of pointers to the IRQ functions.
Then you just use a normal loop such as: (this code isn't tested but it should work)
Code: Select all
IRQ_0:
...
IRQ_1:
...
IRQ_TABLE:
dd IRQ_0
dd IRQ_1
...
Load_IRQs:
pusha
Load_Loop:
mov edi,IRQ_TABLE ;Get array pointer
xor eax,eax ;Zero eax
mov ecx,0FFh ;Loop 255 times
mov eax,ecx ;Set eax to current gate number
mov ebx,ecx ;Need to find current gate address in array
shl ebx,2 ;Multiply by 4 as 4 bytes per entry
add ebx,edi ;Add base of array so get absolute address
call IDT_SetGate
loop Load_Loop
popa
ret
Hope this helps,
Andrew
Posted: Tue Jan 22, 2008 1:50 am
by michael
omg this code is great

.. I've been looking for ages on the net for something like this.
It also looks very interesting how it works.. using structures it appears. I didn't know that was possible in NASM.
Posted: Tue Jan 22, 2008 3:19 am
by pathos
Thanks Andrew, I'll give that a shot when I get a chance.
Posted: Tue Jan 22, 2008 11:30 pm
by Mike
michael wrote:It also looks very interesting how it works.. using structures it appears. I didn't know that was possible in NASM.
It does use structures, and a %rep macro because I didnt want to write the same thing 256 times. I do wish these were more documented, though. (although they are listed in the nasm manuals.)
Posted: Wed Jan 23, 2008 3:36 pm
by Andyhhp
I was aware that NASM had structures and macros.
While I have used macros before, and their names are very self explanatory, I have never found any decent documentation about structures.
Is your example as extensive as NASM structures are or is there more functionality that can be used?
Posted: Thu Jan 24, 2008 7:09 pm
by pathos
Andyhhp wrote:First you need an array of pointers to the IRQ functions.
Then you just use a normal loop such as: (this code isn't tested but it should work)
Code: Select all
IRQ_0:
...
IRQ_1:
...
IRQ_TABLE:
dd IRQ_0
dd IRQ_1
...
Load_IRQs:
pusha
Load_Loop:
mov edi,IRQ_TABLE ;Get array pointer
xor eax,eax ;Zero eax
mov ecx,0FFh ;Loop 255 times
mov eax,ecx ;Set eax to current gate number
mov ebx,ecx ;Need to find current gate address in array
shl ebx,2 ;Multiply by 4 as 4 bytes per entry
add ebx,edi ;Add base of array so get absolute address
call IDT_SetGate
loop Load_Loop
popa
ret
Hope this helps,
Andrew
I just got finished trying to code and it didn't work. However, I moved the "mov ecx, 0FFh" before the loop begins and that fixed it. Will the loop still exectute 255 times with ecx being set before the loop?
Posted: Thu Jan 24, 2008 10:03 pm
by Andyhhp
Yes - you are totally correct.
As it was, it was resetting the loop counter every loop so it would never end.
It should have the mov ecx,0FFh before the loop. That way the loop will run 255 times then end.
Just to note - this loop will load the 255th gate first and the 1th last. This shouldn't make a difference to the overall effect but it is worth knowing in case any problems arise.
Sorry about the error - shows the importance of testing
Andrew
Posted: Tue Feb 05, 2008 1:00 am
by JonnyPop
Personally, I would prefer this kind of "loop":
Code: Select all
%assign i 0
%macro SetIRQHandler 1
%if i<256
mov eax, i
mov ebx, %1
call IDT_SetGate
%else
%error More than 256 Interrupt handlers.
%endif
%assign i i+1
%endmacro
That would be called like this:
Code: Select all
IRQ_0:
iret
IRQ_1:
iret
...
_func:
call IDT_Install
SetIRQHandler IRQ_0
SetIRQHandler IRQ_1
...
(IMHO indeed)
And I was wondering, Mike"Fry" (I think maybe we should call you "
Fry" from now on as there seem to be quite a few Mike on this forum...): Do the "IDT_Load" function really needs to be isolated or could we just do it that way in "IDT_Install" ? :
Code: Select all
;********************
; Installs idt
;********************
IDT_Install:
pusha
cli
mov word [_IDT_Ptr+idt_ptr.m_size], _IDT_Size - 1
mov dword [_IDT_Ptr+idt_ptr.m_base], _IDT
lidt [_IDT_Ptr]
popa
ret
I don't see why we would call "IDT_Load" from anywhere else in the code...

?
Posted: Tue Feb 05, 2008 1:23 am
by Mike
JonnyPop wrote:
And I was wondering, Mike"Fry" (I think maybe we should call you "
Fry" from now on as there seem to be quite a few Mike on this forum...): Do the "IDT_Load" function really needs to be isolated or could we just do it that way in "IDT_Install" ? :
It does not *need* to be isolated if you do not want it to. I was following a practice of "one routine having one purpose" when writing the code, which is considered "good programming practice" in alot of fields. This practice is debatable, however. That is, in my code it might be coinsidered overkill if you are needing speed.
Also, its okay to call me "fry"

I noticed the amount of Mikes on this forum myself...
Posted: Tue Feb 05, 2008 11:05 pm
by JonnyPop
As of today, almost 10% of the registered users are Mikes ! (3/34 maybe more...)
edit: Ok sorry this post wasn't really necessary and might lead us into an off-topic and not-really-useful discussion... Let's pretend I never wrote it and continue on...
Posted: Wed Feb 06, 2008 2:59 am
by Mike
JonnyPop wrote:As of today, almost 10% of the registered users are Mikes ! (3/34 maybe more...)

You know what? "fry" sounds a little "crusty" for a name ie like fast food... Mabey I will keep Mike

Posted: Thu Feb 07, 2008 4:17 am
by JonnyPop
No problem 'Mike the Admin' !
And to put this thread back on its rail, I updated my little macro :
Code: Select all
%assign nIRQh 0
%macro SetIRQHandler 1
%if nIRQh<256
mov eax, nIRQh
mov ebx, %1
call IDT_SetGate
%else
%error More than 256 Interrupt handlers.
%endif
%assign nIRQh nIRQh+1
%endmacro
%macro SetIRQUnhandled 1
SetIRQHandler %1
%rep 256-(nIRQh-1)
inc eax
call IDT_SetGate
%endrep
%endmacro
So you can do something like:
Code: Select all
IRQ_1: ...
IRQ_2: ...
...
IRQ_NULL: ...
_func:
call IDT_Install
SetIRQHandler IRQ_0
SetIRQHandler IRQ_1
%rep 14
SetIRQHandler IRQ_NULL ; Unhandled IRQs until the 16th
%endrep
SetIRQHandler IRQ_16 ; This one is handled
... ; You can add some later here...
SetIRQUnhandled IRQ_NULL ; And this one automatically adjusts
; to fill the rest
Maybe we could make it even more convenient by mixing this approach with the one given by
pathos using an 'IRQ_TABLE'...
I think I'll look at this while waiting for tutorial 15...
Until then I hope this can be useful for somebody. If so let me know.
