Page 1 of 1

GDT Not Working!

Posted: Mon Mar 21, 2011 10:20 am
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?

Re: GDT Not Working!

Posted: Mon Mar 21, 2011 12:42 pm
by Casm
It looks to me as if you are loading protected mode segment selectors before switching to protected mode (by updating cr0).

Re: GDT Not Working!

Posted: Mon Mar 21, 2011 1:02 pm
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

Re: GDT Not Working!

Posted: Mon Mar 21, 2011 1:06 pm
by jfl
That didn't seem to work. I don't know if I'm doing it in the right place.

Re: GDT Not Working!

Posted: Mon Mar 21, 2011 1:15 pm
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);
}

Re: GDT Not Working!

Posted: Mon Mar 21, 2011 5:39 pm
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.

Re: GDT Not Working!

Posted: Tue Mar 22, 2011 11:58 am
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.

Re: GDT Not Working!

Posted: Tue Mar 22, 2011 4:45 pm
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).

Re: GDT Not Working!

Posted: Tue Mar 22, 2011 10:14 pm
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.

Re: GDT Not Working!

Posted: Wed Mar 23, 2011 9:39 am
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

Re: GDT Not Working!

Posted: Wed Mar 23, 2011 9:46 am
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.

Re: GDT Not Working!

Posted: Wed Mar 23, 2011 10:06 am
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!

Re: GDT Not Working!

Posted: Wed Mar 23, 2011 10:15 am
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.

Re: GDT Not Working!

Posted: Wed Mar 23, 2011 10:42 am
by jfl
Thanks! It finally works!