lgdt not working, mov cr0, eax causing triple fault.

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.
XeonX369
Member
Member
Posts: 29
Joined: Mon Sep 28, 2009 1:12 pm

lgdt not working, mov cr0, eax causing triple fault.

Post by XeonX369 »

Hello OSDev forum community.

I have make progress in my stage2 bootloader, with a notable snag. My lgdt instruction appears to have triple faulted, leaving the program to not actually print the following message.

I was on another forum site, and someone told me to try setting the ORG directive to 7E00h (where I have this code loaded at). That didn't work so well, with the code not appearing to execute the printing instructions, and yet a simple "debug instruction" (which had been made by writing directly to video ram) outputing until the instruction to enable protected mode.

The exact piece of GDT setup code is shown:

Code: Select all

gdtset:		;function to setup and load the Global Descriptor Table
	xor ax, ax		;clear ax register		
	mov es, ax
	mov si, gdt		;start of GDT table into SI register
	mov di, [gdtbse]	;locate GDT at 500h in memory
	mov cx, [gdtsze]	;size of the GDT (defined my fancy footwork)
	push ds			;remember pre-modified DS register
	mov ds, ax

	cld			;clear the direction flag
	rep movsb		;move byte from DS:SI to ES:DI

	pop ds			;restore pre-modified DS register

	lgdt[gdtr]		;load the Global Descriptor Table

	lea si, [gdtmess]	;load the address of the GDT Setup Message into SI
	call prntstr		;print the string
ret
I had to insert the push ds and pop ds instructions because this would completely shut the operation down for semi-obvious reason (which maybe now are not so correct).

My primary question is, why is lgdt not working? Am I not setting it up correctly. Does the ORG directive have anything to do with this?

Also, why, when I set the ORG directive to flat zero, does the program not execute any printing code, up until the protected mode enabler when it triple faults? Is this due to a GDT not being loaded?

To aide in information, my information layout is as such:
7C00 -> 7E00 :bootsector (stage 1)
7E00 -> 8200 :bootloader (stage 2)
7A00 -> 7C00 :stack (growing down from 7C00 towards 7A00)
9200 -> _undetermined_ :kernel
500 -> 5C0 :GDT

Many thanks for your help, your wisdom is appreciated.
Attachments
bootloader.asm
(7.5 KiB) Downloaded 115 times
User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by ~ »

If your program is loaded at 7E00h then you should use ORG 7E00h. In that way I think you could set DS, ES, FS and GS to 0.

Also change these lines:

Code: Select all

	mov ax, 7E00h		;setup segment registers
    	mov ds, ax
    	mov es, ax
	mov fs, ax
	mov gs, ax

	mov ax, 7C00h		;stack grows down from 7C00h
    	mov ss, ax

They should be:

Code: Select all

	mov ax, 7E0h		;setup segment registers
    	mov ds, ax
    	mov es, ax
	mov fs, ax
	mov gs, ax

	mov ax, 7C0h		;stack grows down from 7C00h
    	mov ss, ax
Otherwise DS, ES, FS and GS will get translated to 7E000h and SS into 7C000h.

Also, you preferably should first do the jump to protected mode and then load the segments so they get properly loaded according to protected mode rules.

The basic sequence should be:

- setup your GDT and GDTR
- enable A20
- execute LGDT
- enable protected mode in CR0
- do a far jump to protected mode
- (at this point the code should be 32-bit with BITS 32)
- load DS, ES, FS, GS and SS now that we are in protected mode (don't access any memory or stack at this moment)

It looks like you are following that sequence.
XeonX369
Member
Member
Posts: 29
Joined: Mon Sep 28, 2009 1:12 pm

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by XeonX369 »

Also change these lines:

Code:
mov ax, 7E00h ;setup segment registers
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax

mov ax, 7C00h ;stack grows down from 7C00h
mov ss, ax



They should be:

Code:
mov ax, 7E0h ;setup segment registers
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax

mov ax, 7C0h ;stack grows down from 7C00h
mov ss, ax


Otherwise DS, ES, FS and GS will get translated to 7E000h and SS into 7C000h.
~ ... I tried setting the segment registers to the recommended values, but it now simply prints out two apparently garbage characters: what looks like an "upside-down L," and a "heart" character.
User avatar
XanClic
Member
Member
Posts: 138
Joined: Wed Feb 13, 2008 9:38 am

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by XanClic »

If you set SS to 0x07C0 (and SP to 0x0000), the stack won't grow towards 0x7A00. [-X
The first word you push on it will force SP to overflow (becoming 0xFFFE) and will thus be stored on 0x07C0:0xFFFE, which is actually 0x107BE. So you should set SS to 0x0000 and SP to 0x7C00. :wink:
XeonX369
Member
Member
Posts: 29
Joined: Mon Sep 28, 2009 1:12 pm

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by XeonX369 »

If you set SS to 0x07C0 (and SP to 0x0000), the stack won't grow towards 0x7A00. [-X
The first word you push on it will force SP to overflow (becoming 0xFFFE) and will thus be stored on 0x07C0:0xFFFE, which is actually 0x107BE. So you should set SS to 0x0000 and SP to 0x7C00. :wink:
I have set the SS register to 0x0000 and SP to 0x7C00, and yet I still have the junk character output, culminating with a triple fault, complete with the jumk output of (^2)JB, with ^2 representing a superscript 2.

Edit: If it helps, here is my Bochs register dump, for which I am working on interpreting:

Code: Select all

00034236000i[CPU0 ] EFER   = 0x00000000
00034236000i[CPU0 ] | RAX=0000000000000e00  RBX=000000000000000a
00034236000i[CPU0 ] | RCX=0000000000090002  RDX=0000000000000000
00034236000i[CPU0 ] | RSP=0000000000007c00  RBP=0000000000000000
00034236000i[CPU0 ] | RSI=00000000000e7c9a  RDI=000000000000ffac
00034236000i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
00034236000i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
00034236000i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
00034236000i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
00034236000i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af pf cf
00034236000i[CPU0 ] | SEG selector     base    limit G D
00034236000i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00034236000i[CPU0 ] |  CS:7e00( 0004| 0|  0) 0007e000 0000ffff 0 0
00034236000i[CPU0 ] |  DS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00034236000i[CPU0 ] |  SS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00034236000i[CPU0 ] |  ES:7e00( 0005| 0|  0) 0007e000 0000ffff 0 0
00034236000i[CPU0 ] |  FS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00034236000i[CPU0 ] |  GS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00034236000i[CPU0 ] |  MSR_FS_BASE:0000000000000000
00034236000i[CPU0 ] |  MSR_GS_BASE:0000000000000000
00034236000i[CPU0 ] | RIP=000000000000ca9a (000000000000ca9a)
00034236000i[CPU0 ] | CR0=0x60000010 CR2=0x0000000000000000
00034236000i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
Also, in order to give you a more complete view of the current environment, I have uploaded my bootsector (stage1) code and the current state of this code, as an attachment.
Attachments
bootloader.asm
Bootloader (stage 2 - this code)
(7.55 KiB) Downloaded 81 times
bootsector.asm
Bootsector (stage 1)
(4.11 KiB) Downloaded 79 times
User avatar
XanClic
Member
Member
Posts: 138
Joined: Wed Feb 13, 2008 9:38 am

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by XanClic »

Code: Select all

[ORG 7E00h]     ;Assembler Directive denoting this as existing at 7E00h

begin:          ;function to jump over the data section and into the main boot sequence
        jmp 7E00h:boot
That's wrong.
~ wrote:If your program is loaded at 7E00h then you should use ORG 7E00h. In that way I think you could set DS, ES, FS and GS to 0.
It should look like that:

Code: Select all

[ORG 7E00h]
begin:
        jmp 0000h:boot
With org 0x7E00 and a CS equal to 0x7E00, you actually jump to something like 0x7E00:0x7E00, which is 0x7E000+0x7E00=0x85E00. That's anywhere in nowhere.
XeonX369
Member
Member
Posts: 29
Joined: Mon Sep 28, 2009 1:12 pm

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by XeonX369 »

Code: Select all

It should look like that:
Code:
[ORG 7E00h]
begin:
        jmp 0000h:boot
With this, I still get the hanging / triple fault. I am starting to seriously question whether it is in my Bootsector, and yet Bochs gets through to the Bootloader as evidenced by the CS register's value in my log.

Code: Select all

00025816000i[CPU0 ] |  [b]CS:7e00[/b]( 0004| 0|  0) 0007e000 0000ffff 0 0
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by neon »

Either use ORG 0 with your segments set correctly to 0x7e0 or use ORG 0x7e00 with segments set to 0. Both should work without issues. I also advise reading up on how real mode addressing works afterwords you might be able to fix your addressing issues.

If you are not sure if Bochs gets to someplace, use the debugger that comes with Bochs - it can be quite helpful.

*edit: Also, I can be wrong here but gdtsze dw gdt-gdtend-1 in your GDTR definition looks backward when your GDT is defined as:

Code: Select all

gdt:
;...gdt selectors...
gdtend:
To me the size should be gdtend - gdt - 1 instead.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by ~ »

I attached some changes, now you can load the new bootsector and new bootloader to a floppy disk formatted with FAT12. I used YASM assembler to compile it.

Also, listen closely to what others have said. It's true, your GDT size calculation was wrong.

I have attached 2 source files, a FAT12-compatible bootsector and your modified second stage bootloader.

Now your second stage bootloader is being loaded at 80000h (always at the Real Mode memory area), I did this to make a fast and clear test to try and fix the problems.

In your modified bootloader file, I marked with ';MODIFY' the most important things I added and changed.

Now you should grasp it before continuing, I commented out the call to the kernel loading routine because I don't know how it works and probably it cannot read FAT12, and now you are left successfuly into protected mode, after which a MOV DWORD[0xB8000h],"1234" is executed (to write at the beginning of the text screen), and then a CLI and HLT so that you can see the result now in protected mode.

I didn't go further to set up your stack, but now you should have a working Protected Mode environment, and be able to complete what is left.

Maybe you would want to load your final kernel at a place like 0x100000 (1048576, just at the second megabyte, just away from the limited Real Mode memory); otherwise, if you go past 0x9FBFF, you will write reserved memory areas, and some computers seem to lock because of that.

In the way I suggest, you have a lot more free memory than just some kilobytes restricted and wrapped inside reserved areas.
Attachments
FAT12_boot.asm
(13.21 KiB) Downloaded 120 times
bootloader.asm
(8.04 KiB) Downloaded 96 times
XeonX369
Member
Member
Posts: 29
Joined: Mon Sep 28, 2009 1:12 pm

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by XeonX369 »

Thank you neon for your information on the GDT size. I had inserted that line of code as part of a last minute change from an absolute definition to a definition defined by variables, and overlooked the obvious, making a foolish mistake.

~, I most surely appreciate your offer to use your bootsector. I assembled it as well as your modified version of the booloader... and it fails to boot in both Qemu and Bochs. I used both NASM and YASM to compile it in binary.

Also, I would like to say that I am the type that just loves doing things for myself. I don't mean not seeking help and being grateful for it (I am doing this right now), it is just that I like to have my own environment where I can control what is happening and is my own code.

Anyways, back to the topic of the code. I have modified my code to reflect the changes that you have put forth in your code, and have decided to load the kernel at 100000h, as it seems to be the "popular option" amongst the tutorials that I have used.

The code executes all the way through until the kernel jump:

Code: Select all

jmp code:9200h	;jump to residence of Kernel
This causes the code to triple fault.

As just a little "heads up," my current setup is to have my bootsector and bootloader, as well as the kernel be placed into an image file, which may be used to emulate a floppy, or to be written to one. I do realize that using FAT is the most common method, but once again, I would eventually like to setup my own filesystem scheme. I do realize that this is likely to be a clone of FAT, I just feel compelled to write everything.
Attachments
bootloader.asm
Revised Bootloader
(7.56 KiB) Downloaded 67 times
User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by ~ »

briandknopp wrote:~, I most surely appreciate your offer to use your bootsector. I assembled it as well as your modified version of the booloader... and it fails to boot in both Qemu and Bochs. I used both NASM and YASM to compile it in binary.
You have to install the bootsector in the floppy or the floppy image, and then rename your assembled second stage bootloader to "LOADER.BIN", copy it to into the root directory (being an empty floppy preferably) and it should work.

The code executes all the way through until the kernel jump:

Code: Select all

jmp code:9200h	;jump to residence of Kernel
This causes the code to triple fault.
If you have now been able to load your kernel to 0x100000 and set up the protected mode as before, you have already done a far jump in your bootloader itself, so all you need is to make a near jump to the new residence of the kernel, like this:

Code: Select all

jmp 0x100000
It should be possible because your CS register is already loaded with a protected mode value, and now you can jump around in the 4GB address space.

Also, the format of far jump instruction I use to first enter protected mode is like this:

Code: Select all

a32 jmp dword _segment:_offset
XeonX369
Member
Member
Posts: 29
Joined: Mon Sep 28, 2009 1:12 pm

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by XeonX369 »

The exact problem that I seem to be having is that the jmp instruction just isn't working, no matter where I load the kernel to.

Someone told me that the kernel cannot be loaded into anything above 64KB while 16-bit addressing is being used, so I decided to go to 8200h, and even then, it does not work.

I am giving your setup scheme a try, with the bootsector and loader.
User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by ~ »

briandknopp wrote:Someone told me that the kernel cannot be loaded into anything above 64KB while 16-bit addressing is being used, so I decided to go to 8200h, and even then, it does not work.
It can be done, but you need to temporarily enter Protected Mode and it's mandatory to enable A20 line, to configure the data segments with a 32-bit limit; that's what is called Unreal Mode.

You can see a sample here:
http://126.sytes.net/projects/x86/OS/Lo ... real86.asm

I have attached updated code of bootsector and bootloader.

Mainly, I integrated that code module to enable Unreal Mode in the bootloader, you can now access all 4GB of memory from the 16-bit code. You just need to do something like this to reach higher data:

Code: Select all

push ds  ;Save original DS
xor ax,ax
mov ds,ax  ;Set DS to 0 for full
           ;linear 32-bit address
           ;space while using DS

;IMPORTANT NOTE: instructions that make use
;                of 32-bit addressings from
;                16-bit code must use the
;                "a32" and "o32" prefixes to
;                override 16-bit Addressing
;                (thanks to "a32") and override
;                the 16-bit Operand limitation
;                (thanks to "o32"); otherwise, the
;                program locks in (Un)Real Mode:
;;;
  a32 o32 mov dword[0xB8000],"aBcD"

pop ds  ;Restore original DS
Or something like this:

Code: Select all

push ds  ;Save original DS
xor ax,ax
mov ds,ax  ;Set DS to 0 for full
           ;linear 32-bit address
           ;space while using DS

mov eax,0xB8000
mov dword[eax],"aBcD"

pop ds  ;Restore original DS
The bootsector loads the bootloader to 8000h:0000h and using the methods above it should be possible to put the kernel at 0x100000, but FAT code will be required unless you can make your own bootloader load the second stage to 8000h:0000h and use the on-disk format you were going to use originally.
Attachments
bootloader.asm
(31.76 KiB) Downloaded 105 times
FAT12_boot.asm
(13.19 KiB) Downloaded 69 times
User avatar
XanClic
Member
Member
Posts: 138
Joined: Wed Feb 13, 2008 9:38 am

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by XanClic »

~ wrote:
briandknopp wrote:Someone told me that the kernel cannot be loaded into anything above 64KB while 16-bit addressing is being used, so I decided to go to 8200h, and even then, it does not work.
It can be done, but you need to temporarily enter Protected Mode and it's mandatory to enable A20 line, to configure the data segments with a 32-bit limit; that's what is called Unreal Mode.
Though you need to do this in order to access memory above 1MB, you don't need it for memory above 64KB. The first MB is fully accessible in Real Mode, of course.
XeonX369
Member
Member
Posts: 29
Joined: Mon Sep 28, 2009 1:12 pm

Re: lgdt not working, mov cr0, eax causing triple fault.

Post by XeonX369 »

Though you need to do this in order to access memory above 1MB, you don't need it for memory above 64KB. The first MB is fully accessible in Real Mode, of course.
Yes, I realize that Unreal mode is required for over 1MB of memory access. I still can't figure out why the jmp is not performing. I think that I may have an idea however:
I load 16 sectors with my kernel loading function in the bootloader. Perhaps 16 sectors is not enough. I thought this as unlikely, because my kernel is compiled in C as a binary, with a simple string printing routine, and a clear screen. This is linked to a "pre-kernel" file, which is the actual code that is jmped to, which then loads the kernel proper. Perhaps I have to read from more than 16 sectors. Or even the linker is not working, although I do have a valid linker file, which places the code at it's desired location...

I will check this out today, and be back on tomorrow.
Post Reply