Page 1 of 2

GDT Entry

Posted: Wed Jun 12, 2013 11:20 pm
by BMW
I have got myself so confused over GDT entries and endianness......

http://wiki.osdev.org/Global_Descriptor_Table

Is this a correct GDT Entry structure (Language: C, Architecture: x86_32)?

Code: Select all

struct GDTEntry
{
   uint16_t LimitLow;
   uint16_t BaseLow;
   uint8_t BaseMid;
   uint8_t Access;
   uint8_t LimitHigh : 4;
   uint8_t Flags : 4;
   uint8_t BaseHigh;
} __attribute__((packed));
Do I place the items with the least significant bits in first due to Little Endianness? (for example the LimitLow occupies bits 0-15 so I put it first)

Would this work with a CPU using Big Endian?

Re: GDT Entry

Posted: Wed Jun 12, 2013 11:39 pm
by Kazinsal
BMW wrote:Would this work with a CPU using Big Endian?
The GDT is an x86 concept. No big endian CPUs have the structure.

Re: GDT Entry

Posted: Wed Jun 12, 2013 11:47 pm
by BMW
Blacklight wrote:
BMW wrote:Would this work with a CPU using Big Endian?
The GDT is an x86 concept. No big endian CPUs have the structure.
Ah. That makes sense. Thanks. So my structure is right?

Re: GDT Entry

Posted: Thu Jun 13, 2013 12:31 am
by iansjack
The GDT entries are just 32-bit (or 64-bit numbers). So the easy way to answer your question is just to look at memory and see what has been put there by your code.

Re: GDT Entry

Posted: Thu Jun 13, 2013 1:19 am
by walfud

Code: Select all

struct GDTEntry
{
   uint16_t LimitLow;
   uint16_t BaseLow;
   uint8_t BaseMid;
   uint8_t Type : 4; 
   uint8_t S : 1;
   uint8_t DPL : 3;
   uint8_t P : 1;
   uint8_t LimitHigh : 4;
   uint8_t AVL : 1;
   uint8_t L : 1;
   uint8_t DB : 1;
   uint8_t G : 1;
   uint8_t BaseHigh;
} __attribute__((packed));
I'd like to split 'Access' and 'Flags' into individual field.

Re: GDT Entry

Posted: Thu Jun 13, 2013 2:54 am
by walfud
Oh, I see a definition from here: http://www.osdever.net/tutorials/view/b ... t-tutorial

Code: Select all

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));

Re: GDT Entry

Posted: Thu Jun 13, 2013 4:27 am
by sortie
Your structure will be endian-dependent the moment you use data types larger than uint8_t or you use bit fields. Note that the bit fields layout order is also endian-dependent, which may be tricky to get right if you don't understand endianness that well. Personally, I am not that happy with bit fields and just assemble the values with shifts and or, such as gdt->foo = bar << 4 | qux << 0.

Re: GDT Entry

Posted: Thu Jun 13, 2013 10:02 pm
by BMW
sortie wrote:Your structure will be endian-dependent the moment you use data types larger than uint8_t or you use bit fields. Note that the bit fields layout order is also endian-dependent, which may be tricky to get right if you don't understand endianness that well. Personally, I am not that happy with bit fields and just assemble the values with shifts and or, such as gdt->foo = bar << 4 | qux << 0.
Say if I use 8 1-bit bitfields for a byte, I would just order them from MSB to LSB aye?
e.g. is this correct?

Code: Select all

struct GDTEntry
{
	uint16_t LimitLow;
	uint16_t BaseLow;
	uint8_t BaseMid;
	uint8_t PresentBit : 1;
	uint8_t RingLevel : 2;
	uint8_t One : 1;
	uint8_t ExecutableBit : 1;
	uint8_t DCBit : 1;
	uint8_t RWBit : 1;
	uint8_t AccessedBit : 1;
	uint8_t LimitHigh : 4;
	uint8_t GranularityBit : 1;
	uint8_t SizeBit : 1;
	uint8_t Zero : 2;
	uint8_t BaseHigh;
} __attribute__((packed));

Re: GDT Entry

Posted: Fri Jun 14, 2013 2:01 am
by AJ
Hi,

Why don't you create an instance of that struct and then see what you end up with in memory?

Cheers,
Adam

Re: GDT Entry

Posted: Fri Jun 14, 2013 2:02 am
by dozniak
BMW wrote: Say if I use 8 1-bit bitfields for a byte, I would just order them from MSB to LSB aye?
e.g. is this correct?
This depends on the compiler and the ABI. I.e. not correct in general case.

Re: GDT Entry

Posted: Fri Jun 14, 2013 2:18 am
by BMW
dozniak wrote:
BMW wrote: Say if I use 8 1-bit bitfields for a byte, I would just order them from MSB to LSB aye?
e.g. is this correct?
This depends on the compiler and the ABI. I.e. not correct in general case.
Yeah, I am just using bytes as the minimum size and anding and oring stuff together now... less problems.

wtf?

Posted: Fri Jun 14, 2013 2:49 am
by BMW
How come this works?:

Code: Select all

struct GDTInfo gdti;
gdti.size = 8 * descriptorcount - 1;
gdti.address = GDT_ADDRESS;
	
struct GDTInfo* ptr = &gdti;

disable_interrupts();
__asm__ __volatile__("lgdt %0" : : "m" (*ptr));
enable_interrupts();
But that is wrong isn't it? Because it would be passing the value pointed to by ptr to LGDT (which would be the first 4 bytes of the gdti structure...)

If I change it so that I am passing the POINTER to gdti, OS all broken...

This is theoretically correct isn't it?:

Code: Select all

__asm__ __volatile__("lgdt %0" : : "m" (ptr));
EDIT: Sorry if this is a RTFM question, but there is a serious lack of DECENT GCC inline assembly information.

Re: GDT Entry

Posted: Fri Jun 14, 2013 5:29 am
by sortie
BMW wrote: Say if I use 8 1-bit bitfields for a byte, I would just order them from MSB to LSB aye?
e.g. is this correct?
...
Don't assume. If you are doing an ELF-operating system, look up what your compiler generates in the System V ABI (i386 version). Don't pay attention to me. On SysV targets, the bit fields are placed according to the host endianness. That is, struct { unsigned int a1: 1; unsigned int a2: 1; ... unsigned int a32: 1; } will have the exactly opposite order when switching endian (such as going from big endian to little endian). I recommend that you write a test program using bit fields for your host operating system with gcc and use objdump -d to see what the program does.
BMW wrote:EDIT: Sorry if this is a RTFM question, but there is a serious lack of DECENT GCC inline assembly information.
There is such a lack and it's kinda a problem. The root cause is that it's not really a compiler problem, because it depends on the backend and the assembly itself depends on the assembler. If in doubt, write an assembler function in a file instead.

Re: wtf?

Posted: Sat Jun 15, 2013 1:10 am
by Mikemk
BMW wrote:

Code: Select all

disable_interrupts();
__asm__ __volatile__("lgdt %0" : : "m" (*ptr));
enable_interrupts();
That is an excellent way to crash.

Re: wtf?

Posted: Sat Jun 15, 2013 3:34 pm
by BMW
m12 wrote:
BMW wrote:

Code: Select all

disable_interrupts();
__asm__ __volatile__("lgdt %0" : : "m" (*ptr));
enable_interrupts();
That is an excellent way to crash.
Works fine for me.

Please explain...?