Author Topic: GDT position/length calculation failing.  (Read 35817 times)

Offline Dulci

  • Jr. Member
  • *
  • Posts: 7
GDT position/length calculation failing.
« on: November 03, 2010, 08:13:29 PM »
Hey, so I'm trying to load my GDT, but for some reason my method of getting the gdt size/position isn't working I don't think. Here's my code:

Code: [Select]
section .data
greeting2   db 'A'
error   db '2'
load   db '3'

 
;========================================
;===================GDT==================
;========================================

BGDT:
    ; Null
    dd 0
    dd 0

    ; Code
    dw 0xFFFF           ; limit low
    dw 0                ; base low
    db 0                ; base middle
    db 10011010b            ; access
    db 11001111b            ; granularity
    db 0                ; base high

    ; Data
    dw 0xFFFF           ; limit low
    dw 0                ; base low
    db 0                ; base middle
    db 10010010b            ; access
    db 11001111b            ; granularity
    db 0                ; base high

EGDT:
 
TGDT:
    dw EGDT - BGDT - 1  ; limit (Size of GDT)
    dd BGDT
 
section .text
[BITS 16]

global ssblstart
ssblstart:
    ; Set our data segment
    cli ; Clear instructions
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ax, 0x9000  ; set stack at 0x9000-0xffff
    mov ss, ax
    mov sp, 0xFFFF
    pusha
    lgdt [TGDT]
    popa
    mov     eax, cr0
    or      eax, 1
    mov     cr0, eax
    cli
    hlt
    jmp 0x8:PmodeE
;    printr [greeting2], 12
;   mov ax, cr0
;   or ax, 1
;   mov cr0, axe
 
[BITS 32]

PmodeE:
    cli
    mov     ax, 0x10        ; set data segments to data selector (0x10)
    mov     ds, ax
    mov     ss, ax
    mov     es, ax
    mov     esp, 90000h
    cli
    hlt

When I far jump over to PmodeE it crashes (which is why I hlt before getting there). I believe it does this because for some reason [TGDT] is zero. Anyone have an idea how I can fix this?
« Last Edit: November 03, 2010, 09:04:27 PM by Keith Kanios »

Offline Keith Kanios

  • Full Member
  • **
  • Posts: 383
  • Country: us
    • Personal Homepage
Re: GDT position/length calculation failing.
« Reply #1 on: November 03, 2010, 09:19:47 PM »
Your GDT & GDTR looks right, if my memory serves me. You are CLI'ing before and performing a far jump immediately after enabling #PE, which is right.

How are you assembling and linking this code?

On a side note: Your 16-bit stack is not aligned and PUSHA/POPA before/after LGDT is not needed.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: GDT position/length calculation failing.
« Reply #2 on: November 03, 2010, 10:30:48 PM »
Code: [Select]
TGDT:
    dw EGDT - BGDT - 1  ; limit (Size of GDT)
    dd BGDT

This address for "BGDT" needs to be an absolute physical address - no segment involved! If this code is loaded at zero offset, zero segment, this will be correct - but that doesn't seem likely. I'm confused... "global" would indicate that it is linked, but the mixed 16- and 32-bit code in the same section suggests "-f bin". This looks a little bit like a bootsector... could be a "second stage", but setting initial segregs to zero would be wrong(?). If it's a bootsector, it probably needs "org 7C00h" in there, since that's where it's loaded. If it's loaded somewhere else, that address in the funny "GDT pointer" needs to be adjusted...

Code: [Select]
TGDT:
    dw EGDT - BGDT - 1  ; limit (Size of GDT)
    dd BGDT + (LOADSEG << 4)

Or something similar... Try just adding "org 7C00h" to it - that may be enough to fix it. That applies only if you're assembling this as "-f bin"! If you're using some other output format and linking it... we need more info.

Best,
Frank


Offline Dulci

  • Jr. Member
  • *
  • Posts: 7
Re: GDT position/length calculation failing.
« Reply #3 on: November 03, 2010, 10:37:14 PM »
Thanks for the help!

This is a second stage bootloader, I link it to the linker file:

Code: [Select]
/* **********************ssblinker.ld********************** */
/*
* This file is used to link the second stage bootloader.
*/

OUTPUT_ARCH(i386)
ENTRY(ssblstart)
SECTIONS
{
  .text 0x500 :
  {
    code = .; _code = .; __code = .;
    *(.text)
    . = ALIGN(4096);
  }

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

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

  end = .; _end = .; __end = .;
}
I have the bits directive because part of this is taking place in real mode. I load this into 0x500. Once again, thank you for your help!

Offline Keith Kanios

  • Full Member
  • **
  • Posts: 383
  • Country: us
    • Personal Homepage
Re: GDT position/length calculation failing.
« Reply #4 on: November 03, 2010, 11:00:42 PM »
How big (how many bytes) is your second stage boot loader after assembling and linking?

Offline Keith Kanios

  • Full Member
  • **
  • Posts: 383
  • Country: us
    • Personal Homepage
Re: GDT position/length calculation failing.
« Reply #5 on: November 03, 2010, 11:15:13 PM »
Code: [Select]
ORG 0x500

section .text
[BITS 16]

ssblstart:
    ; Set our data segment
    cli ; Clear interrupts
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov sp, ax
    mov ax, 0x9000  ; set stack at 9000:0000
    mov ss, ax
    lgdt [TGDT]
    mov     eax, cr0
    or      eax, 1
    mov     cr0, eax
    jmp 0x8:PmodeE
;    printr [greeting2], 12
;   mov ax, cr0
;   or ax, 1
;   mov cr0, axe

[BITS 32]

PmodeE:
    cli
    mov     ax, 0x10        ; set data segments to data selector (0x10)
    mov     ds, ax
    mov     ss, ax
    mov     es, ax
    mov     esp, 90000h
    hlt
    jmp $

section .data
greeting2   db 'A'
error   db '2'
load   db '3'

 
;========================================
;===================GDT==================
;========================================
align 4
BGDT:
    ; Null
    dd 0
    dd 0

    ; Code
    dw 0xFFFF           ; limit low
    dw 0                ; base low
    db 0                ; base middle
    db 10011010b            ; access
    db 11001111b            ; granularity
    db 0                ; base high

    ; Data
    dw 0xFFFF           ; limit low
    dw 0                ; base low
    db 0                ; base middle
    db 10010010b            ; access
    db 11001111b            ; granularity
    db 0                ; base high

EGDT:
 
TGDT:
    dw EGDT - BGDT - 1  ; limit (Size of GDT)
    dd BGDT

Assemble the above code with nasm -f bin -o stage2.bin stage2.asm (where stage2 is whatever your filename is) and use it as your second stage boot loader. If you continue to have problems after using the above alternative, it is an issue with the linker or how you are storing/loading the second stage boot loader.
« Last Edit: November 03, 2010, 11:21:21 PM by Keith Kanios »

Offline Dulci

  • Jr. Member
  • *
  • Posts: 7
Re: GDT position/length calculation failing.
« Reply #6 on: November 03, 2010, 11:40:32 PM »
That isn't working either. The code compiles to 90 bytes.

Offline Keith Kanios

  • Full Member
  • **
  • Posts: 383
  • Country: us
    • Personal Homepage
Re: GDT position/length calculation failing.
« Reply #7 on: November 04, 2010, 03:54:06 AM »
That isn't working either. The code compiles to 90 bytes.

Are you testing this on a real machine, or on something like Bochs?

Offline Dulci

  • Jr. Member
  • *
  • Posts: 7
Re: GDT position/length calculation failing.
« Reply #8 on: November 04, 2010, 06:36:38 AM »
I'm testing this on Quemu and Virtualbox. Quemu crashes if it reaches the jmp, where as if I use Virtualbox, I test a successful jmp by manipulating memory at 0xB8000 to display a message. In both cases, I haven't had a known successful jump. I'll try setting up Bochs tomorrow, and test with that though. I'm somewhat hesitant with testing anything on my machine yet.

Offline Keith Kanios

  • Full Member
  • **
  • Posts: 383
  • Country: us
    • Personal Homepage
Re: GDT position/length calculation failing.
« Reply #9 on: November 04, 2010, 04:47:30 PM »
The above example worked as the MBR (first sector of a floppy disk image) in Bochs, with four exceptions/changes.

1.) ORG obviously had to be changed to 0x7C00.

2.) I had to comment out the section directives, nearly forgot that the binary format isn't as simple/direct as it used to be.

3.) Had to pad and add boot signature at end of 512 bytes (0x55 0xAA).

4.) Had to remove align because it is somehow broken (for -f bin at least) in NASM 2.09.03.

Below is a modified/condensed version of your code to be used as the MBR:

Code: [Select]
ORG 0x7C00
[BITS 16]
ssblstart:
    ; Set our data segment
    cli ; Clear interrupts
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov sp, ax
    mov ax, 0x9000  ; set stack at 9000:0000
    mov ss, ax
    lgdt [TGDT]
    mov     eax, cr0
    or      eax, 1
    mov     cr0, eax
    jmp 0x8:PmodeE


[BITS 32]
PmodeE:
    cli
    mov     ax, 0x10        ; set data segments to data selector (0x10)
    mov     ds, ax
    mov     ss, ax
    mov     es, ax
    mov     esp, 90000h
.hang:
    inc DWORD[0x000B8000]
    jmp .hang

;========================================
;===================GDT==================
;========================================
TIMES 128-($-$$) DB 0
BGDT:
    ; Null
    dd 0
    dd 0

    ; Code
    dw 0xFFFF           ; limit low
    dw 0                ; base low
    db 0                ; base middle
    db 10011010b            ; access
    db 11001111b            ; granularity
    db 0                ; base high

    ; Data
    dw 0xFFFF           ; limit low
    dw 0                ; base low
    db 0                ; base middle
    db 10010010b            ; access
    db 11001111b            ; granularity
    db 0                ; base high

EGDT:
 
TGDT:
    dw EGDT - BGDT - 1  ; limit (Size of GDT)
    dd BGDT

TIMES 510-($-$$) DB 0
SIGNATURE DW 0xAA55

The TIMES 128-($-$$) DB 0 line is a work-around for the broken align, so the GDT is aligned properly.

The above code will cycle the first text-mode character/cell.

Now, if you can get the above to work as your second stage boot loader (after changing ORG back to 0x500) then it sounds like the linking process you were using is the culprit.

Otherwise, it sounds like you are probably loading the second stage boot loader correctly because the initial 16-bit code runs, but perhaps not to absolute address that is expected. This is why the code (relative) works, but the GDT (absolute) is failing.

I'm not too familiar with QEMU, but in Bochs I know you can log the runtime/registers to a file.

Try the following second stage boot loader:

Code: [Select]
ORG 0x500

;section .text
[BITS 16]

ssblstart:
    ; Set our data segment
    cli ; Clear interrupts
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov sp, ax
    mov ax, 0x9000  ; set stack at 9000:0000
    mov ss, ax
    lgdt [TGDT]
mov eax,TGDT
hlt
jmp $
    mov     eax, cr0
    or      eax, 1
    mov     cr0, eax
    jmp 0x8:PmodeE
;    printr [greeting2], 12
;   mov ax, cr0
;   or ax, 1
;   mov cr0, axe

[BITS 32]

PmodeE:
    cli
    mov     ax, 0x10        ; set data segments to data selector (0x10)
    mov     ds, ax
    mov     ss, ax
    mov     es, ax
    mov     esp, 90000h
    hlt
    jmp $

;section .data
greeting2   db 'A'
error   db '2'
load   db '3'

 
;========================================
;===================GDT==================
;========================================
TIMES 128-($-$$) DB 0
BGDT:
    ; Null
    dd 0
    dd 0

    ; Code
    dw 0xFFFF           ; limit low
    dw 0                ; base low
    db 0                ; base middle
    db 10011010b            ; access
    db 11001111b            ; granularity
    db 0                ; base high

    ; Data
    dw 0xFFFF           ; limit low
    dw 0                ; base low
    db 0                ; base middle
    db 10010010b            ; access
    db 11001111b            ; granularity
    db 0                ; base high

EGDT:
 
TGDT:
    dw EGDT - BGDT - 1  ; limit (Size of GDT)
    dd BGDT

TIMES 510-($-$$) DB 0
SIGNATURE DW 0xAA55

Assemble the above with nasm -f bin -o stage2.bin stage2.asm, run it in Bochs with logging configured, give it a few seconds, stop Bochs and look at EAX and EIP/RIP within the log file. EIP/RIP will tell you at what address your code is actually running and EAX will tell you at what address your code thinks the GDT is at. If both the EIP/RIP and EAX are not within around 128 bytes of each other, then you know for certain your first stage loading process is the culprit.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: GDT position/length calculation failing.
« Reply #10 on: November 04, 2010, 07:16:29 PM »
Alternatively, assembling Dulci's code (only slightly modified... sp=ffff just ain't right, and I put an 'X' on the screen if we make it) as "-f elf32", and linking with "-T ssblinker.ld", I get... a much larger file. Commenting out the "align"s in the linker script, I get 97 bytes (why do I have to do this?). A disassembly/dump of this "looks right". Next step, if I get to it, will be to write/modify a bootsector to load the thing at 0x500 (I don't cotton much to emulators and prefer to test on "real hardware", PITA as that is...).

I see some "lint" in the form of "greeting2", etc. May I assume that this thing has been "tested" and is actually being loaded where it's suppose to be, etc.? I guess it wouldn't be crashing, if it wasn't. :) (that reminds me - remove that "hlt" before testing...)

Best,
Frank


Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: GDT position/length calculation failing.
« Reply #11 on: November 04, 2010, 08:21:17 PM »
After a few false starts modifying a bootsector intended to do something else, "works for me". I did have to comment out the "align" lines in the linker script...

Best,
Frank


Offline Keith Kanios

  • Full Member
  • **
  • Posts: 383
  • Country: us
    • Personal Homepage
Re: GDT position/length calculation failing.
« Reply #12 on: November 04, 2010, 08:48:42 PM »
After a few false starts modifying a bootsector intended to do something else, "works for me". I did have to comment out the "align" lines in the linker script...

Yep, ALIGN is going to physically pad sections and/or virtually change the addresses... but now you took all of the fun out of training people to troubleshoot properly :(

I've seen Dulci's linker script nearly verbatim elsewhere (OS Dev related stuff) but that was geared towards ELF-linked kernels loaded by GRUB.

Dulci, unless you are using something else beside NASM to produce your second stage boot loader, get rid of that superfluous linking stage.

Offline Dulci

  • Jr. Member
  • *
  • Posts: 7
Re: GDT position/length calculation failing.
« Reply #13 on: November 04, 2010, 09:06:13 PM »
I'll be using C as well, which is why I'm linking. I did some more testing, and this is what I'm doing now. I use my first stage bootloader to load up gdt, as well as the second stage bootloader, I then jump over to the second stage bootloader, but after switching to protected mode, doing any form of jmp or nontrivial instruction causes things to go haywire.

I'll be mixing in some C  to the second stage bootloader as well (which is why I am using ld instead of just -f bin).

The value of the GDT register seems to be correct, 00007c9c 00000017 so unlike before, the GDT is no longer 0.

I've removed the align 4086 from the linker file as well, here is my code:

bootstart (bootstart isn't linked to anything):
Code: [Select]
%macro print 2
mov ah, 0xa
mov al, [%1]
mov bl, 0xf
mov bh, 0
mov cx, %2
int 0x10
%endmacro

%macro printr 2
mov al, %1
mov bl, 0xf
mov bh, 0
mov cl, %2
mov ah, 0xa
int 0x10
%endmacro

[ORG 0x7c00]
[BITS 16]

jmp 0x0000:bootstart ; jump to set up CS:IP

bootstart:
; Set our data segment
xor ax, ax
mov ds, ax
mov es, ax
mov [dlvalue], dl
cli
print greeting, 2
jmp reset
; 512 bytes per sector
; 18 sectors per track
; 63 tracks

reset:
mov ah, 0 ; reset floppy disk function
mov dl, [dlvalue]
int 0x13 ; call BIOS
jmp readCD
print reading, 3
jc reset

readCDerror:
xor ch, ch
xor cl, cl
mov dl, [dlvalue]
print error, 4
jmp reset

readCD: ; Read 0x7700 bytes from the disk to point 0x500 in memory from beginning of cd
mov dh, 0 ; Drive head
mov ax, 0x500
mov es, ax ; Set es to 0x500, where we want to buffer to
xor bx, bx ; buffer is at 500:0, es:bx
mov ch, 0 ; Set track to 0
mov cl, 2 ; sector to 2
mov ah, 0x2 ; What we want to do with 0x13
mov al, 0x2 ; How much we want to read
mov dl, [dlvalue]
clc
int 0x13
jc readCDerror ; There was a problem with reading

jumpOut:
print load, 5
; Load GDT
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ax, 0x9000 ; set stack at 0x9000-0xffff
mov ss, ax
mov sp, 0xFFFF
pusha
lgdt [TGDT]
popa

jmp 0x500:0x0 ; No problem, just jump to the entry point

data:
greeting   db 'G'
error   db 'E'
load   db 'L'
reading   db 'R'
dlvalue db 0

;========================================
;===================GDT==================
;========================================
align 4

BGDT:
; Null
dd 0
dd 0
; Code
dw 0xFFFF ; limit low
dw 0 ; base low
db 0 ; base middle
db 10011010b ; access
db 11001111b ; granularity
db 0 ; base high
; Data
dw 0xFFFF ; limit low
dw 0 ; base low
db 0 ; base middle
db 10010010b ; access
db 11001111b ; granularity
db 0 ; base high
EGDT:

TGDT:
dw EGDT - BGDT - 1 ; limit (Size of GDT)
dd BGDT

times 510-($-$$) db 0
dw 0xAA55

ssbootloader (linked to ssblinker):
Code: [Select]
;**********************ssbootloader.s**********************;
; The second stage bootloader

%macro print 2
mov ah, 0xa
mov al, [%1]
mov bl, 0xf
mov bh, 0
mov cx, %2
int 0x10
%endmacro

%macro printr 2
mov al, %1
mov bl, 0xf
mov bh, 0
mov cl, %2
mov ah, 0xa
int 0x10
%endmacro

section .data
greeting2   db 'A'
error   db '2'
load   db '3'

section .text
[BITS 16]
global ssblstart
ssblstart:
; Set our data segment
mov eax, cr0
or eax, 1
mov cr0, eax
cli
hlt
jmp 0x8:PmodeE
;    printr [greeting2], 12
; mov ax, cr0
; or ax, 1
; mov cr0, axe

[BITS 32]

PmodeE:
cli
mov ax, 0x10 ; set data segments to data selector (0x10)
mov ds, ax
mov ss, ax
mov es, ax
mov esp, 90000h
cli
hlt


ssblinker:
Code: [Select]
/* **********************ssblinker.ld********************** */
/*
* This file is used to link the second stage bootloader.
*/

OUTPUT_ARCH(i386)
ENTRY(ssblstart)
SECTIONS
{
  .text 0x500 :
  {
    code = .; _code = .; __code = .;
    *(.text)
  }

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

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

  end = .; _end = .; __end = .;
}

compilation code:
Code: [Select]
nasm bootstart.s -f bin -o $(IMGDIR)/bootstart.img
nasm ssbootloader.s -f elf -o $(OBJDIR)/ssbootloader.o
ld --oformat=binary -T $(SRC)/bootloader/ssblinker.ld -o img/ssbootloader.img objects/bootloader/ssbootloader.o

dd if=/dev/zero of=img/boot.img bs=1024 count=1440 seek=0 skip=0
dd if=img/bootstart.img of=img/boot.img seek=0 skip=0 bs=512 count=1 conv=notrunc
dd if=img/ssbootloader.img of=img/boot.img seek=1 skip=0 bs=512 count=10 conv=notrunc
mv img/boot.img bin/boot.img
sh ../../me/ciso.sh
cisco.sh just creates the iso. This builds a bootable img where it boots correctly in bootstart, and transfers to ssbootloader correctly, but after enabled bit 1 in cr0, things start messing up.
« Last Edit: November 04, 2010, 09:17:14 PM by Dulci »

Offline Keith Kanios

  • Full Member
  • **
  • Posts: 383
  • Country: us
    • Personal Homepage
Re: GDT position/length calculation failing.
« Reply #14 on: November 04, 2010, 09:36:24 PM »
0500:0000 is not the same as 0000:0500!

0000:0500 (segment:offset) = 0x00000500 (absolute)

0500:0000 (segment:offset) = 0x00005000 (absolute)

Remember, you must multiply the segment selector address by 16 and then add the offset.

You either have to change your load address to 0050:0000 (better yet, 0000:0500) or change your linker script to .text 0x5000.
« Last Edit: November 04, 2010, 09:38:02 PM by Keith Kanios »