Triple Fault When entering PMode

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
System123
Member
Member
Posts: 196
Joined: Mon Jul 07, 2008 1:25 am

Triple Fault When entering PMode

Post by System123 »

Here is my second stage loader code. The code triple faults at the sections that I have marked with a ** and the code I changed to stop the fault is marked with a ##. However I do not know why it is faulting.

Code: Select all

[BITS 16]       ; We need 16-bit intructions for Real mode

        cli                     ; Disable interrupts, we want to be alone

        xor ax, ax
        mov ds, ax              ; Set DS-register to 0 - used by lgdt

        lgdt [gdt_desc]         ; Load the GDT descriptor

        mov eax, cr0            ; Copy the contents of CR0 into EAX
        or eax, 1               ; Set bit 0
        mov cr0, eax            ; Copy the contents of EAX into CR0
        
        **jmp 08h:clear_pipe
        ##jmp clear_pipe      ; Jump to code segment, offset clear_pipe


[BITS 32]                       ; We now need 32-bit instructions
clear_pipe:

        **mov ax, 10h             ; Save data segment identifyer
        ##mov eax, 10h

        **mov ds, ax              ; Move a valid data segment into the data segment register
        **mov ss, ax              ; Move a valid data segment into the stack segment register
        **mov esp, 090000h        ; Move the stack pointer to 090000h

        **mov byte [ds:0B8000h], 'P'      ; Move the ASCII-code of 'P' into first video memory
        **mov byte [ds:0B8001h], 1Bh      ; Assign a color code
        
hang:
        jmp hang                ; Loop, self-jump


gdt:                    ; Address for the GDT

gdt_null:               ; Null Segment
        dd 0
        dd 0

gdt_code:               ; Code segment, read/execute, nonconforming
        dw 0FFFFh
        dw 0
        db 0
        db 10011010b
        db 11001111b
        db 0

gdt_data:               ; Data segment, read/write, expand down
        dw 0FFFFh
        dw 0
        db 0
        db 10010010b
        db 11001111b
        db 0

gdt_end:                ; Used to calculate the size of the GDT



gdt_desc:                       ; The GDT descriptor
        dw gdt_end - gdt - 1    ; Limit (size)
        dd gdt                  ; Address of the GDT

The rest of the code works fine.
Gizmic OS
Currently - Busy with FAT12 driver and VFS
User avatar
-m32
Member
Member
Posts: 120
Joined: Thu Feb 21, 2008 5:59 am
Location: Ottawa, Canada

Re: Triple Fault When entering PMode

Post by -m32 »

System123 wrote:

Code: Select all

[BITS 16]
        xor ax, ax
        mov ds, ax              ; Set DS-register to 0 - used by lgdt
I'm pretty sure you can't set your DS to the NULL descriptor ;)
User avatar
01000101
Member
Member
Posts: 1599
Joined: Fri Jun 22, 2007 12:47 pm
Contact:

Re: Triple Fault When entering PMode

Post by 01000101 »

what is the BOCHS triple-fault error message?
if it is a garbage code execution, you're probably not using a valid GDT setup.

also, setting DS as 0 is fine as long as you weren't using DS for any jumps or any previous usage in the first-stage bootloader.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Re: Triple Fault When entering PMode

Post by AJ »

-m32 wrote:I'm pretty sure you can't set your DS to the NULL descriptor ;)
That code is perfectly valid as it takes place while the System123 is still in 16 bit mode and is not a selector to the NULL descriptor. In fact, as the comment says it may even be necessary to do this depending on where the GDT is.

@System123: The reason that your code changes work is that in your new version (marked with ##), you never actually switch to PMode - the far jump is required. As 01000101 says, we need the Bochs error message (and register dump) to see what is going on. Also, leave your original code in place and move a JMP $ down through your code to see if it is before or after the far jump that the triple fault occurs.

Also, IIRC, doesn't a GDT need to be double-word aligned? That may be worth a go.

Cheers,
Adam
User avatar
-m32
Member
Member
Posts: 120
Joined: Thu Feb 21, 2008 5:59 am
Location: Ottawa, Canada

Re: Triple Fault When entering PMode

Post by -m32 »

AJ wrote:That code is perfectly valid as it takes place while the System123 is still in 16 bit mode and is not a selector to the NULL descriptor.
Right, right, if it had happened after the lgdt, then there would have been a problem. I'm not paying attention again :)
System123
Member
Member
Posts: 196
Joined: Mon Jul 07, 2008 1:25 am

Re: Triple Fault When entering PMode

Post by System123 »

Ok here is my Bochs register dump aswel as my bootloader and my secondstage loader.

Code: Select all

00008023335i[BIOS ] Booting from 0000:7c00
00008857828e[CPU0 ] jump_protected: gate type 0 unsupported
00008857828i[CPU0 ] CPU is in protected mode (active)
00008857828i[CPU0 ] CS.d_b = 16 bit
00008857828i[CPU0 ] SS.d_b = 16 bit
00008857828i[CPU0 ] EFER   = 0x00000000
00008857828i[CPU0 ] | RAX=0000000060000011  RBX=0000000000000203
00008857828i[CPU0 ] | RCX=0000000000000003  RDX=0000000000000fff
00008857828i[CPU0 ] | RSP=000000000000fffb  RBP=0000000000000000
00008857828i[CPU0 ] | RSI=00000000ffff01c5  RDI=0000000000080005
00008857828i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
00008857828i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
00008857828i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
00008857828i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
00008857828i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00008857828i[CPU0 ] | SEG selector     base    limit G D
00008857828i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00008857828i[CPU0 ] |  CS:0050( 0004| 0|  0) 00000500 0000ffff 0 0
00008857828i[CPU0 ] |  DS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00008857828i[CPU0 ] |  SS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00008857828i[CPU0 ] |  ES:0050( 0005| 0|  0) 00000500 0000ffff 0 0
00008857828i[CPU0 ] |  FS:07c0( 0005| 0|  0) 00007c00 0000ffff 0 0
00008857828i[CPU0 ] |  GS:07c0( 0005| 0|  0) 00007c00 0000ffff 0 0
00008857828i[CPU0 ] |  MSR_FS_BASE:0000000000007c00
00008857828i[CPU0 ] |  MSR_GS_BASE:0000000000007c00
00008857828i[CPU0 ] | RIP=0000000000000016 (0000000000000016)
00008857828i[CPU0 ] | CR0=0x60000011 CR1=0x0 CR2=0x0000000000000000
00008857828i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00008857828i[CPU0 ] >> jmp far 0008:001b : EA1B000800
00008857828e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
00008857828i[SYS  ] bx_pc_system_c::Reset(SOFTWARE) called
00008857828i[CPU0 ] cpu software reset
Boot loader:

Code: Select all

;*********************************************
;	Boot1.asm
;		- A Simple Bootloader
;
;	Operating Systems Development Tutorial
;*********************************************

bits	16						; we are in 16 bit real mode

org		0					; we will set regisers later

start:	jmp	main					; jump to start of bootloader

;*********************************************
;	BIOS Parameter Block
;*********************************************

; BPB Begins 3 bytes from start. We do a far jump, which is 3 bytes in size.
; If you use a short jump, add a "nop" after it to offset the 3rd byte.

;;TIMES 0Bh-$+start DB 0				; we are not going to use this anymore

bpbOEM					DB "My OS   "			; OEM identifier (Cannot exceed 8 bytes!)
bpbBytesPerSector:  	DW 512
bpbSectorsPerCluster: 	DB 1
bpbReservedSectors: 	DW 1
bpbNumberOfFATs: 		DB 2
bpbRootEntries: 		DW 224
bpbTotalSectors: 		DW 2880
bpbMedia: 				DB 0xf8  ;; 0xF1
bpbSectorsPerFAT: 		DW 9
bpbSectorsPerTrack: 	DW 18
bpbHeadsPerCylinder: 	DW 2
bpbHiddenSectors: 		DD 0
bpbTotalSectorsBig:     DD 0
bsDriveNumber: 	        DB 0
bsUnused: 				DB 0
bsExtBootSignature: 	DB 0x29
bsSerialNumber:	        DD 0xa0a1a2a3
bsVolumeLabel: 	        DB "MOS FLOPPY "
bsFileSystem: 	        DB "FAT12   "

;************************************************;
;	Prints a string
;	DS=>SI: 0 terminated string
;************************************************;
Print:
			lodsb				; load next byte from string from SI to AL
			or	al, al			; Does AL=0?
			jz	PrintDone		; Yep, null terminator found-bail out
			mov	ah, 0eh			; Nope-Print the character
			int	10h
			jmp	Print			; Repeat until null terminator found
	PrintDone:
			ret				; we are done, so return

;************************************************;
; Reads a series of sectors
; CX=>Number of sectors to read
; AX=>Starting sector
; ES:BX=>Buffer to read to
;************************************************;

ReadSectors:
     .MAIN
          mov     di, 0x0005                          ; five retries for error
     .SECTORLOOP
          push    ax
          push    bx
          push    cx
          call    LBACHS                              ; convert starting sector to CHS
          mov     ah, 0x02                            ; BIOS read sector
          mov     al, 0x01                            ; read one sector
          mov     ch, BYTE [absoluteTrack]            ; track
          mov     cl, BYTE [absoluteSector]           ; sector
          mov     dh, BYTE [absoluteHead]             ; head
          mov     dl, BYTE [bsDriveNumber]            ; drive
          int     0x13                                ; invoke BIOS
          jnc     .SUCCESS                            ; test for read error
          xor     ax, ax                              ; BIOS reset disk
          int     0x13                                ; invoke BIOS
          dec     di                                  ; decrement error counter
          pop     cx
          pop     bx
          pop     ax
          jnz     .SECTORLOOP                         ; attempt to read again
          int     0x18
     .SUCCESS
          mov     si, msgProgress
          call    Print
          pop     cx
          pop     bx
          pop     ax
          add     bx, WORD [bpbBytesPerSector]        ; queue next buffer
          inc     ax                                  ; queue next sector
          loop    .MAIN                               ; read next sector
          ret

;************************************************;
; Convert CHS to LBA
; LBA = (cluster - 2) * sectors per cluster
;************************************************;

ClusterLBA:
          sub     ax, 0x0002                          ; zero base cluster number
          xor     cx, cx
          mov     cl, BYTE [bpbSectorsPerCluster]     ; convert byte to word
          mul     cx
          add     ax, WORD [datasector]               ; base data sector
          ret
     
;************************************************;
; Convert LBA to CHS
; AX=>LBA Address to convert
;
; absolute sector = (logical sector / sectors per track) + 1
; absolute head   = (logical sector / sectors per track) MOD number of heads
; absolute track  = logical sector / (sectors per track * number of heads)
;
;************************************************;

LBACHS:
          xor     dx, dx                              ; prepare dx:ax for operation
          div     WORD [bpbSectorsPerTrack]           ; calculate
          inc     dl                                  ; adjust for sector 0
          mov     BYTE [absoluteSector], dl
          xor     dx, dx                              ; prepare dx:ax for operation
          div     WORD [bpbHeadsPerCylinder]          ; calculate
          mov     BYTE [absoluteHead], dl
          mov     BYTE [absoluteTrack], al
          ret

;*********************************************
;	Bootloader Entry Point
;*********************************************

main:

     ;----------------------------------------------------
     ; code located at 0000:7C00, adjust segment registers
     ;----------------------------------------------------
     
          cli						; disable interrupts
          mov     ax, 0x07C0				; setup registers to point to our segment
          mov     ds, ax
          mov     es, ax
          mov     fs, ax
          mov     gs, ax

     ;----------------------------------------------------
     ; create stack
     ;----------------------------------------------------
     
          mov     ax, 0x0000				; set the stack
          mov     ss, ax
          mov     sp, 0xFFFF
          sti						; restore interrupts

     ;----------------------------------------------------
     ; Display loading message
     ;----------------------------------------------------
     
          mov     si, msgLoading
          call    Print
          
     ;----------------------------------------------------
     ; Load root directory table
     ;----------------------------------------------------

     LOAD_ROOT:
     
     ; compute size of root directory and store in "cx"
     
          xor     cx, cx
          xor     dx, dx
          mov     ax, 0x0020                           ; 32 byte directory entry
          mul     WORD [bpbRootEntries]                ; total size of directory
          div     WORD [bpbBytesPerSector]             ; sectors used by directory
          xchg    ax, cx
          
     ; compute location of root directory and store in "ax"
     
          mov     al, BYTE [bpbNumberOfFATs]            ; number of FATs
          mul     WORD [bpbSectorsPerFAT]               ; sectors used by FATs
          add     ax, WORD [bpbReservedSectors]         ; adjust for bootsector
          mov     WORD [datasector], ax                 ; base of root directory
          add     WORD [datasector], cx
          
     ; read root directory into memory (7C00:0200)
     
          mov     bx, 0x0200                            ; copy root dir above bootcode
          call    ReadSectors

     ;----------------------------------------------------
     ; Find stage 2
     ;----------------------------------------------------

     ; browse root directory for binary image
          mov     cx, WORD [bpbRootEntries]             ; load loop counter
          mov     di, 0x0200                            ; locate first root entry
     .LOOP:
          push    cx
          mov     cx, 0x000B                            ; eleven character name
          mov     si, ImageName                         ; image name to find
          push    di
     rep  cmpsb                                         ; test for entry match
          pop     di
          je      LOAD_FAT
          pop     cx
          add     di, 0x0020                            ; queue next directory entry
          loop    .LOOP
          jmp     FAILURE

     ;----------------------------------------------------
     ; Load FAT
     ;----------------------------------------------------

     LOAD_FAT:
     
     ; save starting cluster of boot image
     
          mov     si, msgCRLF
          call    Print
          mov     dx, WORD [di + 0x001A]
          mov     WORD [cluster], dx                  ; file's first cluster
          
     ; compute size of FAT and store in "cx"
     
          xor     ax, ax
          mov     al, BYTE [bpbNumberOfFATs]          ; number of FATs
          mul     WORD [bpbSectorsPerFAT]             ; sectors used by FATs
          mov     cx, ax

     ; compute location of FAT and store in "ax"

          mov     ax, WORD [bpbReservedSectors]       ; adjust for bootsector
          
     ; read FAT into memory (7C00:0200)

          mov     bx, 0x0200                          ; copy FAT above bootcode
          call    ReadSectors

     ; read image file into memory (0050:0000)
     
          mov     si, msgCRLF
          call    Print
          mov     ax, 0x0050
          mov     es, ax                              ; destination for image
          mov     bx, 0x0000                          ; destination for image
          push    bx

     ;----------------------------------------------------
     ; Load Stage 2
     ;----------------------------------------------------

     LOAD_IMAGE:
     
          mov     ax, WORD [cluster]                  ; cluster to read
          pop     bx                                  ; buffer to read into
          call    ClusterLBA                          ; convert cluster to LBA
          xor     cx, cx
          mov     cl, BYTE [bpbSectorsPerCluster]     ; sectors to read
          call    ReadSectors
          push    bx
          
     ; compute next cluster
     
          mov     ax, WORD [cluster]                  ; identify current cluster
          mov     cx, ax                              ; copy current cluster
          mov     dx, ax                              ; copy current cluster
          shr     dx, 0x0001                          ; divide by two
          add     cx, dx                              ; sum for (3/2)
          mov     bx, 0x0200                          ; location of FAT in memory
          add     bx, cx                              ; index into FAT
          mov     dx, WORD [bx]                       ; read two bytes from FAT
          test    ax, 0x0001
          jnz     .ODD_CLUSTER
          
     .EVEN_CLUSTER:
     
          and     dx, 0000111111111111b               ; take low twelve bits
         jmp     .DONE
         
     .ODD_CLUSTER:
     
          shr     dx, 0x0004                          ; take high twelve bits
          
     .DONE:
     
          mov     WORD [cluster], dx                  ; store new cluster
          cmp     dx, 0x0FF0                          ; test for end of file
          jb      LOAD_IMAGE
          
     DONE:
     
          mov     si, msgCRLF
          call    Print
          push    WORD 0x0050
          push    WORD 0x0000
          retf
          
     FAILURE:
     
          mov     si, msgFailure
          call    Print
          mov     ah, 0x00
          int     0x16                                ; await keypress
          int     0x19                                ; warm boot computer


     
     absoluteSector db 0x00
     absoluteHead   db 0x00
     absoluteTrack  db 0x00
     
     datasector  dw 0x0000
     cluster     dw 0x0000
     ImageName   db "KERNEL  BIN"
     msgLoading  db 0x0D, 0x0A, "Loading Boot Image ", 0x0D, 0x0A, 0x00
     msgCRLF     db 0x0D, 0x0A, 0x00
     msgProgress db ".", 0x00
     msgFailure  db 0x0D, 0x0A, "ERROR : Press Any Key to Reboot", 0x00
     
          TIMES 510-($-$$) DB 0
          DW 0xAA55
and here is my second stage:

Code: Select all

[BITS 16]       ; We need 16-bit intructions for Real mode

        cli                     ; Disable interrupts, we want to be alone

        xor ax, ax
        mov ds, ax              ; Set DS-register to 0 - used by lgdt

        lgdt [gdt_desc]         ; Load the GDT descriptor

        mov eax, cr0            ; Copy the contents of CR0 into EAX
        or eax, 1               ; Set bit 0
        mov cr0, eax            ; Copy the contents of EAX into CR0
        
        jmp 08h:clear_pipe      ; Jump to code segment, offset clear_pipe


[BITS 32]                       ; We now need 32-bit instructions
clear_pipe:

        mov ax, 10h             ; Save data segment identifyer
        mov ds, ax              ; Move a valid data segment into the data segment register
        mov ss, ax              ; Move a valid data segment into the stack segment register
        mov esp, 090000h        ; Move the stack pointer to 090000h

        mov byte [ds:0B8000h], 'P'      ; Move the ASCII-code of 'P' into first video memory
        mov byte [ds:0B8001h], 1Bh      ; Assign a color code
        
hang:
        jmp hang                ; Loop, self-jump


gdt:                    ; Address for the GDT

gdt_null:               ; Null Segment
        dd 0
        dd 0

gdt_code:               ; Code segment, read/execute, nonconforming
        dw 0FFFFh
        dw 0
        db 0
        db 10011010b
        db 11001111b
        db 0

gdt_data:               ; Data segment, read/write, expand down
        dw 0FFFFh
        dw 0
        db 0
        db 10010010b
        db 11001111b
        db 0

gdt_end:                ; Used to calculate the size of the GDT



gdt_desc:                       ; The GDT descriptor
        dw gdt_end - gdt - 1    ; Limit (size)
        dd gdt                  ; Address of the GDT
Please help
Gizmic OS
Currently - Busy with FAT12 driver and VFS
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: Triple Fault When entering PMode

Post by neon »

Add org 0x500 to the beginning of your 2nd stage. This is needed as you null out DS and the code and data is located at 0x500.

The reason for the triple fault is that I believe the gdt is being loaded from a different invalid address then what you expect.

Also, just a slight error in your code: The gdt pointer is not the gdt descriptor. In your GDT you have 3 descriptors (Not segments!) -- null, code, and data. Do not mix the concepts of segment:offset addressing with protected modes descriptor:linear addressing ;) I just noticed a couple of your comments use incorrect terms so I wanted to clarify.
Also, IIRC, doesn't a GDT need to be double-word aligned? That may be worth a go.
I don't think so. The only thing that I can find atm in the manuals is for performance purposes, the base address of the GDT should be dword aligned (page 3-21, section 3.5.1). I am not finding anything that states that it is required however.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
System123
Member
Member
Posts: 196
Joined: Mon Jul 07, 2008 1:25 am

Re: Triple Fault When entering PMode

Post by System123 »

Ok I have made those changes and it works. However I wish to now convert that code into a stub which I will link to my kernel. The problem is when I try compile it with -f elf it tells me that [Org] is an unknown directive. How can I fix this problem in order to allow compilation in elf format.
Gizmic OS
Currently - Busy with FAT12 driver and VFS
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: Triple Fault When entering PMode

Post by neon »

The org directive is only allowed in binary programs.

If you want it to be in elf format, you will need to a)take out the org directive and set up your segment registers correctly, and b) Have your first stage boot loader parse the ELF header structures to locate the entry point of your elf program and execute it.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
User avatar
inflater
Member
Member
Posts: 1309
Joined: Thu Sep 28, 2006 10:32 am
Location: Slovakia
Contact:

Re: Triple Fault When entering PMode

Post by inflater »

@System123: Why do you want to use ELF anyway?
My web site: http://inflater.wz.cz (Slovak)
Derrick operating system: http://derrick.xf.cz (Slovak and English :P)
System123
Member
Member
Posts: 196
Joined: Mon Jul 07, 2008 1:25 am

Re: Triple Fault When entering PMode

Post by System123 »

My bootloader does that. I have changed to a bootloader that supports that. How would I go about setting up the registers properly??

I use elf because that is how I compile my source in free pascal. I compile it for Linux so therefore I must use elf. Elf supports more than COFF and I believe it is faster than Win32.

I was also having problems trying to link Win32 code. as you use the GNU-Bintutils linker which is supports ELF and not PE.
Gizmic OS
Currently - Busy with FAT12 driver and VFS
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: Triple Fault When entering PMode

Post by neon »

How would I go about setting up the registers properly??
Because you switch boot loaders I cannot guarantee that your 2nd stage is at linear address 0x500 (0x50:0 real mode). Assuming that it still is, then DS should be 0x50 along with your other segment registers including CS. This insures all of your offsets are relative to the correct segment address. ie; its basic segment:offset addressing concepts.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
User avatar
inflater
Member
Member
Posts: 1309
Joined: Thu Sep 28, 2006 10:32 am
Location: Slovakia
Contact:

Re: Triple Fault When entering PMode

Post by inflater »

@System123: You can compile your kernel to binary, if you want. Just compile your sources (even the asm stub) to a.out and then you can link it to binary this way:

Code: Select all

ld (your source files.o) --oformat binary -o (your output file name) -Ttext 0x(if you use .TEXT section as code section, this is the ORG in pmode)
Not propagating (my favorite :P) binary format, just to show how can that be done, since wiki compiles to elf.
My web site: http://inflater.wz.cz (Slovak)
Derrick operating system: http://derrick.xf.cz (Slovak and English :P)
Post Reply