OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Aug 09, 2022 1:29 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 12 posts ] 
Author Message
 Post subject: Triple Fault with div0 Exception
PostPosted: Fri Jun 24, 2022 4:22 am 
Offline

Joined: Fri Jun 24, 2022 1:39 am
Posts: 3
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:
.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:
.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?


Top
 Profile  
 
 Post subject: Re: Triple Fault with div0 Exception
PostPosted: Mon Jun 27, 2022 6:28 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 3993
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:
__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"?


Top
 Profile  
 
 Post subject: Re: Triple Fault with div0 Exception
PostPosted: Wed Jun 29, 2022 5:57 am 
Offline

Joined: Wed Jun 29, 2022 2:17 am
Posts: 20
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:
.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:
.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

_________________
I'm a new man to develop operating system.


Top
 Profile  
 
 Post subject: Re: Triple Fault with div0 Exception
PostPosted: Wed Jun 29, 2022 6:10 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4387
Location: Chichester, UK
theflysong wrote:
INT 0 means trigger interrupt 0x20, instead of trigger interrupt 0x00

What?


Top
 Profile  
 
 Post subject: Re: Triple Fault with div0 Exception
PostPosted: Wed Jun 29, 2022 6:48 am 
Offline

Joined: Wed Jun 29, 2022 2:17 am
Posts: 20
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

_________________
I'm a new man to develop operating system.


Top
 Profile  
 
 Post subject: Re: Triple Fault with div0 Exception
PostPosted: Wed Jun 29, 2022 7:39 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4387
Location: Chichester, UK
Why would it do that?


Top
 Profile  
 
 Post subject: Re: Triple Fault with div0 Exception
PostPosted: Wed Jun 29, 2022 4:29 pm 
Offline
Member
Member

Joined: Sun Jun 23, 2019 5:36 pm
Posts: 595
Location: North Dakota, United States
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.


Top
 Profile  
 
 Post subject: Re: Triple Fault with div0 Exception
PostPosted: Thu Jun 30, 2022 2:09 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4387
Location: Chichester, UK
It's easy to see how the misunderstanding might occur. But its misinformation and, and such, is not helpful.


Top
 Profile  
 
 Post subject: Re: Triple Fault with div0 Exception
PostPosted: Wed Jul 06, 2022 2:51 pm 
Offline

Joined: Fri Jun 24, 2022 1:39 am
Posts: 3
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.


Top
 Profile  
 
 Post subject: Re: Triple Fault with div0 Exception
PostPosted: Wed Jul 06, 2022 3:18 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 3993
Can you share your Bochs log? It should tell us exactly where the problem is.


Top
 Profile  
 
 Post subject: Re: Triple Fault with div0 Exception
PostPosted: Wed Jul 06, 2022 3:21 pm 
Offline

Joined: Fri Jun 24, 2022 1:39 am
Posts: 3
Octocontrabass wrote:
Can you share your Bochs log? It should tell us exactly where the problem is.


Of course, here you are.


Attachments:
File comment: Bochs Log file
bochsout.txt [16.26 KiB]
Downloaded 6 times
Top
 Profile  
 
 Post subject: Re: Triple Fault with div0 Exception
PostPosted: Wed Jul 06, 2022 3:50 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 3993
Code:
00000688971i[CPU0  ] | ESP=fffffffc

Your stack pointer is invalid.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 12 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], DotBot [Bot], Google [Bot] and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group