x86nasmbootloaderbootosdev

Int 0x13 failing without error on real hardware, working in Qemu


I currently have a basic bootloader setup to load my kernel with some debug statements:

; Set offsets
[org 0x7c00]
KERNEL_OFFSET equ 0x2000

[bits 16]

; Setup Stack
mov bp, 0x1000
mov sp, bp

mov bx, RM_BOOT_MSG
call print_rm
call nl_rm

; Load kernel
mov bx, KERNEL_OFFSET  ; Read from disk and store in KERNEL_OFFSET
mov dh, 10             ; Read X sectors
call load_disk

mov bx, KERNEL_LOAD_MSG
call print_rm
call nl_rm

mov bx, KERNEL_OFFSET
call print_rm_hex
call nl_rm

mov bx, [KERNEL_OFFSET]
call print_rm_hex
call nl_rm

mov bh, 0
mov bl, dl
call print_rm_hex
call nl_rm

call KERNEL_OFFSET    ; Begin entering kernel

jmp $

%include "boot/disk.asm"
%include "boot/32b_gdt.asm"

RM_BOOT_MSG: dw "16-bit RM", 0
KERNEL_LOAD_MSG: dw "Kernel Successfully Loaded", 0

; Padding
times 510 - ($ - $$) db 0
dw 0xaa55

Here is my load_disk function:

; Disk
; See this for all int 0x13 notes: https://stanislavs.org/helppc/int_13-2.html

; AH = 02
; AL = number of sectors to read    (1-128 dec.)
; CH = track/cylinder number  (0-1023 dec., see below)
; CL = sector number  (1-17 dec.)
; DH = head number  (0-15 dec.)
; DL = drive number (0=A:, 1=2nd floppy, 80h=drive 0, 81h=drive 1)
; ES:BX = pointer to buffer

; on return:
;   AH = status  (see INT 13,STATUS)
;   AL = number of sectors read
;   CF = 0 if successful
;      = 1 if error

; Note 0x01 is the boot sector, 0x02 is the first free one

[bits 16]

; Load "dh" sectors for drive "dl" at es:bx
load_disk:
    pusha
    push dx

    mov ah, 0x02

    mov al, dh

    mov ch, 0x00

    mov cl, 0x02

    mov dh, 0x00

    int 0x13
    jc .disk_error

    pop dx
    cmp al, dh
    jne .sector_error
    
    popa
    ret

    .disk_error:
        mov bx, DISK_ERR_MSG
        call print_rm

        mov dh, ah
        call print_rm_hex
        call nl_rm

        jmp .fail_loop

    .sector_error:
        mov bx, SECTOR_ERR_MSG
        call print_rm
        call nl_rm

    .fail_loop:
        jmp $

DISK_ERR_MSG: dw "Error loading from disk: ", 0
SECTOR_ERR_MSG: dw "Incorrect number of sectors read", 0

%include "boot/print_rm.asm"

When run via qemu-system-i386 -s -usb -drive format=raw,file=\\.\PHYSICALDRIVE2, it works as expected with no errors, specifically outputting

Kernel Successfully Loaded
0x2000 (the Kernel offset)
0xC6BB (what is stored at 0x2000)
0x0080 (the drive)

On hardware, it instead outputs:

Kernel Successfully Loaded
0x2000 (the Kernel offset)
0x519B (what is stored at 0x2000)
0x0080 (the drive)

I.e. it fails to load the correct data, causing call KERNEL_OFFSET to fail (it may be noted that when call load_disk is commented out, it still prints 0x519B as the data stored at 0x2000).

Any suggestions on how to resolve this difference between QEMU and hardware would be greatly appreciated.


Solution

  • As noted by Michael Petch, this is resolved by ensuring relevant segment registers are set to 0

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