I have a LGDT problem too...

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
badengineer
Posts: 6
Joined: Tue Aug 07, 2007 10:38 pm

I have a LGDT problem too...

Post by badengineer »

I have a problem with LGDT too, sympton wise, it's very similar to another thread here.

http://www.osdev.org/phpBB2/viewtopic.php?t=14542

However, I think I'm using the stable toolchain, gcc version 4.1.2 20070502 (Red Hat 4.1.2-12)

The code skeleton is from the operating system class of mit opencourseware... a few other schools use the same code/materials for their classes.

Anyway, the code looks like this:

Code: Select all

struct Segdesc gdt[] =
{
	// 0x0 - unused (always faults -- for trapping NULL far pointers)
	SEG_NULL,

	// 0x8 - kernel code segment
	[GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0),

	// 0x10 - kernel data segment
	[GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0),

	// 0x18 - user code segment
	[GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3),

	// 0x20 - user data segment
	[GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3),

	// 0x28 - tss, initialized in idt_init()
	[GD_TSS >> 3] = SEG_NULL
};

struct Pseudodesc gdt_pd = {
	sizeof(gdt) - 1, (unsigned long) gdt
};

......


	// Map VA 0:4MB same as VA KERNBASE, i.e. to PA 0:4MB.
	// (Limits our kernel to <4MB)
	pgdir[0] = pgdir[PDX(KERNBASE)];

	// Install page table.
	lcr3(boot_cr3);

	// Turn on paging.
	cr0 = rcr0();
	cr0 |= CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_TS|CR0_EM|CR0_MP;
	cr0 &= ~(CR0_TS|CR0_EM);
	lcr0(cr0);

	// Current mapping: KERNBASE+x => x => x.
	// (x < 4MB so uses paging pgdir[0])

	// Reload all segment registers.
	asm volatile("lgdt gdt_pd");
	asm volatile("movw %%ax,%%gs" :: "a" (GD_UD|3));
	asm volatile("movw %%ax,%%fs" :: "a" (GD_UD|3));
	asm volatile("movw %%ax,%%es" :: "a" (GD_KD));
	asm volatile("movw %%ax,%%ds" :: "a" (GD_KD));
	asm volatile("movw %%ax,%%ss" :: "a" (GD_KD));
	asm volatile("ljmp %0,$1f\n 1:\n" :: "i" (GD_KT));  // reload cs
	asm volatile("lldt %%ax" :: "a" (0));

	// Final mapping: KERNBASE+x => KERNBASE+x => x.

	// This mapping was only used after paging was turned on but
	// before the segment registers were reloaded.
	pgdir[0] = 0;

	// Flush the TLB for good measure, to kill the pgdir[0] mapping.
	lcr3(boot_cr3);

The sympton is the system resets after "mov gs, ax",

Before:

Code: Select all

CPU#0
eax:0x80050033, ebx:0xf0114000, ecx:0x000005f0, edx:0x00000000
ebp:0xf0112fd8, esp:0xf0112fc0, esi:0x00010094, edi:0x0008fd7a
eip:0xf010124b, eflags:0x00000086, inhibit_mask:0
cs:s=0x0008, dl=0x0000ffff, dh=0x10cf9a00, valid=1
ss:s=0x0010, dl=0x0000ffff, dh=0x10cf9300, valid=7
ds:s=0x0010, dl=0x0000ffff, dh=0x10cf9200, valid=7
es:s=0x0010, dl=0x0000ffff, dh=0x10cf9300, valid=1
fs:s=0x0010, dl=0x0000ffff, dh=0x00cf9300, valid=1
gs:s=0x0010, dl=0x0000ffff, dh=0x00cf9300, valid=1
ldtr:s=0x0000, dl=0x0000ffff, dh=0x00008200, valid=1
tr:s=0x0000, dl=0x0000ffff, dh=0x00008300, valid=1
gdtr:base=0x00113000, limit=0x17
idtr:base=0x00000000, limit=0x3ff
dr0:0x00000000, dr1:0x00000000, dr2:0x00000000
dr3:0x00000000, dr6:0xffff0ff0, dr7:0x00000400
cr0:0x80050033, cr1:0x00000000, cr2:0x00000000
cr3:0x00114000, cr4:0x00000000

After mov gs, ax:

Code: Select all

CPU#0
eax:0x00000000, ebx:0x00000000, ecx:0x00000000, edx:0x00000683
ebp:0x00000000, esp:0x00000000, esi:0x00000000, edi:0x00000000
eip:0x0000fff0, eflags:0x00000002, inhibit_mask:0
cs:s=0xf000, dl=0x0000ffff, dh=0xff009bff, valid=1
ss:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
ds:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
es:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
fs:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
gs:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
ldtr:s=0x0000, dl=0x0000ffff, dh=0x00008200, valid=1
tr:s=0x0000, dl=0x0000ffff, dh=0x00008300, valid=1
gdtr:base=0x00000000, limit=0xffff
idtr:base=0x00000000, limit=0xffff
dr0:0x00000000, dr1:0x00000000, dr2:0x00000000
dr3:0x00000000, dr6:0xffff0ff0, dr7:0x00000400
cr0:0x00000010, cr1:0x00000000, cr2:0x00000000
cr3:0x00000000, cr4:0x00000000
Need some help to figure out why it is happening... I compared the instruction and symbol file, gdt entries look correct

f010124b: 0f 01 15 50 33 11 f0 lgdtl 0xf0113350

f0113350 D gdt_pd

I attached the tar file; gmake will generate the image file.

Thanks.
badengineer
Posts: 6
Joined: Tue Aug 07, 2007 10:38 pm

tarball

Post by badengineer »

attached. thanxxxxxx
Attachments
lab2.tar.gz
(60.65 KiB) Downloaded 54 times
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

Code: Select all

cs:s=0xf000, dl=0x0000ffff, dh=0xff009bff, valid=1
ss:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
ds:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
es:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
fs:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
gs:s=0x0000,
gdtr:[b]base=0x00000000[/b], [b]limit=0xffff[/b] 
Notice that the gdt pointer and limit are wrong. What is the definition of Pseudodesc? Is the limit field a 16-bit integer?

Code: Select all

[GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0), 
Mein Gott! What does this do?! I've been coding C since I was 14 and have never seen this syntax before... :S

JamesM
badengineer
Posts: 6
Joined: Tue Aug 07, 2007 10:38 pm

Post by badengineer »

Pseudodesc is defined as

Code: Select all

// Pseudo-descriptors used for LGDT, LLDT and LIDT instructions.
struct Pseudodesc {
	uint16_t pd_lim;		// Limit
	uint32_t pd_base;		// Base address
} __attribute__ ((packed));

gdt pointer was ok before the "mov gs, ax" instruction...


SEG is a macro: 8)

Code: Select all

#define SEG(type, base, lim, dpl) (struct Segdesc)		\
{ ((lim) >> 12) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff,	\
    type, 1, dpl, 1, (unsigned) (lim) >> 28, 0, 0, 1, 1,		\
    (unsigned) (base) >> 24 }


----------
// Segment Descriptors
struct Segdesc {
	unsigned sd_lim_15_0 : 16;  // Low bits of segment limit
	unsigned sd_base_15_0 : 16; // Low bits of segment base address
	unsigned sd_base_23_16 : 8; // Middle bits of segment base address
	unsigned sd_type : 4;       // Segment type (see STS_ constants)
	unsigned sd_s : 1;          // 0 = system, 1 = application
	unsigned sd_dpl : 2;        // Descriptor Privilege Level
	unsigned sd_p : 1;          // Present
	unsigned sd_lim_19_16 : 4;  // High bits of segment limit
	unsigned sd_avl : 1;        // Unused (available for software use)
	unsigned sd_rsv1 : 1;       // Reserved
	unsigned sd_db : 1;         // 0 = 16-bit segment, 1 = 32-bit segment
	unsigned sd_g : 1;          // Granularity: limit scaled by 4K when set
	unsigned sd_base_31_24 : 8; // High bits of segment base address
};

User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

Code: Select all

CPU#0
eax:0x80050033
There is no possible way that is a valid segment selector.

You are essentially doing:

Code: Select all

mov gs, 0x0033
Which, I think, is wrong.

Code: Select all

[GD_KT >> 3] =
That was the bit I was actually talking about! :P

JamesM
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Post by jnc100 »

What's GD_UD #defined as?

Regards,
John.
badengineer
Posts: 6
Joined: Tue Aug 07, 2007 10:38 pm

Post by badengineer »

Code: Select all

// Global descriptor numbers
#define GD_KT     0x08     // kernel text
#define GD_KD     0x10     // kernel data
#define GD_UT     0x18     // user text
#define GD_UD     0x20     // user data
#define GD_TSS    0x28     // Task segment selector
because of 8 byte per entry... gdt[] array just use these constants to define element 1,2,3,4,5.

Code: Select all

[GD_UT >> 3] = 
is creating an associated array type of structure... not really defined in C, maybe? but it does work. You can access the array as gdt.
badengineer
Posts: 6
Joined: Tue Aug 07, 2007 10:38 pm

Post by badengineer »

ok, problem solved... After some googling and experiements, I found out that I have to remove CR0_WP from this line before reload cr0

Code: Select all

cr0 |= CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_TS|CR0_EM|CR0_MP;
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

Hmm strange that WP (supervisor-mode write-protect) causes you to fault. Are you forgetting to mark your kernel pages read-write?

JamesM
User avatar
KrnlHckr
Member
Member
Posts: 36
Joined: Tue Jul 17, 2007 9:16 am
Location: Washington, DC Metro Area
Contact:

Post by KrnlHckr »

badengineer wrote:

Code: Select all

[GD_UT >> 3] = 
is creating an associated array type of structure... not really defined in C, maybe? but it does work. You can access the array as gdt.


Funky! I'm with JamesM on this one. I, too, have never seen such an animal in C before. I'm quite familiar with the Perl concept of associative array (or hash as Perl-speak would say it), but this is new to me.

I took a peek at the MIT code... :shock:

I've only ever been told, "No operations on the left of an assignment." I've further heard, "Operations on the left of the assignment are undefined. YMMV."

badengineer: do you have a reference for the language-specifics of this critter that we can look at? I'm curious to know why this sort of thing was chosen and not "something simpler". Is it performance related?

:!: EDIT: I got frisky and posted to comp.lang.c and the response came back real quick. Eric Sosman says the following in his follow-up to my post:

The Good Eric Sosman at comp.lang.c wrote: Notice the square brackets around the puzzling
expression. This is one of the new initializer forms
introduced in C99, one that lets you initialize
specific elements in an array. This C90 declaration

int array[] = { 0, 0, 0, 0, 1, 0, 0, 0, 0, 2 };

can be written in C99 as

int array[] = { [4] = 1, [9] = 2 };

(As in C90, any elements you don't mention receive zeroes.
And as in C90, the array size can be calculated from the
initializer list, but slightly differently: In C90 the
array is made large enough to hold as many initializers
as you write, while in C99 it holds the largest-mentioned
intialized element.)

In your example, GC_KT is presumably a macro that
expands to a compile-time constant -- let's suppose it's
twenty-four, so GC_KT >> 3 is three. Then you've got:

struct Segdesc gdt[] = { [3] = yadda };

... which creates an array with four elements, initializes
elements [0], [1], and [2] to zeroes, and initializes the
final element [3] to yadda.

So, if I've digested all that correctly:

Code: Select all

struct Segdesc gdt[] =
{
   SEG_NULL,
   [GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0),
   [GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0),
   [GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3),
   [GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3),
   [GD_TSS >> 3] = SEG_NULL
};
works out to:

Code: Select all

struct Segdesc gdt[] =
{
     SEG_NULL,             /* whatever this is #defineD as */
     gdt[1] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0),
     gdt[2] = SEG(STA_W, 0x0, 0xffffffff, 0),
     gdt[3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3),
     gdt[4] = SEG(STA_W, 0x0, 0xffffffff, 3),
     gdt[5] = SEG_NULL
};
Now I have to work through that SEG macro! :) Something new learned every day.
"If your code won't run, verify that you are, indeed, using the STABLE branches of your toolchain!" -- KrnlHckr, 2007 :oops:
badengineer
Posts: 6
Joined: Tue Aug 07, 2007 10:38 pm

Post by badengineer »

YOU ARE RIGHT. I missed PTE_W with the page table creation.

BINGO. Thanks.
JamesM wrote:Hmm strange that WP (supervisor-mode write-protect) causes you to fault. Are you forgetting to mark your kernel pages read-write?

JamesM
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

No problems :)

JamesM
Post Reply