GDT code - boom

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.
ehird
Member
Member
Posts: 214
Joined: Thu Mar 15, 2007 8:48 am

GDT code - boom

Post by ehird »

This is crashing, and bochs gives almost no message - just something like "page splitting instruction". Now, this is my bad translation from bkerndev's NASM version, so I did something wrong, almost certainly. Any ideas? I can't see anything...

Code: Select all

# see gdt.c
.global gdt_flush
.extern gp
gdt_flush:
	lgdt gp
	mov 0x10, %ax
	mov %ds, %ax
	mov %es, %ax
	mov %fs, %ax
	mov %gs, %ax
	mov %ss, %ax
	jmp flush2
flush2:
	ret
I've tried reversing the mov params in various ways, doing "ljmp", just "ret", etc... everything.
urxae
Member
Member
Posts: 149
Joined: Sun Jul 30, 2006 8:16 am
Location: The Netherlands

Re: GDT code - boom

Post by urxae »

ehird wrote:

Code: Select all

# see gdt.c
.global gdt_flush
.extern gp
gdt_flush:
	lgdt gp
	mov 0x10, %ax
	mov %ds, %ax
	mov %es, %ax
	mov %fs, %ax
	mov %gs, %ax
	mov %ss, %ax
	jmp flush2
flush2:
	ret
I'm pretty sure that jmp is supposed to be

Code: Select all

jmp 0x08:flush2
to reload the cs segment value. (That assumes you left the code segment right after the null segment in the gdt)

What assembler are you using? The %'s would indicate gas, but the current operand order would indicate anything but gas (except the first mov).

If it's gas:
* Reverse the mov parameters from the %ds line down
* The jump is written "ljmp $0x8,$flush2"
* Change the first mov to "mov $0x10,%ax" (note the $). The way it's currently written gas will emit code to load ax from memory address 0x10 instead of writing 0x10 to it, IIRC

If it's not gas you should probably remove the %'s and reverse the parameters of the first mov.
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post by XCHG »

Correct me if I'm wrong but AFAIK, for the real-mode caches to be flushed and renewed by 32-bit segment operands, you have to do a near jump BEFORE setting new values for segment registers such as "FS", "CS" and etc.

The Jump after the flush can be where you change the EIP to your kernel's entry. Therefore, you should have something like this:

Code: Select all

  LGDT    [GDTR]
  JMP     CODESEL:FLUSHCSEIP
  
  [BITS 32]
  FLUSHCSEIP:
    
    MOV     EAX , DATASEL
    MOV     DS , EAX
    MOV     ES , EAX
    MOV     FS , EAX
    MOV     GS , EAX
    MOV     SS , EAX
    MOV     ESP , 0xFFFF
    JMP     CODESEL:KERNELENTRY
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
ehird
Member
Member
Posts: 214
Joined: Thu Mar 15, 2007 8:48 am

Post by ehird »

Yep, it's gas. Trying those changes now, thanks urxae and XCHG


Edit: Hm nope.

Code: Select all

gdt_flush:
	lgdt gp
	mov $0x10, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	mov %ax, %ss
	ljmp $0x08, $flush2
flush2:
	ret

Code: Select all

00017504444e[CPU0 ] interrupt(): gate descriptor is not valid sys seg
00017504444e[CPU0 ] interrupt(): gate descriptor is not valid sys seg
00017504444i[CPU0 ] protected mode
00017504444i[CPU0 ] CS.d_b = 32 bit
00017504444i[CPU0 ] SS.d_b = 32 bit
00017504444i[CPU0 ] | EAX=00106814  EBX=0002bdc0  ECX=00000000  EDX=00000000
00017504444i[CPU0 ] | ESP=00106810  EBP=00106870  ESI=0002bf1a  EDI=0002bf1b
00017504444i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf ZF af PF cf
00017504444i[CPU0 ] | SEG selector     base    limit G D
00017504444i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00017504444i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00017504444i[CPU0 ] |  DS:0000( 0000| 0|  0) 00000000 000fffff 1 1
00017504444i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00017504444i[CPU0 ] |  ES:0000( 0000| 0|  0) 00000000 000fffff 1 1
00017504444i[CPU0 ] |  FS:0000( 0000| 0|  0) 00000000 000fffff 1 1
00017504444i[CPU0 ] |  GS:0000( 0000| 0|  0) 00000000 000fffff 1 1
00017504444i[CPU0 ] | EIP=001006fd (001006fd)
00017504444i[CPU0 ] | CR0=0x00000011 CR1=0 CR2=0x00000000
00017504444i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00017504444i[CPU0 ] >> mov eax, dword ptr ds:0x100223 : A123021000
00017504444e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
00017504444i[SYS  ] bx_pc_system_c::Reset(SOFTWARE) called
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

the code looks ok... the dump however gives the impression it has nothing to do with the code snippet given - only cs and ss are loaded while the code snippet given should load all segments.

Have you used bochs's debugger to find the line of code that causes the crash yet?
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

Code: Select all

interrupt() : gate descriptor is not valid sys seg
Seems to imply you might try looking at the IDT table, and making sure you have the fields correct and the CS descriptor for the entry is correct.
info idt when in debug mode will list the interrupt table.

Code: Select all

>> mov eax, dword ptr ds:0x100223 : A123021000 


This might be the instruction EIP is at when the interrupt happens and fails.

Code: Select all

0x100223
This address is writable and readable in the Bochs emulator 2.1.1 that I use. Since you do not have paging enabled by looking at cr0 I would also assume that the mov instruction is where a interrupt happened.

Also the dump seems to correctly show the CS and DS descriptors loaded with the entire four gigabyte address space in scope. Try inserting a:

Code: Select all

hlt
nop
nop
Right, after you load the segment registers. Then type info gdt while in debug mode just to make certain they got loaded correctly. Even better insert a mov instruction for some data in memory to check DS after you load it.

Also by using EIP you can then turn around a use a linux program like objdump to dump the kernel executable contents if it is not a flat binary and find the function in which this happened in. EIP should fall right inside on of the virtual addresses in the object dump for a function symbol, or tell the linker to export a map file.

I am really sure it is not a flushing problem in Bochs, but I could be wrong. I would keep a flushing problem in the back of my mind and check out the IDT table first.

Also most likely the multiple interrupt message with no dump except the last one would show that the processor is having a fault, double faulting, then triple faulting
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

It can't be an interrupt - bochs does not normally generate NMIs and interrupts are disabled.

What happens is that a null selector is used, causing a GPF, which in turn results in a triple fault for a lack of an IDT.
Intel wrote:A null segment selector can be loaded into the DS, ES, FS, or GS register, but any attempt to access a segment through one of these registers when it is loaded with a null segment selector results in a #GP exception being generated.
Why there are null selectors is probably the key event to track: I think the selectors are still loaded with their real-mode values.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
ehird
Member
Member
Posts: 214
Joined: Thu Mar 15, 2007 8:48 am

Post by ehird »

OK, time to compile bochs' debugger I see ;)

edit: info gdt generates a hundred damn pages of output.

edit: and bochs' debugger is annoying. I spent 20 minutes going through its startup then hit "enter" by mistake and made it reboot before I managed to look at what went wrong.
iammisc
Member
Member
Posts: 269
Joined: Thu Nov 09, 2006 6:23 pm

Post by iammisc »

you put mov 0x10, %eax that should be mov $0x10, %eax

the $ in gas specifies a literal value, right now you are using the value at location 0x10 of memory.
ehird
Member
Member
Posts: 214
Joined: Thu Mar 15, 2007 8:48 am

Post by ehird »

look at my updated code... i do use that
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post by mystran »

With a very large chance, there's something wrong with either the descriptors in teh GDT, or the descriptor in teh register pointing to teh GDT (whatever it's name, you load it by LGDT anyway)..
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
ehird
Member
Member
Posts: 214
Joined: Thu Mar 15, 2007 8:48 am

Post by ehird »

Part 2... I trashed my kernel and started again using nasm so I didn't have to translate. So now my code is:

Code: Select all

global gdt_flush
extern gp
gdt_flush:
	lgdt [gp]
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	jmp 0x08:flush2
flush2:
	ret
which is exactly in bkerndev except that i'm using elf, so no leading underscores.

This is gdt.c:

Code: Select all

#include <system.h>

struct gdt_entry {
	unsigned short limit_low;
	unsigned short base_low;
	unsigned short base_middle;
	unsigned char access;
	unsigned char granularity;
	unsigned char base_high;
} __attribute__((packed));

struct gdt_ptr {
	unsigned short limit;
	unsigned int base;
} __attribute__((packed));

struct gdt_entry gdt[3];
struct gdt_ptr gp;

/* code in start.asm */
extern void gdt_flush();

void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned
	char access, unsigned char gran)
{
	/* base address */
	gdt[num].base_low = (base & 0xFFFF);
	gdt[num].base_middle = (base >> 16) & 0xFF;
	gdt[num].base_high = (base >> 24 & 0xFF);
	/* limits */
	gdt[num].limit_low = (limit & 0xFFFF);
	gdt[num].granularity = ((limit >> 16) & 0x0F);
	/* granularity + access */
	gdt[num].granularity |= (gran & 0xF0);
	gdt[num].access = access;
}

void gdt_install(void)
{
	/* setup the gdt pointer + limit */
	gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
	gp.base = &gdt;

	/* NULL descriptor */
	gdt_set_gate(0, 0, 0, 0, 0);

	/* code segment: base address 0, limit 4GB, 4KB gran, 32-bit opcodes */
	gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);

	/* data segment: same as code segment, but for data */
	gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);

	/* install shiny new gdt */
	gdt_flush();
}
This is almost 1:1 what's in bkerndev, yet qemu still locks up... very odd.

I have absolutely no idea what could be going wrong...
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

you have a type error:

Code: Select all

...
struct gdt_entry gdt[3]; 
...
gp.base = &gdt;
...
the type of &gdt is (gdt_entry**)
gp.base should be the address of the gdt: i.e. of the type (gdt_entry*) (!)
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
ehird
Member
Member
Posts: 214
Joined: Thu Mar 15, 2007 8:48 am

Post by ehird »

tried changing it to = gdt, changing the type in the struct to "struct gdt_entry *", same with ** and &gdt - no luck, still freezes. The code I posted is what the tutorial had, so...
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

There can be soooooooo many things wrong that fixing one thing doesnt help.

In short, it is time for Guru Meditation. Cross-check every line of code with a debugger to see wether it does exactly what you want. Try feeding gcc -Wall -Wextra -Werror, verify all assumptions and check every dusty corner of your code. (Bugs seem to like dust :wink:)

Once you have gulped down 20 litres of coffee (or tea, if you're like me) and still can't find the problem, come back with the full source code + build instructions + binaries and we'll see what can be done.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Post Reply