GDT Not Working!

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
jfl
Posts: 9
Joined: Thu Dec 23, 2010 12:34 pm

GDT Not Working!

Post by jfl »

I have decided to write an OS. So far I have set up the screen and booted from GRUB. But as soon as it sets up the GDT it stops being able to display anything on the screen. Here is the code for the GDT and my linker script:

link.ld:

Code: Select all

ENTRY(start)
SECTIONS
{
  .text 0x100000 :
  {
    code = .; _code = .; __code = .;
    *(.text)
    . = ALIGN(4096);
  }

  .data :
  {
     data = .; _data = .; __data = .;
     *(.data)
     *(.rodata)
     . = ALIGN(4096);
  }

  .bss :
  {
    bss = .; _bss = .; __bss = .;
    *(.bss)
    . = ALIGN(4096);
  }

  end = .; _end = .; __end = .;
}
gdt.s:

Code: Select all

section .data

gdt:
null:
dq 0

code:
dw 0xFFFF
dw 0

db 0
db 10011010b
db 01001111b
db 0

data:
dw 0xFFFF
dw 0

db 0
db 10010010b
db 01001111b
db 0

gdt_end

gdt_desc:
dq gdt_end - gdt
dd gdt


section .text
global gdt_setup

gdt_setup:

xor ax,ax
mov ds,ax

lgdt [gdt_desc]

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

jmp 0x08:flush
flush:
ret
I don't know what is wrong with it. Does anybody know how to fix it?
Casm
Member
Member
Posts: 221
Joined: Sun Oct 17, 2010 2:21 pm
Location: United Kingdom

Re: GDT Not Working!

Post by Casm »

It looks to me as if you are loading protected mode segment selectors before switching to protected mode (by updating cr0).
jfl
Posts: 9
Joined: Thu Dec 23, 2010 12:34 pm

Re: GDT Not Working!

Post by jfl »

Casm wrote:It looks to me as if you are loading protected mode segment selectors before switching to protected mode (by updating cr0).
So before I run the lgdt instruction I have to do this?:

Code: Select all

mov eax, cr0
or eax, 1
mov cr0, eax
jfl
Posts: 9
Joined: Thu Dec 23, 2010 12:34 pm

Re: GDT Not Working!

Post by jfl »

That didn't seem to work. I don't know if I'm doing it in the right place.
jfl
Posts: 9
Joined: Thu Dec 23, 2010 12:34 pm

Re: GDT Not Working!

Post by jfl »

I've changed the code around a bit and it now looks like this:

Code: Select all

bits 32

section .data

gdt:
null:
dq 0

code:
dw 0xFFFF
dw 0

db 0
db 10011010b
db 01001111b
db 0

data:
dw 0xFFFF
dw 0

db 0
db 10010010b
db 01001111b
db 0

gdt_end

gdt_desc:
dq gdt_end - gdt
dd gdt


section .text
global gdt_setup

gdt_setup:

cli

xor ax,ax
mov ds,ax

lgdt [gdt_desc]

mov eax, cr0
or eax, 1
mov cr0, eax

jmp 0x08:.flush
.flush:
mov ax,0x10
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
ret
And in case it is useful here is main.c:

Code: Select all

#include <system.h>
#include <screen.h>

extern void gdt_setup();

void setup();

void main()
{
  setup();
  
  puts("The GDT has been set up!");
}

void setup()
{
  gdt_setup();
  clear(0, 0x0F);
}
User avatar
DavidCooper
Member
Member
Posts: 1150
Joined: Wed Oct 27, 2010 4:53 pm
Location: Scotland

Re: GDT Not Working!

Post by DavidCooper »

db 01001111b
Try changing the two occurrances of this to 11001111b

I'm can't remember what bit-7 of it does, but that's the value I use.
Help the people of Laos by liking - https://www.facebook.com/TheSBInitiative/?ref=py_c

MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
jfl
Posts: 9
Joined: Thu Dec 23, 2010 12:34 pm

Re: GDT Not Working!

Post by jfl »

DavidCooper wrote:
db 01001111b
Try changing the two occurrances of this to 11001111b

I'm can't remember what bit-7 of it does, but that's the value I use.
That bit is the granularity. 0 means byte granularity and 1 means page granularity. My limit uses byte granularity.
User avatar
DavidCooper
Member
Member
Posts: 1150
Joined: Wed Oct 27, 2010 4:53 pm
Location: Scotland

Re: GDT Not Working!

Post by DavidCooper »

jfl wrote:
DavidCooper wrote:
db 01001111b
Try changing the two occurrances of this to 11001111b

I'm can't remember what bit-7 of it does, but that's the value I use.
That bit is the granularity. 0 means byte granularity and 1 means page granularity. My limit uses byte granularity.
Yes, I've checked that - you're limiting yourself to a 1MB range instead of 4GB, but while that's unusual it shouldn't stop you accessing the screen.

Code: Select all

jmp 0x08:.flush
.flush:
mov ax,0x10
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
ret
You haven't shown how you're trying to print to the screen, but to narrow down where the problem is, why not try adding a bit of code just before the ret in the bit above to print to the screen from there. I work directly in machine code directly using decimals, so I'd put 120 in al, 12 in ah, 0,128,11,0 in edi and post ax to the screen with 102,171 (thereby printing a red capital X to the top left corner of the screen). In assembler that would be something like:-

Code: Select all

mov al,0x78
mov ah,0x0C
mov edi,000B8000h // check that value - I'm not great with hex
stosw // check that too - I'm trying to represent the instruction that sends
          // ax to the address in edi
My own code to jump switch from real mode to 32-bit protected mode is as follows, but I'm only giving it to you so you can see if your own code is doing something radically different in case it helps: 250 (disable interrupts), 51 192 (ax=0), 142 208 (ss=0), 188 0 124 (sp = address where boot sector is loaded in, so stack will run downwards from there), 176 209 230 100 (send 209 to port 100), 176 223 230 96 (and 223 to port 96 - A20 now on to open up whole memory range of machine), 190 196 124 (si = address of lgdt pointer to GDT - see below), 15 1 20 (load GDT), 15 32 192 (mov eax, cr0), 12 1 (or ah, 1), 15 34 192 (mov cr0, eax - switch to protected mode made), 102 234 172 125 0 0 8 0 (jump to next byte to load cs).

The thing I called the lgdt pointer is a block of 8 bytes, the first two of which in my case are 39, 0 (which is 5 times 8 minus 1 because I have five entries in my GDT - as you only have three entries you should make these 23, 0 instead), then the two-byte address of the start of your GDT, then four zero bytes (though I can't remember if segment values are part of that - my GDT's in the bottom 64KB of memory so they would be zero anyway).
Help the people of Laos by liking - https://www.facebook.com/TheSBInitiative/?ref=py_c

MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
User avatar
Chandra
Member
Member
Posts: 487
Joined: Sat Jul 17, 2010 12:45 am

Re: GDT Not Working!

Post by Chandra »

Here's a help from my side with modification to your code:

Code: Select all

bits 32

section .data

gdt:
null:
dq 0

code:
dw 0xFFFF
dw 0

db 0
db 10011010b
db 01001111b
db 0

data:
dw 0xFFFF
dw 0

db 0
db 10010010b
db 01001111b
db 0

gdt_end

gdt_desc:
dq gdt_end - gdt    ; Replace this with: dw gdt_end - gdt - 1
dd gdt


section .text
global gdt_setup

gdt_setup:

cli

xor ax,ax
mov ds,ax

lgdt [gdt_desc]

mov eax, cr0
or eax, 1
mov cr0, eax

jmp 0x08:.flush
.flush:
mov ax,0x10
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
ret
Good to help!
Please look at the Intel's manual for more info about GDTR register.
Programming is not about using a language to solve a problem, it's about using logic to find a solution !
jfl
Posts: 9
Joined: Thu Dec 23, 2010 12:34 pm

Re: GDT Not Working!

Post by jfl »

DavidCooper wrote:
jfl wrote:
DavidCooper wrote: Try changing the two occurrances of this to 11001111b

I'm can't remember what bit-7 of it does, but that's the value I use.
That bit is the granularity. 0 means byte granularity and 1 means page granularity. My limit uses byte granularity.
Yes, I've checked that - you're limiting yourself to a 1MB range instead of 4GB, but while that's unusual it shouldn't stop you accessing the screen.

Code: Select all

jmp 0x08:.flush
.flush:
mov ax,0x10
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
ret
You haven't shown how you're trying to print to the screen, but to narrow down where the problem is, why not try adding a bit of code just before the ret in the bit above to print to the screen from there. I work directly in machine code directly using decimals, so I'd put 120 in al, 12 in ah, 0,128,11,0 in edi and post ax to the screen with 102,171 (thereby printing a red capital X to the top left corner of the screen). In assembler that would be something like:-

Code: Select all

mov al,0x78
mov ah,0x0C
mov edi,000B8000h // check that value - I'm not great with hex
stosw // check that too - I'm trying to represent the instruction that sends
          // ax to the address in edi
My own code to jump switch from real mode to 32-bit protected mode is as follows, but I'm only giving it to you so you can see if your own code is doing something radically different in case it helps: 250 (disable interrupts), 51 192 (ax=0), 142 208 (ss=0), 188 0 124 (sp = address where boot sector is loaded in, so stack will run downwards from there), 176 209 230 100 (send 209 to port 100), 176 223 230 96 (and 223 to port 96 - A20 now on to open up whole memory range of machine), 190 196 124 (si = address of lgdt pointer to GDT - see below), 15 1 20 (load GDT), 15 32 192 (mov eax, cr0), 12 1 (or ah, 1), 15 34 192 (mov cr0, eax - switch to protected mode made), 102 234 172 125 0 0 8 0 (jump to next byte to load cs).

The thing I called the lgdt pointer is a block of 8 bytes, the first two of which in my case are 39, 0 (which is 5 times 8 minus 1 because I have five entries in my GDT - as you only have three entries you should make these 23, 0 instead), then the two-byte address of the start of your GDT, then four zero bytes (though I can't remember if segment values are part of that - my GDT's in the bottom 64KB of memory so they would be zero anyway).
I used this just before the ret statement and it didn't work:

Code: Select all

mov [0xb8000],dword 15 << 7 | 0x78
jfl
Posts: 9
Joined: Thu Dec 23, 2010 12:34 pm

Re: GDT Not Working!

Post by jfl »

Chandra wrote:Here's a help from my side with modification to your code:

Code: Select all

bits 32

section .data

gdt:
null:
dq 0

code:
dw 0xFFFF
dw 0

db 0
db 10011010b
db 01001111b
db 0

data:
dw 0xFFFF
dw 0

db 0
db 10010010b
db 01001111b
db 0

gdt_end

gdt_desc:
dq gdt_end - gdt    ; Replace this with: dw gdt_end - gdt - 1
dd gdt


section .text
global gdt_setup

gdt_setup:

cli

xor ax,ax
mov ds,ax

lgdt [gdt_desc]

mov eax, cr0
or eax, 1
mov cr0, eax

jmp 0x08:.flush
.flush:
mov ax,0x10
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
ret
Good to help!
Please look at the Intel's manual for more info about GDTR register.
I tried the change you said and it still didn't work.

I have attached a screenshot. It should be cleared and display a message but that doesn't happen.

Here is my monitor code written in c:
screen.h:

Code: Select all

#ifndef SCREEN_H
#define SCREEN_H

void clear(unsigned char background, unsigned char foreground);
void putch(const char c);
void puts(const char *s);

#endif
screen.c:

Code: Select all

#include <screen.h>
#include <system.h>

unsigned short *vmem = (short *) 0xB8000;

short colours;

unsigned int x = 0, y = 0;

static void cursor()
{
  unsigned short tmp = y * 80 + x;
  
  outb(0x3D4, 14);
  outb(0x3D5, tmp >> 8);
  outb(0x3D4, 15);
  outb(0x3D5, tmp);
}

static void scroll()
{
  unsigned short space = (colours << 8) | 0x20;
  unsigned short tmp;
  
  if(y >= 25)
  {
    tmp = y - 24;
    memcpy((char *) vmem, (char *) vmem + tmp * 80, 160 * (25 - tmp));
    
    memsetw (vmem + (25 - tmp) * 80, space, 80);
  }
}

void clear(unsigned char background, unsigned char foreground)
{
  colours = ((background << 4) | foreground) << 8;
  short space = colours | 0x20;
  
  memsetw(vmem, space, 1920);
  
  x = y = 0;
  
  cursor();
}

void putch(const char c)
{
   if(c == 0x7) /* write code to make a noise!!!!!!!*/;
   else if(c == 0x8)
   {
     if(x != 0 && y != 0)
     {
       if(x = 0)
       {
	 y--;
	 x = 80;
       }
       else x--;
     }
   }
   else if(c == 0x9) x = (x+8) & ~(8-1);
   else if(c == 0xD) x = 0;
   else if(c > 0x1F && c < 0x7F)
   {
     vmem[y * 80 + x] = colours | c;
     x++;
   }
   
   if (x >= 80)
   {
       x = 0;
       y++;
   }
   scroll();
   cursor();
}
void puts(const char *s)
{
  int i = 0;
  while (s[i])
  {
    putch(s[i++]);
  }
}
I use GRUB Legacy for booting.
Attachments
snapshot1.png
User avatar
Chandra
Member
Member
Posts: 487
Joined: Sat Jul 17, 2010 12:45 am

Re: GDT Not Working!

Post by Chandra »

jfl wrote:I tried the change you said and it still didn't work.
One thing I'm sure is that, you didn't check the Intel's manual, did you?

I just made a small modification so that you could figure out the rest. But you seem to be too noob for that.
Okay, this time some more modifications:

Code: Select all

gdt_end
which should be actually declared as a label like this,

Code: Select all

gdt_end:
Oh come on, should I make whole lots of changes?
Replace you entire descriptor table code with this:

Code: Select all

gdt:                    	; Address for the GDT

gdt_null:               	; Null Segment
        dd 0
        dd 0

gdt_code:               	; Code segment, read/execute, nonconforming
        dw 0ffffh			; Limit 4 GB
        dw 0			; Base 0
        db 0
        db 10011010b
        db 11001111b
        db 0

gdt_data:               	; Data segment, read/write, expand down
        dw 0ffffh			; Limit 4 GB
        dw 0			; Base 0
        db 0
        db 10010010b
        db 11001111b
        db 0


gdt_end:                	; Mark as the end label, will be used to calculate the size of GDT



gdt_desc:                       	; The GDT descriptor
        dw gdt_end - gdt - 1    	; Limit (size)
        dd gdt                  	; Address of the GDT
Before anyone accuses me with the sin of spoon-feeding, I'm out of here.
Good to help again!
Programming is not about using a language to solve a problem, it's about using logic to find a solution !
User avatar
turdus
Member
Member
Posts: 496
Joined: Tue Feb 08, 2011 1:58 pm

Re: GDT Not Working!

Post by turdus »

You have several mistakes in your code:
1. you do not specify "bit 16", so the code before the jump will be compiled for protected mode and you execute it in real mode.
2. if you call gdt_setup from real mode, the stack will contain a real mode return address. When you make a ret in protmode, it interpreted as linear address, and that's obviously won't work.

Code: Select all

bit 16
gdt_setup:
...
jmp 0x08:.flush
bit 32
.flush:
and to get the correct return address, you should know it's called with a near or a far call. You should do something like

Code: Select all

gdt_setup:
xor ebp, ebp
pop bp
and instead of a ret

Code: Select all

jmp ebp
If it was a far call, the stack contains to words (each 16 bits): segment and offset, so you have to calculate seg<<4+offs, and jump there.
jfl
Posts: 9
Joined: Thu Dec 23, 2010 12:34 pm

Re: GDT Not Working!

Post by jfl »

Thanks! It finally works!
Post Reply