Nasm - Useful IDT Interface

OS Design, Theory, and Programming

Moderator: Moderators

Nasm - Useful IDT Interface

Postby 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
User avatar
Mike
Site Admin
 
Posts: 463
Joined: Sat Oct 20, 2007 7:58 pm

Postby 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.)
pathos
Moderator
 
Posts: 97
Joined: Thu Jan 10, 2008 6:43 pm
Location: USA

Postby 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
Andyhhp
Moderator
 
Posts: 387
Joined: Tue Oct 23, 2007 10:05 am
Location: 127.0.0.1

Postby 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.
michael
 
Posts: 29
Joined: Thu Nov 15, 2007 12:06 am

Postby pathos » Tue Jan 22, 2008 3:19 am

Thanks Andrew, I'll give that a shot when I get a chance.
pathos
Moderator
 
Posts: 97
Joined: Thu Jan 10, 2008 6:43 pm
Location: USA

Postby 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
User avatar
Mike
Site Admin
 
Posts: 463
Joined: Sat Oct 20, 2007 7:58 pm

Postby 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
Andyhhp
Moderator
 
Posts: 387
Joined: Tue Oct 23, 2007 10:05 am
Location: 127.0.0.1

Postby 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?
pathos
Moderator
 
Posts: 97
Joined: Thu Jan 10, 2008 6:43 pm
Location: USA

Postby 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
Andyhhp
Moderator
 
Posts: 387
Joined: Tue Oct 23, 2007 10:05 am
Location: 127.0.0.1

Postby 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
JonnyPop
 
Posts: 6
Joined: Fri Jan 25, 2008 2:01 am
Location: Québec, Québec

Postby 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
Mike
Site Admin
 
Posts: 463
Joined: Sat Oct 20, 2007 7:58 pm

Postby 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
JonnyPop
 
Posts: 6
Joined: Fri Jan 25, 2008 2:01 am
Location: Québec, Québec

Postby 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
Mike
Site Admin
 
Posts: 463
Joined: Sat Oct 20, 2007 7:58 pm

Postby 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:
User avatar
JonnyPop
 
Posts: 6
Joined: Fri Jan 25, 2008 2:01 am
Location: Québec, Québec


Return to Advanced OS Development

Who is online

Users browsing this forum: No registered users and 1 guest

cron