How to make interrupts

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
Sly
Posts: 7
Joined: Sun Mar 01, 2009 7:57 pm

How to make interrupts

Post by Sly »

I looked on the wiki for this and could not find anything that I understood. How do you create an IDT and write interrupt handlers.

(If you post examples, please make them in asm because C and osdev... I just don't get it :cry: )
User avatar
AndrewAPrice
Member
Member
Posts: 2306
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: How to make interrupts

Post by AndrewAPrice »

The IDT (interrupt descriptor table) is basically an array of 8-byte entries.

An entry consists of:
bits 0..15 - Lower address of the interrupt handler
bits 16..31 - the code segment selector (from your GDT), - the segment which the CPU will enter when this ring fires (e.g. this will be 0x8 and points to your second GDT entry, which is the segment your interrupt handler is in if you follow most GDT tutorials)
bits 32..39 - Unused, set to 0
bit 40 - Set to 1 to handle this interrupt
bits 41..42 - The ring level of the interrupt handler (usually 0 if the interrupt handler is within your kernel)
bit 43 - system segment (not sure what this bit is)
bits 44..47 - Gate type
bits 48..63 - Upper address of the interrupt handler

You can have as little as you want for how many interrupts you want to handle (there's no point going over 256 entries).

Once you have this table, you create a IDT descriptor, which is basically a 6-byte structure:
bits 0..7 - how many entries are in your IDT table
bits 8..47 - the address of you IDT table

Then call LIDT with the address of your IDT descriptor, and it will load the descriptor into a CPU register. Then call STI to enable interrupts.

In it's simplest form, each interrupt handler is just like a function except your return with IRET instead of RET.

There is far more you need to learn though, like remapping your IRQs so they don't overlap with your exceptions.

I recommend you learn the basics of C, so you can read tutorial code. C is very simple, you don't have any higher level constructs like classes, garbage collection, or templates built into the language. All you have to know is the basics of structs, functions, and variables and you're done!

EDIT: Oops, the IDT is a 6-byte structure, not 3 (don't know what I was thinking?!). Thanks Troy.
Last edited by AndrewAPrice on Wed Mar 04, 2009 9:58 pm, edited 1 time in total.
My OS is Perception.
Sly
Posts: 7
Joined: Sun Mar 01, 2009 7:57 pm

Re: How to make interrupts

Post by Sly »

I do understand C, and (i think, anyway) well. I just can't compile it for use in osdev. Anyway, for the IDT, I don't mean to sound like a jerk, but I've read the Intel manual, and I've used Google, but I need a little more details. How do I create an interrupt that can be executed by typing int 32? :?
User avatar
Troy Martin
Member
Member
Posts: 1686
Joined: Fri Apr 18, 2008 4:40 pm
Location: Langley, Vancouver, BC, Canada
Contact:

Re: How to make interrupts

Post by Troy Martin »

Now that you've broken it down into simple chunks, Messiah, I'm going to try to apply this to my 32-bit assembly OS :D
Image
Image
Solar wrote:It keeps stunning me how friendly we - as a community - are towards people who start programming "their first OS" who don't even have a solid understanding of pointers, their compiler, or how a OS is structured.
I wish I could add more tex
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: How to make interrupts

Post by neon »

I do understand C, and (i think, anyway) well. I just can't compile it for use in osdev. Anyway, for the IDT, I don't mean to sound like a jerk, but I've read the Intel manual, and I've used Google, but I need a little more details. How do I create an interrupt that can be executed by typing int 32? :?
MessiahAndrw broke it down pretty well. Using an instruction like int 32 tells the processor to call the interrupt handler installed in IDT entry 32. Each entry stores the address of the function. So, all you need to do is create the interrupt handler, and install a function pointer to it in the IDT entry.

It should not be too hard. If there is anything you dont understand, please be more specific.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
User avatar
Dex
Member
Member
Posts: 1444
Joined: Fri Jan 27, 2006 12:00 am
Contact:

Re: How to make interrupts

Post by Dex »

Simple example in asm

Code: Select all

								; pm4.asm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	pm4.asm - protected-mode demo code
;	Christopher Giese <geezer[AT]execpc.com>
;
;	Release date 9/28/98. Distribute freely. ABSOLUTELY NO WARRANTY.
;	Assemble with NASM:	nasm -o pm4.com pm4.asm
;
; Demonstrates:
;	- CPU detection (386 or better)
;	- Validation in software interrupt handler
;Fixes/changes:
;	- IDT now contains true interrupt gates (type 0x8E) instead
;	  of trap gates (type 0x8F)
;	- spin: jmp spin changed to jmp $
;	- Byte 6 of descriptors (flags/limit 19:16) changed from
;	  0xFC to 0xCF
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[SECTION .text]
[ORG 0x100]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	16-bit real mode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[BITS 16]
; check for protected or V86 mode. Code from Freedows '98 ldr_asm.asm
; Copyright (C) 1997 Joachim Breitsprecher <[email protected]>
start:	smsw ax
	test al,1		; look at PE bit of MSW (CR0)
	je cpu_chk
	mov ax,0x4C01		; exit to DOS with error code 1
	int 0x21

; determine CPU type. Code from Freedows '98 ldr_asm.asm
; Copyright (C) 1997 Joachim Breitsprecher <[email protected]>
; I (Chris Giese) have not tested this code with 8088/'286 systems.
cpu_chk:cli
	pushf
		pushf
		pop ax
		mov bx,ax	; ax=bx=flags
		and ax,0x0FFF	; ax=flags & 0x0FFF
		or bx,0x7000	; bx=flags | 0x7000
		push ax		; try clearing b15:b12 of flags
		popf
		pushf
		pop ax		; ax=result
		push bx		; try setting b14:b12 of flags
		popf
		pushf
		pop bx		; bx=result
	popf
	and ax,0xF000
	cmp ax,0xF000
	je not_386		; 80(1)86/88 sets b15:b12
	test bx,0x7000		; 80286 clears b14:b12
	jne start2
not_386:mov ax,0x4C02		; exit to DOS with error code 2
	int 0x21

; set base of code/data descriptors to CS<<4/DS<<4 (CS=DS)
start2:	xor ebx,ebx
	mov bx,cs		; BX=segment
	shl ebx,4		; EBX=linear address of segment base
	mov eax,ebx
	mov [gdt1 + 2],ax
	mov [gdt2 + 2],ax
	mov [gdt3 + 2],ax
	shr eax,16
	mov [gdt1 + 4],al
	mov [gdt2 + 4],al
	mov [gdt3 + 4],al
	mov [gdt1 + 7],ah
	mov [gdt2 + 7],ah
	mov [gdt3 + 7],ah
; point gdtr to the gdt, point idtr to the idt
	add ebx,gdt		; EBX=linear address of gdt
	mov [gdtr + 2],ebx
	add ebx,idt - gdt	; EBX=linear address of idt
	mov [idtr + 2],ebx
; disable interrupts
	cli
; load GDT and IDT for full protected mode
	lgdt [gdtr]
	lidt [idtr]
; save real-mode CS in BP
	mov bp,cs
; set PE [protected mode enable] bit and go
	mov eax,cr0
	or al,1
	mov cr0,eax
	jmp SYS_CODE_SEL:do_pm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	32-bit protected mode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[BITS 32]
do_pm:	mov ax,SYS_DATA_SEL
	mov ds,ax		; not segments anymore: SELECTORS
	mov ss,ax
	nop
	mov es,ax
	mov fs,ax
	mov gs,ax
; say hello!
	lea esi,[hi_msg]
	call wrstr
; try an interrupt
	int 0x20
; switch to 16-bit protected mode on your way to real mode
	jmp REAL_CODE_SEL:do_16
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	character-output video routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
wrch:	push gs
	push ecx
	push ebx
	push eax
		mov ax,LINEAR_SEL
		mov gs,ax
; (Y * 80 + X) * 2 --> EAX
		movzx eax,byte [CsrY]
		mov cl,80
		mul cl
		add al,[CsrX]
		adc ah,0
		shl eax,1
; EAX + 0xB8000 --> EBX; store char
		lea ebx,[eax + 0xB8000]
		pop eax
		push eax
		mov [gs:ebx],al
; advance cursor
		mov cx,[CsrX]
		inc cl
		cmp cl,80	; cursor off right side of screen?
		jb wrch2
		xor cl,cl	; yes, wrap to left side...
		inc ch		; ...and down one line
		cmp ch,25	; cursor off bottom of screen?
		jb wrch2
		xor ch,ch	; yes, wrap to top left corner (no scroll)
wrch2:		mov [CsrX],cx
	pop eax
	pop ebx
	pop ecx
	pop gs
	ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	string-output video routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
wrstr:	push esi
	push eax
		cld
		jmp wrstr2
wrstr1:		call wrch
wrstr2:		lodsb
		or al,al
		jne wrstr1
	pop eax
	pop esi
	ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	default handler for interrupts/exceptions
;	prints " Unhandled interrupt!"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
unhand:	cli
	lea esi,[unhand_msg]
	call wrstr
	jmp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	handler for INT 0x20
;	validates interrupt and prints " Hey, INT 0x20 occured!"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
isr20:	pusha
		mov ebx,[32+esp]	; get stacked EIP
; did we get here because of INT 0x20 instruction?
		cmp word [ebx - 2],0x20CD
		jne unhand		; no, goto unhand
		lea esi,[isr20_msg]	; yes, print a message and return
		call wrstr
	popa
	iret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	16-bit protected mode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[BITS 16]
; switch to 16-bit stack and data
do_16:	mov ax,REAL_DATA_SEL
	mov ds,ax
	mov ss,ax
	nop
; push real-mode CS:IP
	lea bx,[do_rm]
	push bp
	push bx
; clear PE [protected mode enable] bit and return to real mode
		mov eax,cr0
		and al,0xFE
		mov cr0,eax
		retf		; jumps to do_rm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	16-bit real mode again
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; restore real-mode segment register values
do_rm:	mov ax,cs
	mov ds,ax
	mov ss,ax
	nop
	mov es,ax
	mov fs,ax
	mov gs,ax
; point to real-mode IDTR
	lidt [ridtr]
; re-enable interrupts
	sti
; exit to DOS with errorlevel 0
	mov ax,0x4C00
	int 0x21
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
CsrX:	db 0
CsrY:	db 0

hi_msg:	db "Hello, how's it going?", 0

unhand_msg:
	db " Unhandled interrupt!", 0

isr20_msg:
	db " Hey, INT 0x20 occured!", 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	16-bit limit/32-bit linear base address of GDT and IDT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
gdtr:	dw gdt_end - gdt - 1	; GDT limit
	dd gdt			; linear, physical address of GDT

idtr:	dw idt_end - idt - 1	; IDT limit
	dd idt			; linear, physical address of IDT

; an IDTR 'appropriate' for real mode
ridtr:	dw 0xFFFF		; limit=0xFFFF
	dd 0			; base=0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	global descriptor table (GDT)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; null descriptor
gdt:	dw 0			; limit 15:0
	dw 0			; base 15:0
	db 0			; base 23:16
	db 0			; type
	db 0			; limit 19:16, flags
	db 0			; base 31:24
; linear data segment descriptor
LINEAR_SEL	equ	$-gdt
	dw 0xFFFF		; limit 0xFFFFF
	dw 0			; base for this one is always 0
	db 0
	db 0x92			; present, ring 0, data, expand-up, writable
	db 0xCF			; page-granular, 32-bit
	db 0
; code segment descriptor
SYS_CODE_SEL	equ	$-gdt
gdt1:	dw 0xFFFF
	dw 0			; (base gets set above)
	db 0
	db 0x9A			; present, ring 0, code, non-conforming, readable
	db 0xCF
	db 0
; data segment descriptor
SYS_DATA_SEL	equ	$-gdt
gdt2:	dw 0xFFFF
	dw 0			; (base gets set above)
	db 0
	db 0x92			; present, ring 0, data, expand-up, writable
	db 0xCF
	db 0
; code segment descriptor that is 'appropriate' for real mode
; (16-bit, limit=0xFFFF)
REAL_CODE_SEL	equ	$-gdt
gdt3:	dw 0xFFFF
	dw 0			; (base gets set above)
	db 0
	db 0x9A			; present, ring 0, code, non-conforming, readable
	db 0			; byte-granular, 16-bit
	db 0
; data segment descriptor that is 'appropriate' for real mode
; (16-bit, limit=0xFFFF)
REAL_DATA_SEL	equ	$-gdt
gdt4:	dw 0xFFFF
	dw 0			; (base gets set above)
	db 0
	db 0x92			; present, ring 0, data, expand-up, writable
	db 0			; byte-granular, 16-bit
	db 0
gdt_end:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	interrupt descriptor table (IDT)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32 reserved interrupts:
idt:	dw unhand		; entry point 15:0
	dw SYS_CODE_SEL		; selector
	db 0			; word count
	db 0x8E			; type (32-bit Ring 0 interrupt gate)
	dw 0			; entry point 31:16 (XXX - unhand >> 16)

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0

	dw unhand
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0
; user interrupt handler
	dw isr20
	dw SYS_CODE_SEL
	db 0
	db 0x8E
	dw 0
idt_end:

Sly
Posts: 7
Joined: Sun Mar 01, 2009 7:57 pm

Re: How to make interrupts

Post by Sly »

Okay, one thing. How do you create a pointer to your handler in your IDT entry?
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: How to make interrupts

Post by neon »

Sly wrote:Okay, one thing. How do you create a pointer to your handler in your IDT entry?
Just get the address of your interrupt handler, and store it in the IDT entry. The lower 16 bits of the address go in the first 16 bits of the IDT entry, the upper 15 bits go into bits 48-63 of the IDT entry. This makes up the full 32 bit linear address of your interrupt handler. (This can be seen in the IDT entry bit format posted earlier.)
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
User avatar
Troy Martin
Member
Member
Posts: 1686
Joined: Fri Apr 18, 2008 4:40 pm
Location: Langley, Vancouver, BC, Canada
Contact:

Re: How to make interrupts

Post by Troy Martin »

Couple questions:

1. Does this say, in NASM assembly, that the interrupt is a trap gate in ring 0?

Code: Select all

db 11110001b
2. Messiah said that the IDT descriptor is 3 bytes, but the wiki says 6. Which one is it?
Image
Image
Solar wrote:It keeps stunning me how friendly we - as a community - are towards people who start programming "their first OS" who don't even have a solid understanding of pointers, their compiler, or how a OS is structured.
I wish I could add more tex
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: How to make interrupts

Post by Combuster »

it should be 6 bytes
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
Troy Martin
Member
Member
Posts: 1686
Joined: Fri Apr 18, 2008 4:40 pm
Location: Langley, Vancouver, BC, Canada
Contact:

Re: How to make interrupts

Post by Troy Martin »

Combuster wrote:it should be 6 bytes
So a dw for the number of entries and a dd for the location of the start of the IDT.
Image
Image
Solar wrote:It keeps stunning me how friendly we - as a community - are towards people who start programming "their first OS" who don't even have a solid understanding of pointers, their compiler, or how a OS is structured.
I wish I could add more tex
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: How to make interrupts

Post by Combuster »

a dw for the limit of the table (= 8 * entries - 1)
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Post Reply