Page 1 of 1

Triple Fault with div0 Exception

Posted: Fri Jun 24, 2022 4:22 am
by CaramelizedSophus
Hello there,

I have a fully functional IDT code(also ISR, IRQ,...) written in C/C++. But I wanted to make things more simple, and transparent, so I decided to rewrite my IDT code in assembly. As I did with the GDT code.

The matter is that my exceptions are in a different file and are defined as a macro in order to avoid repeating code.
The procedures found in the file are:
  • __install_gate -> Sets a descriptor gate
  • __set_gates -> Sets all of 32 exceptions gates for the IDT.
  • __install_idt -> Calls __set_gates and then loads the address into IDTR
When or how does the problem occur:
  • 1. Set gates and load IDT
  • 2. Force exception(i.e. by-zero div.)
  • 3. Page Fault is called, cannot call any handler because IDT seems corrupt.
  • 4. After second PF, which is the third raised exception, Triple Fault and reset.
I have checked the code in runtime with GDB, all descriptors get filled as expected. Also verified the addresses with a dissasembler and with objdump. Nothing wierd.

Below the full code:

Code: Select all

.section .text

.global __install_idt


/*
 * EAX: idt array index
 * EBX: idt array start
 * 
 * ECX: offset
 * DX: segment selector
 * DI: flags
*/
__install_gate:

    push %eax
    push %ebx
    push %ecx
    push %edx
    push %esi
    push %edi

    mov %eax, %esi
    mov _idt_gate_start, %ebx

    //offset low 16 and high 16
    mov %ecx, %eax
    mov %ax, 0(%ebx, %esi, 8)
    shr $16, %eax
    mov %ax, 6(%ebx, %esi, 8)
    
    //segment selector
    mov %dx, 2(%ebx, %esi, 8)

    //flags and zeroed
    shl $8, %edi
    and $0xFF00, %edi
    
    mov %di, 4(%ebx, %esi, 8)
    
    pop %edi
    pop %esi
    pop %edx
    pop %ecx
    pop %ebx
    pop %eax
    

ret


__set_gates:
    push %eax
    push %ebx
    push %ecx
    push %edx
    push %edi
    push %esi

    mov $32, %ecx
    __idt_spec_set:
        mov _idt_start, %ebx
        mov $_KERNEL_CS, %edx
        xor %edi,%edi
        mov $_IDT_GATE_FLAGS, %di
        //no need to modify ecx?
        push %ecx
            mov $_exceptions_table, %esi # address of table
            #mov (%esi), %eax
            #mov %eax, %esi
            dec %ecx
            mov %ecx, %eax #index in table
            mov (%esi, %ecx, 4),%ecx # macro label address in table
            #mov (%ecx), %ecx # address of function
            
            call __install_gate
        pop %ecx
    loop __idt_spec_set
/*
	mov $0x00008E0000080000, %eax
	mov _idt_start, %ebx
	mov %ea
*/
    pop %esi
    pop %edi
    pop %edx
    pop %ecx
    pop %ebx
    pop %eax
ret

__install_idt:

    call __set_gates
    #cli
    lidtl _idtr
    #sti

ret

.section .data

_idtr:
.word (_idt_end - _idt_start)
.long _idt_start

_idt_start:

.space (_IDT_GATE_SIZE * 256)

_idt_end:

_exceptions_table:

.long __int_0x00
.long __int_0x01
.long __int_0x02
.long __int_0x03
.long __int_0x04
.long __int_0x05
.long __int_0x06
.long __int_0x07
.long __int_error_0x08
.long __int_0x09
.long __int_error_0x0A
.long __int_error_0x0B
.long __int_error_0x0C
.long __int_error_0x0D
.long __int_error_0x0E
.long __int_0x0F
.long __int_0x10
.long __int_error_0x11
.long __int_0x12
.long __int_0x13
.long __int_0x14
.long __int_0x15
.long __int_0x16
.long __int_0x17
.long __int_0x18
.long __int_0x19
.long __int_0x1A
.long __int_0x1B
.long __int_0x1C
.long __int_0x1D
.long __int_error_0x1E
.long __int_0x1F

.section .bss

_idt_gate_start:

offset1         : .space 2
segsec          : .space 2
reserved        : .space 1
p_dpl_0_gate    : .space 1
offset2         : .space 2

_idt_gate_end:

.set _IDT_GATE_SIZE, (_idt_gate_end - _idt_gate_start)
.set _KERNEL_CS, 0x08
.set _IDT_GATE_FLAGS, 0x8E
The exceptions code:

Code: Select all

.section .text

.macro __int_ num
.global __int_\num
__int_\num:
    cli
    mov $\num, (_int_number)
    push $0
    push (_int_number)
    jmp __int_common
.endm

.macro __int_error_ num
.global __int_error_\num
__int_error_\num:
    cli
    movb $\num, (_int_number)
    push (_int_number)
    jmp __int_common
.endm

__int_common:
    //pushed EFLAGS, CS, EIP
    //pushed error, int number

    //push general purpose register
    pushal 

    //push segments registers
    push %ds
    push %es
    push %fs
    push %gs

    //clear df for c++ func
    cld

    push %esp
    call __isr_handler
    add $4, %esp

    pop %gs
    pop %fs
    pop %es
    pop %ds

    popal

    //discard int number and error code
    add $8, %esp


iret


__int_          0x00
__int_          0x01
__int_          0x02
__int_          0x03
__int_          0x04
__int_          0x05
__int_          0x06
__int_          0x07
__int_error_    0x08
__int_          0x09
__int_error_    0x0A
__int_error_    0x0B
__int_error_    0x0C
__int_error_    0x0D
__int_error_    0x0E
__int_          0x0F
__int_          0x10
__int_error_    0x11
__int_          0x12
__int_          0x13
__int_          0x14
__int_          0x15
__int_          0x16
__int_          0x17
__int_          0x18
__int_          0x19
__int_          0x1A
__int_          0x1B
__int_          0x1C
__int_          0x1D
__int_error_    0x1E
__int_          0x1F



.section .bss

_int_number: .space 1
Am I missing something?

Re: Triple Fault with div0 Exception

Posted: Mon Jun 27, 2022 6:28 pm
by Octocontrabass
CaramelizedSophus wrote:But I wanted to make things more simple, and transparent, so I decided to rewrite my IDT code in assembly.
Rewriting your code in assembly makes it much harder to find the problem. I don't think that's more simple or transparent.
CaramelizedSophus wrote:2. Force exception(i.e. by-zero div.)
How exactly are you doing this? You can't use "INT 0", that won't push an error code. You can't use division by zero in C, that's undefined behavior and might cause a different exception or no exception at all.
CaramelizedSophus wrote:3. Page Fault is called, cannot call any handler because IDT seems corrupt.
4. After second PF, which is the third raised exception, Triple Fault and reset.
How did you determine that it's a page fault?

Code: Select all

__int_\num:
    cli
    mov $\num, (_int_number)
    push $0
    push (_int_number)
    jmp __int_common
.endm
Why do your handlers start with CLI? Why are you using a variable in memory instead of "push $\num"?

Re: Triple Fault with div0 Exception

Posted: Wed Jun 29, 2022 5:57 am
by theflysong
CaramelizedSophus wrote:Hello there,

I have a fully functional IDT code(also ISR, IRQ,...) written in C/C++. But I wanted to make things more simple, and transparent, so I decided to rewrite my IDT code in assembly. As I did with the GDT code.

The matter is that my exceptions are in a different file and are defined as a macro in order to avoid repeating code.
The procedures found in the file are:
  • __install_gate -> Sets a descriptor gate
  • __set_gates -> Sets all of 32 exceptions gates for the IDT.
  • __install_idt -> Calls __set_gates and then loads the address into IDTR
When or how does the problem occur:
  • 1. Set gates and load IDT
  • 2. Force exception(i.e. by-zero div.)
  • 3. Page Fault is called, cannot call any handler because IDT seems corrupt.
  • 4. After second PF, which is the third raised exception, Triple Fault and reset.
I have checked the code in runtime with GDB, all descriptors get filled as expected. Also verified the addresses with a dissasembler and with objdump. Nothing wierd.

Below the full code:

Code: Select all

.section .text

.global __install_idt


/*
 * EAX: idt array index
 * EBX: idt array start
 * 
 * ECX: offset
 * DX: segment selector
 * DI: flags
*/
__install_gate:

    push %eax
    push %ebx
    push %ecx
    push %edx
    push %esi
    push %edi

    mov %eax, %esi
    mov _idt_gate_start, %ebx

    //offset low 16 and high 16
    mov %ecx, %eax
    mov %ax, 0(%ebx, %esi, 8)
    shr $16, %eax
    mov %ax, 6(%ebx, %esi, 8)
    
    //segment selector
    mov %dx, 2(%ebx, %esi, 8)

    //flags and zeroed
    shl $8, %edi
    and $0xFF00, %edi
    
    mov %di, 4(%ebx, %esi, 8)
    
    pop %edi
    pop %esi
    pop %edx
    pop %ecx
    pop %ebx
    pop %eax
    

ret


__set_gates:
    push %eax
    push %ebx
    push %ecx
    push %edx
    push %edi
    push %esi

    mov $32, %ecx
    __idt_spec_set:
        mov _idt_start, %ebx
        mov $_KERNEL_CS, %edx
        xor %edi,%edi
        mov $_IDT_GATE_FLAGS, %di
        //no need to modify ecx?
        push %ecx
            mov $_exceptions_table, %esi # address of table
            #mov (%esi), %eax
            #mov %eax, %esi
            dec %ecx
            mov %ecx, %eax #index in table
            mov (%esi, %ecx, 4),%ecx # macro label address in table
            #mov (%ecx), %ecx # address of function
            
            call __install_gate
        pop %ecx
    loop __idt_spec_set
/*
	mov $0x00008E0000080000, %eax
	mov _idt_start, %ebx
	mov %ea
*/
    pop %esi
    pop %edi
    pop %edx
    pop %ecx
    pop %ebx
    pop %eax
ret

__install_idt:

    call __set_gates
    #cli
    lidtl _idtr
    #sti

ret

.section .data

_idtr:
.word (_idt_end - _idt_start)
.long _idt_start

_idt_start:

.space (_IDT_GATE_SIZE * 256)

_idt_end:

_exceptions_table:

.long __int_0x00
.long __int_0x01
.long __int_0x02
.long __int_0x03
.long __int_0x04
.long __int_0x05
.long __int_0x06
.long __int_0x07
.long __int_error_0x08
.long __int_0x09
.long __int_error_0x0A
.long __int_error_0x0B
.long __int_error_0x0C
.long __int_error_0x0D
.long __int_error_0x0E
.long __int_0x0F
.long __int_0x10
.long __int_error_0x11
.long __int_0x12
.long __int_0x13
.long __int_0x14
.long __int_0x15
.long __int_0x16
.long __int_0x17
.long __int_0x18
.long __int_0x19
.long __int_0x1A
.long __int_0x1B
.long __int_0x1C
.long __int_0x1D
.long __int_error_0x1E
.long __int_0x1F

.section .bss

_idt_gate_start:

offset1         : .space 2
segsec          : .space 2
reserved        : .space 1
p_dpl_0_gate    : .space 1
offset2         : .space 2

_idt_gate_end:

.set _IDT_GATE_SIZE, (_idt_gate_end - _idt_gate_start)
.set _KERNEL_CS, 0x08
.set _IDT_GATE_FLAGS, 0x8E
The exceptions code:

Code: Select all

.section .text

.macro __int_ num
.global __int_\num
__int_\num:
    cli
    mov $\num, (_int_number)
    push $0
    push (_int_number)
    jmp __int_common
.endm

.macro __int_error_ num
.global __int_error_\num
__int_error_\num:
    cli
    movb $\num, (_int_number)
    push (_int_number)
    jmp __int_common
.endm

__int_common:
    //pushed EFLAGS, CS, EIP
    //pushed error, int number

    //push general purpose register
    pushal 

    //push segments registers
    push %ds
    push %es
    push %fs
    push %gs

    //clear df for c++ func
    cld

    push %esp
    call __isr_handler
    add $4, %esp

    pop %gs
    pop %fs
    pop %es
    pop %ds

    popal

    //discard int number and error code
    add $8, %esp


iret


__int_          0x00
__int_          0x01
__int_          0x02
__int_          0x03
__int_          0x04
__int_          0x05
__int_          0x06
__int_          0x07
__int_error_    0x08
__int_          0x09
__int_error_    0x0A
__int_error_    0x0B
__int_error_    0x0C
__int_error_    0x0D
__int_error_    0x0E
__int_          0x0F
__int_          0x10
__int_error_    0x11
__int_          0x12
__int_          0x13
__int_          0x14
__int_          0x15
__int_          0x16
__int_          0x17
__int_          0x18
__int_          0x19
__int_          0x1A
__int_          0x1B
__int_          0x1C
__int_          0x1D
__int_error_    0x1E
__int_          0x1F



.section .bss

_int_number: .space 1
Am I missing something?
INT 0 means trigger interrupt 0x20, instead of trigger interrupt 0x00

Re: Triple Fault with div0 Exception

Posted: Wed Jun 29, 2022 6:10 am
by iansjack
theflysong wrote: INT 0 means trigger interrupt 0x20, instead of trigger interrupt 0x00
What?

Re: Triple Fault with div0 Exception

Posted: Wed Jun 29, 2022 6:48 am
by theflysong
iansjack wrote:
theflysong wrote: INT 0 means trigger interrupt 0x20, instead of trigger interrupt 0x00
What?
When INT 0 instruction is executed, the cpu uses the IDT[0x20] as it's ISR

Re: Triple Fault with div0 Exception

Posted: Wed Jun 29, 2022 7:39 am
by iansjack
Why would it do that?

Re: Triple Fault with div0 Exception

Posted: Wed Jun 29, 2022 4:29 pm
by Ethin
theflysong wrote:
iansjack wrote:
theflysong wrote: INT 0 means trigger interrupt 0x20, instead of trigger interrupt 0x00
What?
When INT 0 instruction is executed, the cpu uses the IDT[0x20] as it's ISR
That's... Not what the Intel or AMD manuals say. I would encourage you to go re-read them. I believe you have a major misunderstanding of how interrupts work on the x86 architecture, and how the INT n/INTO/INT3/INT1 instructions work.

Re: Triple Fault with div0 Exception

Posted: Thu Jun 30, 2022 2:09 am
by iansjack
It's easy to see how the misunderstanding might occur. But its misinformation and, and such, is not helpful.

Re: Triple Fault with div0 Exception

Posted: Wed Jul 06, 2022 2:51 pm
by CaramelizedSophus
Sorry for being late to answer
Octocontrabass wrote:
CaramelizedSophus wrote:2. Force exception(i.e. by-zero div.)
How exactly are you doing this? You can't use "INT 0", that won't push an error code. You can't use division by zero in C, that's undefined behavior and might cause a different exception or no exception at all.
I force the exception with inline assembly.
Octocontrabass wrote:
CaramelizedSophus wrote:3. Page Fault is called, cannot call any handler because IDT seems corrupt.
4. After second PF, which is the third raised exception, Triple Fault and reset.
How did you determine that it's a page fault?

Sorry, I meant General Protection fault. I know because bochs halts showing what triggered the triple fault.

Re: Triple Fault with div0 Exception

Posted: Wed Jul 06, 2022 3:18 pm
by Octocontrabass
Can you share your Bochs log? It should tell us exactly where the problem is.

Re: Triple Fault with div0 Exception

Posted: Wed Jul 06, 2022 3:21 pm
by CaramelizedSophus
Octocontrabass wrote:Can you share your Bochs log? It should tell us exactly where the problem is.
Of course, here you are.

Re: Triple Fault with div0 Exception

Posted: Wed Jul 06, 2022 3:50 pm
by Octocontrabass

Code: Select all

00000688971i[CPU0  ] | ESP=fffffffc
Your stack pointer is invalid.