Setting up GDT in C ?

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.
osdevnewbie
Member
Member
Posts: 36
Joined: Sat Mar 26, 2016 1:25 pm

Setting up GDT in C ?

Post by osdevnewbie »

Hi,
When trying to setup a new GDT using C code following Bran's tuto
http://www.osdever.net/tutorials/view/b ... t-tutorial
I get a triple fault.
here is the code:

Code: Select all

#define GDTBASE    0x800		
struct gdt_entry
{
    unsigned short limit_low;
    unsigned short base_low;
    unsigned char base_middle;
    unsigned char access;
    unsigned char granularity;
    unsigned char base_high;
} __attribute__((packed));

struct gdt_ptr
{
    unsigned short limit;
    unsigned int base;
} __attribute__((packed));

struct gdt_entry gdt[3];
struct gdt_ptr gdtp;

void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran)
{
    gdt[num].base_low = (base & 0xFFFF);
    gdt[num].base_middle = (base >> 16) & 0xFF;
    gdt[num].base_high = (base >> 24) & 0xFF;
    gdt[num].limit_low = (limit & 0xFFFF);
    gdt[num].granularity = ((limit >> 16) & 0x0F);
    gdt[num].granularity |= (gran & 0xF0);
    gdt[num].access = access;
}

void gdt_install()
{
    gdtp.limit = (sizeof(struct gdt_entry) * 3) - 1;
    gdtp.base = GDTBASE ; 
    gdt_set_gate(0, 0, 0, 0, 0);
    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
    memcpy((char *)gdtp.base, (char *)gdt, gdtp.limit);
    load_gdtr();
}

Code: Select all

global _load_gdtr     
extern _gdtp            
_load_gdtr:
    lgdt [_gdtp]        
    mov ax, 0x10      
    mov ds, ax        
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    jmp 0x08:next     
next:
    ret               
and Bochs logout:

Code: Select all

00014963370i[BIOS  ] Booting from 0000:7c00
00015845034i[FLOPPY] partial read() on floppy image returns 180/512
00015933925i[FLOPPY] read() on floppy image returns 0
.....................................................
00017711733i[FLOPPY] read() on floppy image returns 0
00017826504e[CPU0  ] load_seg_reg(SS): not writable data segment
00017826504e[CPU0  ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00017826504e[CPU0  ] interrupt(): gate descriptor is not valid sys seg (vector=0x08)
00017826504i[CPU0  ] CPU is in protected mode (active)
00017826504i[CPU0  ] CS.mode = 32 bit
00017826504i[CPU0  ] SS.mode = 32 bit
00017826504i[CPU0  ] EFER   = 0x00000000
00017826504i[CPU0  ] | EAX=00000010  EBX=00007d04  ECX=00000016  EDX=00000816
00017826504i[CPU0  ] | ESP=0008ffa4  EBP=0008ffd0  ESI=000e0000  EDI=0000ffac
00017826504i[CPU0  ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf ZF af PF cf
00017826504i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00017826504i[CPU0  ] |  CS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00017826504i[CPU0  ] |  DS:0010( 0002| 0|  0) ffffffff ffffffff 1 1
00017826504i[CPU0  ] |  SS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00017826504i[CPU0  ] |  ES:0010( 0002| 0|  0) ffffffff ffffffff 1 1
00017826504i[CPU0  ] |  FS:0010( 0002| 0|  0) ffffffff ffffffff 1 1
00017826504i[CPU0  ] |  GS:0010( 0002| 0|  0) ffffffff ffffffff 1 1
00017826504i[CPU0  ] | EIP=0000101d (0000101d)
00017826504i[CPU0  ] | CR0=0x60000011 CR2=0x00000000
00017826504i[CPU0  ] | CR3=0x00000000 CR4=0x00000000
00017826504i[CPU0  ] 0x000000000000101d>> mov ss, ax : 8ED0
00017826504p[CPU0  ] >>PANIC<< exception(): 3rd (13) exception with no resolution
It seems that the fault occures when doing the far jump: jmp 0x08:next
I've spent many hours trying to fix it, alas with no success.
Edit:
Unlike Bran, I'm not loading the kernel with GRUB, I'm using Blundell's bootsector.
Many thanks for your help
Last edited by osdevnewbie on Sat Nov 25, 2017 11:05 am, edited 1 time in total.
User avatar
Velko
Member
Member
Posts: 153
Joined: Fri Oct 03, 2008 4:13 am
Location: Ogre, Latvia, EU

Re: Setting up GDT in C ?

Post by Velko »

GDTBASE
What is that?
If something looks overcomplicated, most likely it is.
osdevnewbie
Member
Member
Posts: 36
Joined: Sat Mar 26, 2016 1:25 pm

Re: Setting up GDT in C ?

Post by osdevnewbie »

Velko wrote:
GDTBASE
What is that?
I want to put the IDT at adress 0X00 and the GDT just after at 0x800.
Even without moving the GDT to 0x800 I get the triple fault any way.
MichaelPetch
Member
Member
Posts: 799
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Setting up GDT in C ?

Post by MichaelPetch »

You are going to have to show all your code, including the bootsector you are using and the commands you use to build the kernel. Placing your project into github or similar service would be beneficial.
FallenAvatar
Member
Member
Posts: 283
Joined: Mon Jan 03, 2011 6:58 pm

Re: Setting up GDT in C ?

Post by FallenAvatar »

Code: Select all

00017826504e[CPU0  ] load_seg_reg(SS): not writable data segment
00017826504e[CPU0  ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00017826504e[CPU0  ] interrupt(): gate descriptor is not valid sys seg (vector=0x08)
hmm......

- Amy
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Setting up GDT in C ?

Post by iansjack »

You appear to be copying one less byte of the GDT than you need to. This could leave you with an invalid data (and stack) selector.

You don't show your memcpy function, but presumably you are sure it works correctly. Single-step the code and check the values in your GDT after moving it.
osdevnewbie
Member
Member
Posts: 36
Joined: Sat Mar 26, 2016 1:25 pm

Re: Setting up GDT in C ?

Post by osdevnewbie »

iansjack wrote: You don't show your memcpy function, but presumably you are sure it works correctly.
Thanks all for your help.
here's the dump of memory at 0x800 after memcpy():

0x00000800 --> 0x00000000
0x00000804 --> 0x00000000
0x00000808 --> 0x0000FFFF
0x0000080C --> 0x00CF9A00
0x00000810 --> 0x0000FFFF
0x00000814 --> 0x00CF9200
MichaelPetch
Member
Member
Posts: 799
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Setting up GDT in C ?

Post by MichaelPetch »

I took the Blundell boot_sect.asm built it up, put it in a disk image a the first sector. used the link.ld that is in the Bran tutorial and changed the origin point to 0x1000 instead of 0x100000. Created a C file with your code + a copy of memcpy from Bran's tutorial. Added an entry point in an assembler that calls a main C function. I created such a function that simply calls gdt_install and then goes into an infinite loop. I linked it altogether listing the object file with the entry point first to an ELF executable. Converted it to a binary kernel file. Placed the kernel file on the subsequent sectors and it ran as expected and the GDT loaded. I agree with iansjack about the memcpy - you have to add 1 to the limit to copy the whole thing. My code worked without that change, but it really needs to be done.

I suspect the problem is related to code you haven't shown, or in how you build and link your kernel. You really leave it as a guessing game as to what is wrong without providing code and the process you use to build your kernel and disk image, but I had no problem taking the Blundell and Bran code and getting it to work with no hassles.As it stands with the exception of the memcpy copying one fewer byte than it should the code you have runs properly.

If you made all your code and the build process available we'd be able to identify what you don't get right. I assume with the leading underscores on your external labels that you are building on the Windows platform? If not using a cross compiler and you are using MinGW or Cygwin compilers - they are temperamental and often require extra command line options or linker script changes to get the desired output. I can't stress enough how valuable a cross compiler is to avoid those hassles.

You should eaily be able to tell where things go wrong by simply setting a breakpoint at 0x1000 in Bochs and then you can single step to the failure. It shouldn't be too many instructions. I'd make sure the instructions being stepped match what you would expect from the code.
osdevnewbie
Member
Member
Posts: 36
Joined: Sat Mar 26, 2016 1:25 pm

Re: Setting up GDT in C ?

Post by osdevnewbie »

Thanks MichaelPetch for spending some of your precious time in helping me.
MichaelPetch wrote:you have to add 1 to the limit to copy the whole thing.
In many tutos I've seen they did it the same manner with -1 !!
cause we start counting from ZERO no?
MichaelPetch wrote:If you made all your code and the build process available we'd be able to identify what you don't get right.
I'll attach a ziped file of the code when I go back home.
MichaelPetch wrote: you are building on the Windows platform?
Yes, using NASM and MINGW.
MichaelPetch wrote: simply setting a breakpoint at 0x1000 in Bochs and then you can single step to the failure.
I've done that. The fault happens when executing: jmp 0x08:next.
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Setting up GDT in C ?

Post by iansjack »

The third parameter of memcpy() is the number of bytes that you want to copy. You want to copy the whole of the GDT, i.e. its size, not its size minus one. Depending upon what is in the byte that you don't copy, in both the source and the destination, this may or may not matter. But it is undefined behaviour.

I think you are getting confused with the fact that array indexes start at zero.

The moral is that you shouldn't copy tutorials blindly without understanding every step. Most (all?) tutorials contain errors.
MichaelPetch
Member
Member
Posts: 799
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Setting up GDT in C ?

Post by MichaelPetch »

osdevnewbie wrote:In many tutos I've seen they did it the same manner with -1 !! cause we start counting from ZERO no?
While it is true that you subtract 1 when filling in the gdtp structure (the CPU internally will add 1 to that value to get the true size), when your own code is dealing with the actual structure you have to use its true size. So if you tell memcpy to copy 23 bytes it will copy 23 bytes. But the structure is actually 24 bytes. You need to deal in the real size, not the value passed through the gdtp limit value.
osdevnewbie wrote:Yes, using NASM and MINGW.
I could have guessed. If you build the code in a certain way you may not find the code fixes up reliocations as exepected. there is a part of me that thinks that may be the case here. I'm curious if when you stepped through your code whether you noticed if the gdtp variable was filled in properly ahead of time. Although you say the `memcpy` worked (the data at 0x800 looks okay) I am under the impression that there is a problem with gdtp. One thing you can do is use bochs to step to the instruction just past the `lgdt` and then issue the command `info gdt` in the debugger. If the `gdtp` variable was filled in properly it should result in output that is similar to this:

Code: Select all

Global Descriptor Table (base=0x0000000000000800, limit=23):
The base ix 0x800 and limit 23. I suspect yours is not that at all since the dump of memory 0x800 looks correct BUT your Bochs debug output has this:

Code: Select all

00017826504i[CPU0  ] |  CS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00017826504i[CPU0  ] |  DS:0010( 0002| 0|  0) ffffffff ffffffff 1 1
00017826504i[CPU0  ] |  SS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00017826504i[CPU0  ] |  ES:0010( 0002| 0|  0) ffffffff ffffffff 1 1
00017826504i[CPU0  ] |  FS:0010( 0002| 0|  0) ffffffff ffffffff 1 1
00017826504i[CPU0  ] |  GS:0010( 0002| 0|  0) ffffffff ffffffff 1 1
Of note is the fact that the base value is 0xffffffff for DS, ES, FS, GS. That's incorrect given that your base for the 0x10 descriptor is 0x00000000. This suggests to me that your gdtp is wrong and is pointing at the wrong memory address for the address of the GDT and/or the limit is wrong
osdevnewbie wrote:I've done that. The fault happens when executing: jmp 0x08:next.
According to the Bochs debugging output you provided the failure is actually on the mov ss, ax instruction, not the JMP. The instruction is listed in the output as:

Code: Select all

0x000000000000101d>> mov ss, ax : 8ED0
The 0x10 descriptor entry is effectively invalid and thus causes a fault to occur when moved into SS.
osdevnewbie
Member
Member
Posts: 36
Joined: Sat Mar 26, 2016 1:25 pm

Re: Setting up GDT in C ?

Post by osdevnewbie »

Even without moving the GDT to 0x800, with memcpy(), I get the fault.

Code: Select all

void gdt_install()
{
    gdtp.limit = (sizeof(struct gdt_entry) * 3) - 1;
    gdtp.base = &gdt ;
    gdt_set_gate(0, 0, 0, 0, 0);
    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
   // memcpy((char *)gdtp.base, (char *)gdt, gdtp.limit);
    load_gdtr();
}
MichaelPetch
Member
Member
Posts: 799
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Setting up GDT in C ?

Post by MichaelPetch »

I don't believe the code is executing the way you think it is. I don't actually believe the generated C code after linking is correct. Among other things, I hope you assemble your NASM as Win32 and not COFF. COFF can cause issues with relocations in my experience. You also have to make sure that when you jump to 0x0000:0x1000 that there is a piece of code at the beginning of the kernel binary that transfers control to your main C function. The last thing you want it to do is start executing the code of some other functions first (which might even be the case).
osdevnewbie
Member
Member
Posts: 36
Joined: Sat Mar 26, 2016 1:25 pm

Re: Setting up GDT in C ?

Post by osdevnewbie »

MichaelPetch wrote:I don't believe the code is executing the way you think it is.
The code is working if I don't resetup the GDT.

I've joint the code and generated binary image.
Attachments
GDT.rar
(6.18 KiB) Downloaded 64 times
osdevnewbie
Member
Member
Posts: 36
Joined: Sat Mar 26, 2016 1:25 pm

Re: Setting up GDT in C ?

Post by osdevnewbie »

iansjack wrote:The third parameter of memcpy() is the number of bytes that you want to copy. You want to copy the whole of the GDT, i.e. its size, not its size minus one.
I think you are getting confused with the fact that array indexes start at zero.
Yes, you're right iansjack, thanks.
But it did'nt fix the fault.
Post Reply