Sure thing. I'm not sure what format is good, but I'll show what I have and can modify to show anything you like. I "verified" my GDT by printing out the GRUB supplied GDT first to show that my structs are "correct". I then set my own GDT and print it out. So, this is my output:
Code: Select all
Startiing M6 OS...
==================
This is the GDT supplied by GRUB:
GDT Starts at 0x00009108 and has 5 entries (40b):
000 pres=0 priv=00 exe=0 rw=0 4K=0 32Bit=0 base 0x00000000 limit 65520 top 0x0000fff0
001 pres=1 priv=00 exe=1 rw=1 4K=1 32Bit=1 base 0x00000000 limit 1048575 top 0xfffff000
002 pres=1 priv=00 exe=0 rw=1 4K=1 32Bit=1 base 0x00000000 limit 1048575 top 0xfffff000
003 pres=1 priv=00 exe=1 rw=1 4K=0 32Bit=0 base 0x00000000 limit 65535 top 0x0000ffff
004 pres=1 priv=00 exe=0 rw=1 4K=0 32Bit=0 base 0x00000000 limit 65535 top 0x0000ffff
====================================
These are the segment registers supplied by GRUB
CS: index=001, isldt=0, priv=0
DS: index=002, isldt=0, priv=0
SS: index=002, isldt=0, priv=0
====================================
Setting gdt
This is my own GDT
GDT Starts at 0x00102000 and has 3 entries (24b):
000 pres=1 priv=00 exe=0 rw=0 4K=0 32Bit=0 base 0x00000000 limit 0 top 0x00000000
001 pres=1 priv=00 exe=1 rw=1 4K=1 32Bit=1 base 0x00000000 limit 1048575 top 0xfffff000
002 pres=1 priv=00 exe=0 rw=1 4K=1 32Bit=1 base 0x00000000 limit 1048575 top 0xfffff000
====================================
Generating segments
CS: index=001, isldt=0, priv=0
DS: index=002, isldt=0, priv=0
SS: index=002, isldt=0, priv=0
Integer representation CS=0x8, DS=0x10, SS=0x10
-->X
The source is pretty verbose because I am trying to stick in C as much as possible. Just using little inline blocks to do things like LGDT. Here are my GDT struct defs:
Code: Select all
//GDT Entry Access bits (8bits)
typedef struct{
uint8_t accessed : 1; //CPU sets this to 1 if the segment is accessed
uint8_t read_write : 1; //On code segments, is segment readable (can never write), on data segments, is segment writeable (can awlays read)
uint8_t direction : 1; //On code segments, if 1, code can be executed at lower privilege. On data segments, if 1 segment grows down (ie offset has to be greater than base).
uint8_t executable : 1; //If 1 code segment, if 0, data segment.
uint8_t app : 1; //0 if system, 1 if application
uint8_t privilege : 2; //Privilege level, 0-3, 0 highest.
uint8_t present : 1; //Must be 1 for valid selector.
} __attribute__((__packed__)) gdt_entry_access_t;
//GDT entry (8 bytes)
typedef struct{
uint16_t limit0_15; //Segment limit, bits 0-15
uint16_t base0_15; //Base address, bits 0-15
uint8_t base16_23; //Base address, bits 16-23
gdt_entry_access_t access; //Access rights
uint8_t limit16_19 : 4; //Segment limit, bits 16-19
uint8_t reserved : 2; //Nothing
uint8_t size : 1; //If 1 32bit protected mode, if 0, 16bit protected
uint8_t granularity : 1; //If 0, limit is measured in bytes, otherwise in 4K pages.
uint8_t base24_31; //Base address, bits 24-31
}__attribute__((__packed__)) gdt_entry_t;
//Size and location of Global Descriptor Table
typedef struct{
uint16_t size;
gdt_entry_t* offset;
} __attribute__((__packed__)) gdt_descriptor_t;
//Segment register format
typedef struct{
unsigned priv : 2;
unsigned isldt : 1;
unsigned index : 13;
} __attribute__((__packed__)) segment_t;
And the code to set it
Code: Select all
void set_gdt_entry(gdt_entry_t* entry, unsigned base, unsigned limit, uint8_t executable, uint8_t privelege, uint8_t read_write, uint8_t size, uint8_t granularity)
{
memset(entry, 0, sizeof(gdt_entry_t));
entry->limit0_15 = (uint16_t)(limit & 0xFFFF);
entry->limit16_19 = (uint8_t)( (limit >> 16) & 0xFF);
entry->base0_15 = (uint16_t)(base & 0xFFFF);
entry->base16_23 = (uint8_t)( (base >> 16) & 0xFF);
entry->base24_31 = (uint8_t)( (base >> 24) & 0xFF);
entry->granularity = granularity;
entry->size = size;
entry->reserved = 0;
entry->access.accessed = 0;
entry->access.direction = 0;
entry->access.executable = executable;
entry->access.read_write = read_write;
entry->access.privilege = privelege;
entry->access.app = 0;
entry->access.present = 1;
}
gdt_descriptor_t gdt_descriptor;
gdt_entry_t gdt[3];
void install_basic_gdt()
{
set_gdt_entry(&gdt[0], 0, 0, 0, 0, 0, 0, 0); //First gdt entry is NULL
set_gdt_entry(&gdt[1], 0, 0xFFFFFFFF, 1, 0, 1, 1, 1); //Entire range is executable
set_gdt_entry(&gdt[2], 0, 0xFFFFFFFF, 0, 0, 1, 1, 1); //Entire range is data
gdt_descriptor.offset = gdt;
gdt_descriptor.size = (sizeof(gdt_entry_t) * 3) - 1;
printf("Setting gdt\n");
set_gdt_descriptor(&gdt_descriptor);
}
//Set the GDT Descriptor to the address given
void set_gdt_descriptor(gdt_descriptor_t* descriptor)
{
//Note, the "m" attribute has the effect of passing the address of
//descriptor, which already is a pointer.
__asm__ __volatile__ ("lgdt %0\n" : : "m"(*descriptor));
}
void set_segs(segment_t* cs_seg, segment_t* ds_seg, segment_t* ss_seg)
{
printf("\n\n\n-->");
//Note, the "m" attribute has the effect of passing the address of
//seg, which already is a pointer.
__asm__ __volatile__ ("mov $0x58, %%eax \n"
"mov $0x3F8, %%edx \n"
"mov $0x10, %%ecx \n"
"outb %%al, %%dx \n"
"movw %%cx, %%gs \n"
"outb %%al, %%dx \n"
);
printf("\n\n\n");
}
Hope this makes sense