Sorry about just throwing that interrupt handler...
I thought many of the issues involved in interrupt/exception handling were implied in the little segment I copied before.
The main idea is that the interrupt/exception will land at the correct offset from excpn_handls or int_handls, thus storing the vector at handl_arg.
Code: Select all
/*
* This code IS NOT ACCESSIBLE FROM THIS FILE/PROGRAM.
* We will land here when receiving an exception or interrupt while in
* protected-mode. This code is used by the kernel to do IO from BIOS
* while device-drivers are not loaded.
* We use the stack to save the machine state when receiving a signal, but
* because stack space is limited, only a finite number of nested interrupts
* are allowed.
*/
.code32
.set PG_ENABLED, ~0x7fffffff
.set PG_DISABLE, 0x7fffffff
.set PROT_ENABLED, 0x1
.set PROT_DISABLE, ~0x1
.text
/*
* Generic handler for exceptions.
* Chaos start!
*/
exptn_handls:
cli # This must be the first instruction executed
movb $0, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $1, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $2, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $3, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $4, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $5, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $6, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $7, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $8, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $9, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $10, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $11, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $12, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $13, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $14, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $15, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $16, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $17, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $18, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $19, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $20, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $21, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $22, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $23, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $24, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $25, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $26, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $27, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $28, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $29, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $30, handl_arg
jmp go16excps
cli # This must be the first instruction executed
movb $31, handl_arg
jmp go16excps
/*
* Generic handlers for interrupts.
* Chaos start!
*/
int_handls:
cli # This must be the first instruction executed
movb $32, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $33, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $34, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $35, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $36, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $37, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $38, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $39, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $32, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $40, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $41, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $42, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $43, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $44, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $45, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $46, handl_arg
jmp go16ints
cli # This must be the first instruction executed
movb $47, handl_arg
jmp go16ints
/*
* Save machine state before interrupt.
* Set processor in 16-bit real-mode and set the idtr to our original,
* BIOS-oriented interrupt vector.
*/
go16ints:
movb $1, int_or_excpn # signal is an interrupt
jmp 2f
go16excps:
movb $0, int_or_excpn # signal is an exception
1:
cmpb $0x01, handl_arg
je 2f
cmpb $0x10, handl_arg
je 2f
cmpb $0x13, handl_arg
je 2f
cmpb $0x15, handl_arg
je 2f
cmpb $25, handl_arg
je 2f
cmpb $14, handl_arg // page fault?
je 2f
popl %eax
popl %ebx
popl %ecx
pushl $excpdump_buf
pushl handl_arg
pushl %eax
pushl %ebx
pushl %ecx
call excpdump32
pushl $excpdump_buf
call putstr_low
jmp .
2:
cmpb $NUM_NEST_INTS, int_num # max number of nested interrupts reached?
ja end_caos # yes; ignore interrupt
incb int_num # increment interrupt number
cmpb $0x01, handl_arg # is it video?
je excp_serv # yes
cmpb $0x15, handl_arg # is it video?
je ext_serv # yes
cmpb $0x10, handl_arg # is it video?
je vid_serv # yes
cmpb $0x13, handl_arg # is it disk?
je disk_serv # yes
cmpb $0x21, handl_arg # is it a key press/release?
je key_serv # yes
cmpb $25, handl_arg # temporary handler for memory copy
je temp_serv # yes
cmpb $14, handl_arg # temporary handler for memory copy
je page_serv # yes
jmp 2f
page_serv:
movl %cr2, %eax # faulting linear address
pushl %eax # as argument
call page_serv_high
addl $8, %esp
jmp no_pag2
ext_serv:
movl $ext_bios_regs, %esi # load new stack
movl %eax, (%esi) # save registers buffer
movl %ebx, 4(%esi) # save registers buffer
movl %ecx, 8(%esi) # save registers buffer
movl %edx, 12(%esi) # save registers buffer
pushl %ebp # form
movl %esp, %ebp # stack frame
movl $ext_mem_ents, addr_copy # pointer to buffer
movl $addr_copy, %esi
movl $(ext_mem_gdt_real+18), %edi # point to source address in struct
movl $3, %ecx
rep
movsb
movl $(ext_mem_gdt_real+26), %edi # point to destination address in struct
movl 20(%ebp), %eax
movl %eax, addr_copy # destination address
movl $addr_copy, %esi
movl $3, %ecx
rep
movsb
popl %ebp
jmp 2f
/*
* Stack if we get here:
* 4(%ebp): EIPlow, 8(%ebp): CS, 12(%ebp): EFLAGS, 16(%ebp): EIPtemp,
* 20(%ebp): pointer buffer, 24(%ebp): struct size.
*/
temp_serv:
pushl %ebp # form
movl %esp, %ebp # stack frame
pushl 24(%ebp) # save...
popl kargs_size # ...and restore size of structure
movl $kern_args, %eax
movl %eax, addr_copy
movl $addr_copy, %esi # pointer to buffer
movl $(ext_mem_gdt_real+18), %edi # point to source address in struct
movl $3, %ecx
rep
movsb
movl $(ext_mem_gdt_real+26), %edi # point to destination address in struct
movl 20(%ebp), %eax
movl %eax, addr_copy
movl $addr_copy, %esi # destination address
movl $3, %ecx
rep
movsb
popl %ebp
jmp 2f
/*
* Protected-mode passes an extended disk packet through the stack and the
* extended function through %ah.
*/
disk_serv:
movl $ext_bios_regs, %esi # load new stack
movl %eax, (%esi) # save registers buffer
movl %ebx, 4(%esi) # save registers buffer
movl %ecx, 8(%esi) # save registers buffer
movl %edx, 12(%esi) # save registers buffer
pushl %ebp # form
movl %esp, %ebp # stack frame
movl 28(%ebp), %esi # pointer to extended disk packet
movl $ext_das_struct, %edi # copy it to
movl $16, %ecx # copy 16 bytes
rep
movsb
movl $ext_das_struct, %edi # pointer to extended disk packet
movl $ext_rw_buf, 4(%edi) # copy segment:offset
movl $(ext_mem_gdt_real+18), %edi # point to source address in struct
movl $ext_rw_buf, addr_copy
movl $addr_copy, %esi
movl $3, %ecx
rep
movsb
movl $0, addr_copy
movl $(ext_mem_gdt_real+26), %edi # point to destination address in struct
movl 24(%ebp), %eax
movl %eax, addr_copy # destination address
movl $addr_copy, %esi # destination address
movl $3, %ecx
rep
movsb
popl %ebp
jmp 2f
/*
*/
vid_serv:
pushl %ebp # temporary
movl %esp, %ebp # stack frame
cmpl $0, 20(%ebp) # is argument 0?
je no_str
movl 20(%ebp), %eax
movl %eax, ptr_str_term # save pointer to string
jmp str
no_str:
movl $0, ptr_str_term # pointer is NULL
str:
popl %ebp # restore frame
popl %eax # pop EIP
popl %ebx # pop CS AND don't push it again
popl %ecx # pop EFLAGS AND don't push it again
pushl %eax # push EIP
pushl %ecx # push EFLAGS
pushl %ebx # push CS
pushl $term_handl_scr # push EIP
jmp no_pag2
/*
* Replace the return address to jump to the terminal handler.
*/
key_serv:
popl %eax # pop EIP
popl %ebx # pop CS AND don't push it again
popl %ecx # pop EFLAGS AND don't push it again
pushl %eax # push EIP
pushl %ecx # push EFLAGS
pushl %ebx # push CS
pushl $term_handl_kbd # push EIP
call new_int09_32
jmp no_pag2
excp_serv:
movl %dr6, %eax
shrl $14, %eax
andl $1, %eax
testl %eax, %eax
jz excp_ret
call sstep
excp_ret:
movb int_num, %al # load interrupt number
decb %al # decrement
movb %al, int_num # save interrupt number
sti
iret # return from interrupt
2:
pusha # save
pushf # save
pushl %ds # save
pushl %es # save
pushl %ss # save
pushl %gs # save
pushl %fs # save
movl $MEM_TSS, %edi # pointer to task segment
movl %cr3, %eax # load page directory address register
movl %esp, 56(%edi) # save page directory address to TSS
movl %eax, 32(%edi) # save stack pointer to TSS
movl %cr0, %eax # load status register
testl $PG_ENABLED, %eax
jz no_pag
movb $1, page_on
andl $PG_DISABLE, %eax
movl %eax, %cr0
no_pag:
ljmp $RCODE_SEGSEL, $segtion # to 16-bit segment
.code16
segtion:
movl $RDATA_SEGSEL, %edx
movl %edx, %ss # data segment
movl %edx, %ds # data segment
movl %edx, %es # data segment
movl %edx, %fs # data segment
movl %edx, %gs # data segment
lidt ivtreg # load the real-mode Interrupt Vector Table
movl %cr0, %eax
andl $PROT_DISABLE, %eax # disable segmentation
movl %eax, %cr0
ljmp $0, $end_go16
end_go16:
xorw %ax, %ax # reset segment registers
movw %ax, %ss
movw %ax, %ds
movw %ax, %es
movw $STACK, %sp # load new stack pointer
sti # enable interrupts once again
movb int_or_excpn, %al # load variable
testb %al, %al # is it an interrupt?
jnz 2f # yes
end_go16.1:
jmp int16_excpns # it is an interrupt
2:
end_go16.2:
jmp int16_ints # it is an interrupt
.code32
reentry_eip:
nop # just in case...
movl $SDATA_SEGSEL, %eax # protected-mode segments
movl %eax, %ss
movl %eax, %ds
movl %eax, %es
movl %eax, %gs
movl %eax, %fs
movl $MEM_TSS, %edi # point to TSS
movl 56(%edi), %esp # restore stack pointer
movl 32(%edi), %eax # load page directory address
movl %eax, %cr3 # restore page directory address
movb page_on, %al
testb %al, %al
jz no_pag1
movl %cr0, %eax # load register
orl $PG_ENABLE, %eax # PG on
movl %eax, %cr0 # restore paging
no_pag1:
movb $0, page_on # assume no paging for next interrupt
popl %fs # restore
popl %gs # restore
popl %ss # restore
popl %es # restore
popl %ds # restore
popf # restore
popa # restore
no_pag2:
movb int_num, %al # load interrupt number
decb %al # decrement
movb %al, int_num # save interrupt number
end_caos:
sti
iret # return from interrupt
There are some interrupts that still need to use the BIOS (for example for reading/writing to the disk). In that case, fall back to real-mode and disable paging, and jump to here:
Code: Select all
/*
* When an interrupt or exception is generated in protected-mode, control
* will ultimately lead here. We are already in real-mode and have the vector
* number in the handl_arg variable.
*/
int16_excpns:
movb handl_arg, %al # test if not really and exception
cmpb $0x10, %al
je 1f
cmpb $0x13, %al
je 2f
cmpb $0x15, %al
je 3f
cmpb $25, %al
je 4f
1:
movw $REAL_STRING_BUF, %si
int $0x10
jmp end_int16
2:
movw $ext_bios_regs, %si
movl (%si), %eax
movl 4(%si), %ebx
movl 8(%si), %ecx
movl 12(%si), %edx
pushw %es # save segment
pushw $0 # new segment
popw %es # load it
movw $ext_das_struct, %si # das packet
movb drive_num, %dl # drive number
int $0x13 # extended read or write
jc error
movb $0x87, %ah
movw $0x100, %cx
movw $ext_mem_gdt_real, %si
int $0x15
popw %es # restore segment
jmp end_int16
3:
movw $ext_bios_regs, %di
movl (%di), %eax
movl 4(%di), %ebx
movl 8(%di), %ecx
movl 12(%di), %edx
pushw %es
pushw $0
popw %es
xorl %ebx, %ebx
movl $0x534d4150, %edx # magic number
movl $0xe820, %eax # function number
movl $24, %ecx # number of bytes
movw $ext_mem_ents, %di # buffer to store entries
movl $1, 20(%di) # make it ACPI 3.0 compatible
mem_call:
int $0x15 # return extended memory
movl $0xe820, %edx # value will be exchanged
xchgl %eax, %edx # exchange values
cmpl $0x534d4150, %edx # must be this magic value
jne error
movl $0, 24(%di) # NULL pointer
addw $28, %di # next entry (acpi 3.0)
movl $24, %ecx
testl %ebx, %ebx # last entry?
jnz mem_call # no
movl $0x12345, 20(%di)
end_mem_call:
movb $0x87, %ah
xorb %al, %al
movw ext_mem_ents, %cx
movw $ext_mem_gdt_real, %si
int $0x15
popw %es # restore segment
jmp end_int16
4:
pushw %es
pushw $0
popw %es
movb $0x87, %ah
movw kargs_size, %cx
movw $ext_mem_gdt_real, %si
int $0x15
popw %es # restore segment
jmp end_int16
int16_ints:
xorl %eax, %eax
xorl %ebx, %ebx
movb handl_arg, %al
subb $32, %al
movb $4, %bl
mull %ebx
addw $int16_init.1, %ax
jmp *%ax
int16_init.1:
int $32 # IRQ0: 18.2hz timer tick
jmp end_int16
int $33 # IRQ1: keypress interrupt
jmp end_int16
int $34 # IRQ2: cascade to slave 8259
jmp end_int16
int $35 # IRQ3: COM 2
jmp end_int16
int $36 # IRQ4: COM 1
jmp end_int16
int $37 # IRQ5: LPT 2
jmp end_int16
int $38 # IRQ6: floppy
jmp end_int16
int $39 # IRQ7: LPT 1
jmp end_int16
int $40 # IRQ8: Real-time clock (1khz)
jmp end_int16
int $41 # IRQ9
jmp end_int16
int $42 # IRQ10
jmp end_int16
int $43 # IRQ11
jmp end_int16
int $44 # IRQ12: Mouse
jmp end_int16
int $45 # IRQ13: Math coprocessor
jmp end_int16
int $46 # IRQ14: IDE services
jmp end_int16
int $47 # IRQ15: APM suspend
end_int16:
cli # disable interrupts while restoring protected-mode
end_int16_nocli:
lgdt gdtreg
lidt idtreg
movl %cr0, %eax # load cr0
orl $1, %eax # enable protection bit
movl %eax, %cr0 # return to protected-mode
ljmp $SCODE_SEGSEL, $reentry_eip
The reason for not saving context is that I have no processes/threads yet. I haven't got to the point where processes are created and context switching occurs.
I hope it is a lot clearer now.