3

Problem

I have assembly code that switches to 32bit mode and prints a character successfully when I have it inside the boot sector - But when I use a disk read to load the code to the next sector the GDT table is misplaced even with the org directive, print fails, and the program gets into a boot loop. The disk read is successful, I tested it, and the code is exactly same in both cases except that I'm using org 0x7C00 in boot sector and 0x7E00 in 2nd sector.

I tried removing org and replacing dd gdt_start with dd gdt_start + 0x7E00, as well as hard-coding the exact address.

Project details

  • Writing x86_64 assembly
  • Running with QEMU

Working code sets GDT table to the right address containing 7C03, 3 bytes after the start. Not working code sets GDT to 000f61e0 00000037 (Not even close to right)

Stage 2 code:

BITS 16
org 0x7E00

cli

jmp code

gdt_start:
    dq 0x0000000000000000   ; Null descriptor
    dq 0x00CF9A000000FFFF   ; Code segment
    dq 0x00CF92000000FFFF   ; Data segment
gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start - 1   ; Size
    dd gdt_start


code:

lgdt [gdt_descriptor]

mov eax, cr0
or eax, 1       ; Change PE (Protection Enable) bit if 0
mov cr0, eax

jmp 0x08:protected_mode

[BITS 32]

; Registers are 16-bit and map to 32-bit addresses through GDT table

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

mov eax, 0xB8000
mov byte [eax], 'D'
mov byte [eax+1], 0x01


jmp $

And in case you want to see the disk read code from first stage:

BITS 16
org 0x7C00

jmp code

boot_drive: db 0

code:

mov byte [boot_drive], dl ;  dl is loaded at boot and has the source drive


xor ax, ax
mov ah, 0x02            ; Disk read
mov al, 3              ; Sector read count
mov ch, 0               ; Cylinder
mov cl, 2               ; Sector
mov dh, 0               ; Head
mov dl, [boot_drive]    ; Drive
mov es, ax              ; Segment
mov bx, 0x7E00          ; Offset
int 0x13                ; Disk service

jc error                ; Jmp if CF = 1


jmp 0x0000:0x7e00

error:

mov  ah, 0x0E
mov  al, 'E'
mov  bh, 00
mov  bl, 0x07
int  0x10

times 510 - ($ - $$) db 0

dw 0xAA55
10
  • 2
    Obvious problem: Your mov es, ax line uses the 0203h you wrote to ax. Recall that al and ah are halves of ax so setting mov ah, 02h overwrites a part of ax for instance. Commented Jun 8 at 19:48
  • 1
    Secondary problem: After the error handling in the first stage you immediately fall through into the times ... db 0 data. You should use an infinite loop here as well, or do something like calling int 16h function 00h (wait for keypress) then int 19h (reboot). Commented Jun 8 at 19:50
  • 1
    You should also set ds and ss:sp in the first stage, and esp in the second stage. Commented Jun 8 at 19:51
  • I fixed the 1st thing. What should I set ds and ss:sp to? Also, doesn't the jmp jmp 0x0000:0x7e00 make it so that there's no need for a hang? Commented Jun 8 at 20:23
  • @AdiMehmedičević Matching the org 7C00h, you need DS=0. For the stack you could choose SS=0 with SP=7C00h, putting it below the bootloader. Commented Jun 8 at 20:32

1 Answer 1

2

Every piece of trouble got touched on in the comments. To make sure the corrections get to their right place, I will quickly summarize it here. For more info on much the same stuff, see Segment:Offset instead of org 0x7C00 directive.

Stage 1

BITS 16
org 0x7C00

jmp code

boot_drive: db 0

code:

xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
mov [boot_drive], dl ;  dl is loaded at boot and has the source drive

mov ah, 0x02            ; Disk read
mov al, 3               ; Sector read count
mov ch, 0               ; Cylinder
mov cl, 2               ; Sector
mov dh, 0               ; Head
mov dl, [boot_drive]    ; Drive
mov bx, 0x7E00          ; Offset
int 0x13                ; Disk service
jc error                ; Jmp if CF = 1

jmp 0x0000:0x7e00

error:

mov  ah, 0x0E
mov  al, 'E'
mov  bh, 00
mov  bl, 0x07
int  0x10
cli
hlt
jmp $-2

times 510 - ($ - $$) db 0

dw 0xAA55

Stage 2

BITS 16
org 0x7E00

cli
jmp code

gdt_start:
    dq 0x0000000000000000   ; Null descriptor
    dq 0x00CF9A000000FFFF   ; Code segment
    dq 0x00CF92000000FFFF   ; Data segment
gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start - 1   ; Size
    dd gdt_start

code:

    lgdt [gdt_descriptor]
    mov  eax, cr0
    or   eax, 1       ; Change PE (Protection Enable) bit if 0
    mov  cr0, eax
    jmp  0x08:protected_mode

[BITS 32]

; Registers are 16-bit and map to 32-bit addresses through GDT table

protected_mode:
    mov  ax, 0x10
    mov  ds, ax
    mov  es, ax
    mov  fs, ax
    mov  gs, ax
    mov  ss, ax
    mov  esp, 00007C00h

    mov  eax, 000B8000h
    mov  byte [eax], 'D'
    mov  byte [eax+1], 0x01

    cli
    hlt
    jmp  $-2
Sign up to request clarification or add additional context in comments.

4 Comments

That fixed everything, thank you very much. I tried removing some of the new code to see what makes it work, but it still works, I really don't get what actually fixed the problem...
Happy to hear that it works. So that other people can see that this question got solved, I invite you to click on the accept check mark on the left side of my answer. Thank you.
Gotcha! Thanks again. :)
I figured it out. problem was that I was setting es late, when ax was already occupied. Have to do it earlier.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.