GDT woes

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
proxy

GDT woes

Post by proxy »

hey everyone, I am trying to setup a GDT for my OS. I am loading it with grub so I am already in protected mode and all that:

anyway, here is my class to setup things:

Code: Select all

//----------- GlobalDescriptorTable.h------------
[ Message was too long will be posted in follow up

//---------- GlobalDescriptorTable.cpp-----------
#include <GlobalDescriptorTable.h>
#include <iostream>
#include <memset.h>

#define GDT_EMPTY   0 //empty
#define GDT_FULL   1 //full

using std::cout;
using std::endl;

// Returns zero if there are no free descriptors
unsigned int GDT::findEmpty() {
   unsigned int i;
   for (i = 0; i < MAX_ELEM; i++) {
      if (gdt[i] == GDT_EMPTY) {
         gdt[i] = GDT_FULL;
         return i;
      }
   }
   return 0;
}

size_t GDT::setDesc(size_t selector, uint32 base, uint32 limit, uint32 opt) {
   size_t i = selector / 8;
   
   GDTable[i].base_low    = base & 0xFFFF;
   GDTable[i].base_med    = (base >> 16) & 0xFF;
   GDTable[i].base_high   = base >> 24;
   GDTable[i].limit_low   = limit & 0xFFFF;
   GDTable[i].limit_high  = (limit >> 16) & 0x0F;
   GDTable[i].access      = (opt + D_PRESENT) >> 8;
   GDTable[i].granularity = ((opt & 0xff) >> 4);
   
   return i * 8;
}

size_t GDT::addDesc(uint32 base, uint32 limit, uint32 opt) {
   size_t i = findEmpty(); //finds first free descriptor and validates it.
   GDTable[i].base_low    = base & 0xFFFF;
   GDTable[i].base_med    = (base >> 16) & 0xFF;
   GDTable[i].base_high   = base >> 24;
   GDTable[i].limit_low   = limit & 0xFFFF;
   GDTable[i].limit_high  = (limit >> 16) & 0x0F;
   GDTable[i].access      = (opt + D_PRESENT) >> 8;
   GDTable[i].granularity = ((opt & 0xff) >> 4);
   return i * 8;
}

void GDT::remDesc(uint32 selector) {
   selector /= 8;
   gdt[selector] = GDT_EMPTY; //Marks descriptor as free
   memset(&GDTable[selector], 0, sizeof(x86_desc));
}



GDT::GDT() {
   unsigned int i;

   cout << "Loading GDT..." << endl;

   gdt_desc.size    = (sizeof(GDTable) - 1);
   gdt_desc.offset  =  reinterpret_cast<uint32>(GDTable);

   for (i = 0; i < MAX_ELEM; i++) {
      gdt[i] = GDT_EMPTY; //Mark remaining entries as free
   }

   // The first element of the gdt is not usable and is made null
   gdt[0]            = GDT_FULL;
   memset(&GDTable[0], 0, sizeof(x86_desc));


   // kernel will occupy lower 1GB of address space, more than enough ;)
   setDesc(KERNEL_CS, 0x00000000, 0x3fffffff, (D_CODE | D_READ  | D_BIG | D_BIG_LIM));
   setDesc(KERNEL_DS, 0x00000000, 0x3fffffff, (D_DATA | D_WRITE | D_BIG | D_BIG_LIM));

   
   __asm__ __volatile__("lgdt %0" : "=m" (gdt_desc));
   
   
   // BREAKS HERE
   __asm__ __volatile__(
      "xor %eax, %eax      \n"
      "movw $0x10, %ax   \n"
      "movw %ax, %ds      \n"
      "movw %ax, %es      \n"
      "movw %ax, %ss      \n"
      "movw %ax, %fs      \n"
      "movw $0x08, %ax   \n"
      "movw %ax, %cs      \n"
   );
   
}
basically the call the lgdt succeeds, but any attempt to load the segment registers with ANY data (including the same values) results in an "Invalud Opcode" exception from what I can tell, does anyone hav any insight as to what I am doing wrong?

Thanks

proxy
proxy

Re:GDT woes [ header file ]

Post by proxy »

Code: Select all

#ifndef _GLOBALDESCRIPTORTABLE_H_
#define _GLOBALDESCRIPTORTABLE_H_

#include <types.h>


#define KERNEL_CS   0x08
#define KERNEL_DS   0x10


// descriptors for segment

#define D_ACC 0x100 /* Accessed (Data or Code) */

#define D_WRITE 0x200   /* Writable (Data segments only) */
#define D_READ 0x200   /* Readable (Code segments only) */
#define D_BUSY 0x200   /* Busy (TSS only) */

#define D_EXDOWN 0x400 /* Expand down (Data segments only) */
#define D_CONFORM 0x400 /* Conforming (Code segments only) */

#define D_BIG 0x40 /* Default to 32 bit mode */
#define D_BIG_LIM 0x80 /* Limit is in 4K units */



// Defines for the structure of the descriptors:

struct x86_desc {
   unsigned short   limit_low;      /* limit 0..15 */
   unsigned short   base_low;      /* base 0..15 */
   unsigned char   base_med;      /* base 16..23 */
   unsigned char   access;         /* access byte */
   unsigned int   limit_high:4;   /* limit 16..19 */
   unsigned int   granularity:4;   /* granularity */
   unsigned char   base_high;      /* base 24..31 */
} __attribute__ ((packed));


#define MAX_ELEM 100


// The GDT Class

class GDT {
public:
   enum {
      D_LDT         = 0x200,   /* LDT segment */
      D_TASK         = 0x500,    /* Task gate */
      D_TSS         = 0x900,   /* TSS */
      D_CALL         = 0x0C00,    /* 386 call gate */
      D_INT         = 0x0E00,    /* 386 interrupt gate */
      D_TRAP         = 0x0F00,    /* 386 trap gate */
      D_DATA         = 0x1000,    /* Data segment */
      D_CODE         = 0x1800,    /* Code segment */

      // privileges and presences
      D_DPL3         = 0x6000,   /* DPL3 or mask for DPL */
      D_DPL2         = 0x4000,   /* DPL2 or mask for DPL */
      D_DPL1         = 0x2000,   /* DPL1 or mask for DPL */
      D_PRESENT      = 0x8000   /* Present */
   };

public:
   GDT();
   size_t addDesc(uint32 base, uint32 limit, uint32 opt);
   size_t setDesc(size_t selector, uint32 base, uint32 limit, uint32 opt);
   void remDesc(uint32 selector);

public:
   unsigned int findEmpty(void);

   x86_desc GDTable[MAX_ELEM];
   
   //Look up table to determine which elements of the GDTable are used.
   unsigned char gdt[MAX_ELEM]; 

   class Table {
   public:
      uint16 size;
      uint32 offset;
   } __attribute ((packed));
   
   Table gdt_desc;
   
   
};

#endif
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:GDT woes

Post by Pype.Clicker »

i don't know how you managed to assemble "mov %eax, %cs", but this is certainly not a valid opcode ...
proxy

Re:GDT woes

Post by proxy »

i don't know how you managed to assemble "mov %eax, %cs", but this is certainly not a valid opcode ...
i didn't, i used:

Code: Select all

movw %ax, %cs
gcc doesnt seem to complain about it, but perhaps it is still illegal, how should i setup the CS register then?
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:GDT woes

Post by Pype.Clicker »

the only way to set the CS register is through a jmp command ...
for instance

Code: Select all

jmp far 0x08:.here
.here:
proxy

Re:GDT woes

Post by proxy »

yea i saw some code doing a jmp accross 2 nops earlier, but gcc (3.2.3) doesnt seem to like that nor your code :(

basically i've changed that block to be like this:

Code: Select all

   __asm__ __volatile__(
      "xor %eax, %eax      \n"
      "movw $0x10, %ax   \n"
      "movw %ax, %ds      \n"
      "movw %ax, %es      \n"
      "movw %ax, %ss      \n"
      "movw %ax, %fs      \n"   
      "jmp far 0x08:.set_cs   \n"
      ".set_cs:            \n"
   );
and i get this error
/tmp/ccPTF3Oi.s: Assembler messages:
/tmp/ccPTF3Oi.s:316: Error: junk `0x08:.set_cs' after expression
/tmp/ccPTF3Oi.s:425: Error: junk `0x08:.set_cs' after expression
/tmp/ccPTF3Oi.s:426: Error: symbol `.set_cs' is already defined
make: *** [GlobalDescriptorTable.o] Error 1
i'm going to try to get it to output the interum .S file so i can look at the assembly there and see why it seems to be having issues..

any thoughts?

proxy
proxy

Re:GDT woes

Post by proxy »

even wierder..it seems that gcc it outputing the code for the inline asm twice in the interum .S file!

(which explains the "label already exists error")

I dont understand why it is doing this:

but both 308 and 415 have these sets of lines in the .s file...

Code: Select all

lgdt 900(%ebx)
.loc 1 84 0
xor %eax, %eax      
movw $0x10, %ax   
movw %ax, %ds      
movw %ax, %es      
movw %ax, %ss      
movw %ax, %fs   
could i have somthing messed up with my compile flags?

they are:

Code: Select all

g++ -g -march=pentium -Wall -Wno-trigraphs -O2 -ffreestanding -nostdlib -nostdinc -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -mpreferred-stack-boundary=2 -I. -Iinclude -Iinclude/stl -Iinclude/libc -Iinclude/libc/ctype  -c GlobalDescriptorTable.cpp -o GlobalDescriptorTable.o
?

proxy
proxy

Re:GDT woes

Post by proxy »

ok i have found out 1 source of the problem, it seems that gcc is for some very wierd reason emiiting two copies of the GDT constructor (which results in two lables with same name)

i'm going to look into this, but this is about as wierd an error as I've ever seen gcc do...

proxy
proxy

Re:GDT woes

Post by proxy »

well, the "duplicate constructor" thing is apparently a "feature" of gcc v3 and is un-avoidable...so i simply made a function called "init" which has the body of the constructor and the real constructor simply calls that.

apparently this second constructor is for debugging purposes and sever really gets executed at runtime.

anyway, so the duplicate label thing is fixed, but it doesnt seem to like the jmp.

Code: Select all

   asm(
      "xor %eax, %eax      \n"
      "movw $0x10, %ax   \n"
      "movw %ax, %ds      \n"
      "movw %ax, %es      \n"
      "movw %ax, %ss      \n"
      "movw %ax, %fs      \n"   
      // BREAKS HERE
      "jmp far 0x08:.set_cs   \n"
      ".set_cs:            \n"
   );
the error is:
/tmp/ccfxjO0I.s:316: Error: junk `0x08:.set_cs' after expression

anyone know what's up with it?

proxy
Therx

Re:GDT woes

Post by Therx »

I don't think you need the "far" and also the 0x08 is not nessicerily what you need. It needs to be the index of the GDT segment descriptor times by 8 (due to the structure of the segment selectors)

Pete
proxy

Re:GDT woes

Post by proxy »

yes 0x08 is what my KERNEL_CS is set to, so it seems to be right....

proxy
proxy

Re:GDT woes

Post by proxy »

ok, did some more research and it appears AT&T syntax for segment jumps is:

ljmp $SEGMENT, $OFFSET

so i modified the code to do this:

Code: Select all

   // kernel will occupy lower 1GB of address space, more than enough ;)
   setDesc(KERNEL_CS, 0x00000000, 0x3fffffff, (D_CODE | D_READ  | D_BIG | D_BIG_LIM));
   setDesc(KERNEL_DS, 0x00000000, 0x3fffffff, (D_DATA | D_WRITE | D_BIG | D_BIG_LIM));

   
   __asm__ __volatile__("lgdt %0" : "=m" (gdt_desc));
   
   
   
   __asm__ __volatile__(
      "xor %eax, %eax      \n"
      "movw $0x10, %ax   \n"
      "movw %ax, %ds      \n"
      "movw %ax, %es      \n"
      "movw %ax, %ss      \n"
      "movw %ax, %fs      \n"   
      "ljmp $0x08, $.set_cs      \n"
      ".set_cs:            \n"
   );
this compiles and runs....so i _guess_ it works, what would be the best way to test this?

just throw a

Code: Select all

*((uint8 *)0x40000000) = 0xff;
in my code and see what happens?

btw, thanks all the help guys

proxy
Therx

Re:GDT woes

Post by Therx »

I suppose you could just try to print a character at 0xB8000 (on the screen) If you are certain that you're GDT is loaded then if this works it all works. Another method would be to create a data segment with a base of 0xB8000 and then tried to put a character at offset 0 of that segment and see if it prints.

Pete
proxy

Re:GDT woes

Post by proxy »

well i my IO system seems to be contiuing to work, so i guess it works :P

I've actually implemented a bunch of stuff (threads, console, keyboard, syncronization, scheduler, etc) just relying on grubs GDT.

I guess the next step is paging :)

once again, thanks a ton everyone for humoring my questions

proxy
Therx

Re:GDT woes

Post by Therx »

I've actually implemented a bunch of stuff (threads, console, keyboard, syncronization, scheduler, etc) just relying on grubs GDT.
Unconventional ::)
Post Reply