Nasm - Useful IDT Interface

OS Design, Theory, and Programming

Moderator:Moderators

Post Reply
User avatar
Mike
Site Admin
Posts:465
Joined:Sat Oct 20, 2007 7:58 pm
Contact:
Nasm - Useful IDT Interface

Post by Mike » Sat Jan 19, 2008 7:48 pm

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...
Lead Programmer for BrokenThorn Entertainment, Co.
Website: http://www.brokenthorn.com
Email: webmaster@brokenthorn.com

pathos
Moderator
Posts:97
Joined:Thu Jan 10, 2008 6:43 pm
Location:USA

Post by pathos » Mon Jan 21, 2008 6:40 pm

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.)

Andyhhp
Moderator
Posts:387
Joined:Tue Oct 23, 2007 10:05 am
Location:127.0.0.1
Contact:

Post by Andyhhp » Mon Jan 21, 2008 9:14 pm

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
Image

michael
Posts:29
Joined:Thu Nov 15, 2007 12:06 am

Post by michael » Tue Jan 22, 2008 1:50 am

omg this code is great :shock: :D.. 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.

pathos
Moderator
Posts:97
Joined:Thu Jan 10, 2008 6:43 pm
Location:USA

Post by pathos » Tue Jan 22, 2008 3:19 am

Thanks Andrew, I'll give that a shot when I get a chance.

User avatar
Mike
Site Admin
Posts:465
Joined:Sat Oct 20, 2007 7:58 pm
Contact:

Post by Mike » Tue Jan 22, 2008 11:30 pm

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.)
Lead Programmer for BrokenThorn Entertainment, Co.
Website: http://www.brokenthorn.com
Email: webmaster@brokenthorn.com

Andyhhp
Moderator
Posts:387
Joined:Tue Oct 23, 2007 10:05 am
Location:127.0.0.1
Contact:

Post by Andyhhp » Wed Jan 23, 2008 3:36 pm

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?
Image

pathos
Moderator
Posts:97
Joined:Thu Jan 10, 2008 6:43 pm
Location:USA

Post by pathos » Thu Jan 24, 2008 7:09 pm

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?

Andyhhp
Moderator
Posts:387
Joined:Tue Oct 23, 2007 10:05 am
Location:127.0.0.1
Contact:

Post by Andyhhp » Thu Jan 24, 2008 10:03 pm

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 :D

Andrew
Image

User avatar
JonnyPop
Posts:6
Joined:Fri Jan 25, 2008 2:01 am
Location:Québec, Québec

Post by JonnyPop » Tue Feb 05, 2008 1:00 am

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... :? ?
Last edited by JonnyPop on Tue Feb 05, 2008 11:01 pm, edited 1 time in total.

User avatar
Mike
Site Admin
Posts:465
Joined:Sat Oct 20, 2007 7:58 pm
Contact:

Post by Mike » Tue Feb 05, 2008 1:23 am

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...
Lead Programmer for BrokenThorn Entertainment, Co.
Website: http://www.brokenthorn.com
Email: webmaster@brokenthorn.com

User avatar
JonnyPop
Posts:6
Joined:Fri Jan 25, 2008 2:01 am
Location:Québec, Québec

Post by JonnyPop » Tue Feb 05, 2008 11:05 pm

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...

User avatar
Mike
Site Admin
Posts:465
Joined:Sat Oct 20, 2007 7:58 pm
Contact:

Post by Mike » Wed Feb 06, 2008 2:59 am

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 :)
Lead Programmer for BrokenThorn Entertainment, Co.
Website: http://www.brokenthorn.com
Email: webmaster@brokenthorn.com

User avatar
JonnyPop
Posts:6
Joined:Fri Jan 25, 2008 2:01 am
Location:Québec, Québec

Post by JonnyPop » Thu Feb 07, 2008 4:17 am

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. :wink:

Post Reply