Page 1 of 1

another triple fault loading the GDT!

Posted: Sun Aug 10, 2008 9:41 pm
by niteice
For what's probably the 5th thread in the same number of weeks...

I'm restarting my kernel (again) and following JamesM's guide so I can get up to speed quickly. I get a triple fault when I load my new GDT:

Code: Select all

DT_LoadGDT:
	mov		4(%esp),	%eax	// put gdt pointer in eax
	lgdt	(%eax)

	mov		$0x10,	%ax
	mov		%ax,	%ds
	mov		%ax,	%es
	mov		%ax,	%fs
	mov		%ax,	%gs
	mov		%ax,	%ss

	// jump into the new code segment
	ljmp	$0x08,$.flush
.flush:
	ret
to which Bochs tells me:

Code: Select all

00061636687e[CPU0 ] fetch_raw_descriptor: GDT: index (17)2 > limit (0)
00061636687i[CPU0 ] CPU is in protected mode (active)                 
00061636687i[CPU0 ] CS.d_b = 32 bit                                   
00061636687i[CPU0 ] SS.d_b = 32 bit                                   
00061636687i[CPU0 ] EFER   = 0x00000000                               
00061636687i[CPU0 ] | RAX=0000000000100010  RBX=000000000002bdc0
00061636687i[CPU0 ] | RCX=00000000000000ff  RDX=0000000000000002
00061636687i[CPU0 ] | RSP=0000000000109fcc  RBP=0000000000109fe8
00061636687i[CPU0 ] | RSI=000000000002bf2e  RDI=000000000002bf33
00061636687i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
00061636687i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
00061636687i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
00061636687i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
00061636687i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af pf cf
00061636687i[CPU0 ] | SEG selector     base    limit G D
00061636687i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00061636687i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00061636687i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00061636687i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00061636687i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00061636687i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00061636687i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00061636687i[CPU0 ] |  MSR_FS_BASE:0000000000000000
00061636687i[CPU0 ] |  MSR_GS_BASE:0000000000000000
00061636687i[CPU0 ] | RIP=0000000000100a77 (0000000000100a77)
00061636687i[CPU0 ] | CR0=0x00000011 CR1=0x0 CR2=0x0000000000000000
00061636687i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00061636687i[CPU0 ] >> mov ds, ax : 8ED8
00061636687e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
I've noticed that whichever order I put the segments in, it will always error on the instruction immediately after mov 0x10, %ax.

My GDT is defined as:

Code: Select all

extern void DT_LoadGDT(uint32_t gdt);

#define NUM_GDT_ENTRIES 3

typedef struct
{
	uint16_t lowLimit;
	uint16_t lowBase;
	uint8_t middleBase;
	uint8_t access;
	uint8_t granularity;
	uint8_t highBase;
} __attribute__((packed)) gdtEntry_t;

static gdtEntry_t gdtEntries[NUM_GDT_ENTRIES];

typedef struct
{
	uint16_t limit;
	uint32_t base;
} __attribute__((packed)) gdt_t;

static gdt_t gdt;

static void gdtSet(int32_t i, uint32_t base, uint32_t limit, uint8_t access, uint8_t granularity)
{
	memset(&gdtEntries, 0, sizeof(gdtEntries) * 5);

	gdtEntries[i].lowBase = base & 0xffff;
	gdtEntries[i].middleBase = (base >> 16) & 0xff;
	gdtEntries[i].highBase = (base >> 24) & 0xff;

	gdtEntries[i].lowLimit = limit & 0xffff;
	gdtEntries[i].granularity = (limit >> 16) & 0xffff;

	gdtEntries[i].granularity |= granularity & 0xf0;
	gdtEntries[i].access = access;
}

gdt.limit = (sizeof(gdtEntry_t) * NUM_GDT_ENTRIES) - 1;
gdt.base = (uint32_t)&gdtEntries;

gdtSet(0, 0, 0, 0, 0);	/* null segment */
gdtSet(1, 0, 0xffffffff, 0x9a, 0xcf);	/* code segment */
gdtSet(2, 0, 0xffffffff, 0x92, 0xcf);	/* data segment */
DT_LoadGDT((uint32_t)&gdt);
Sorry for being repetitive, but it's quite frustrating to be stuck at something that seems so simple.

Thanks for any help.

Re: another triple fault loading the GDT!

Posted: Mon Aug 11, 2008 1:42 am
by AJ
Hi,

Are you sure that ESP+4 contains your GDT pointer? The reason for the triple fault is that the CPU thinks the GDT is zero-sized. I can't tell you whether or not you are accessing the correct parameter in DT_LoadGDT because I'm not familiar with AT&T syntax. If you put a jmp $ just before LGDT, you can check whether eax really does contain a pointer to your GDT information structure.

Also, this may not matter for now, but isn't it more usual to set CS (via the far jump) before loading the data segments? It may also be worth checking the value of DS prior to LGDT, because IIRC, LGDT actually loads from ds:[eax].

Cheers,
Adam

Re: another triple fault loading the GDT!

Posted: Mon Aug 11, 2008 9:21 am
by niteice
The disassembly checks out:

Code: Select all

                DT_LoadGDT((uint32_t)&gdt);
  100564:       b8 58 a0 10 00          mov    $0x10a058,%eax
  100569:       89 04 24                mov    %eax,(%esp)
  10056c:       e8 fb 04 00 00          call   100a6c <DT_LoadGDT>
call pushes eip (4 bytes), so it seems to be the right idea.

Re: another triple fault loading the GDT!

Posted: Tue Aug 12, 2008 9:48 am
by niteice
Alright, I triple fault at either:

Code: Select all

mov		%ax,	%ds
if I put that first or

Code: Select all

jmp	$0x08,$.flush
if I put that first.

Re: another triple fault loading the GDT!

Posted: Tue Aug 12, 2008 9:56 am
by System123
I get the same triple faults. I managed to get rid of the mov ds, ax fault by changin it to mov ds, eax.

Re: another triple fault loading the GDT!

Posted: Tue Aug 12, 2008 11:19 am
by niteice
It assembles to the same code. :|

Re: another triple fault loading the GDT!

Posted: Tue Aug 12, 2008 11:44 am
by System123
:S strange because it didn't fault when I did that :S

Re: another triple fault loading the GDT!

Posted: Tue Aug 12, 2008 12:39 pm
by niteice
So I would assume that my GDT is being initialized wrong. There's not really a lot of information on constructing one outside of declaring the raw bytes in an assembly directive though :|

Re: another triple fault loading the GDT!

Posted: Wed Aug 13, 2008 1:41 am
by AJ
Hi,
So I would assume that my GDT is being initialized wrong. There's not really a lot of information on constructing one outside of declaring the raw bytes in an assembly directive though
Have a look at the GDT descriptors in the Intel manuals and http://www.sandpile.org/. These are not assembly specific and if you understand the documentation, you should be able to consturct a GDT in the language of your choice.

Cheers,
Adam

Re: another triple fault loading the GDT!

Posted: Wed Aug 13, 2008 11:31 am
by niteice
Alright, I actually managed to load a working GDT! I actually ended up using the one from OpenBSD:

Code: Select all

static gdtEntry_t gdtEntries[NUM_GDT_ENTRIES] =
{
	{ 0, 0, 0, 0, 0, 0 },	// 0: null limit
	{
		0xffff,
		0,
		0,
		31 | 0 | 0x80,
		0xf | 0 | 0x40 | 0x80,
		0
	},
	{
		0xffff,
		0,
		0,
		19 | 0 | 0x80,
		0xf | 0 | 0x40 | 0x80,
		0
	}
};
Perhaps not the best way to do things but it seems to load fine.