assemblyx86x86-16bootloaderosdev

Why does working Assembly code not work when loaded into different memory by disk read?


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

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

Solution

  • 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