Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disk Read Function not Working as Expected in Bootloader

I'm trying to develop a basic bootloader, but I ran into a problem when I tried to create a function for reading additional sectors from a hard drive. I'm developing it on Kali Linux in NASM and using QEMU as my emulator. This is my main bootloader file:

[org 0x7c00]

mov bp, 0x8000
mov sp, bp

call read_disk

mov si, my_string
call print          ;prints a string, si points to the string to be printed

jmp $

read_disk
    mov ah, 0x02    ;read from disk
    mov al, 0x01    ;read one sector
    mov ch, 0x00    ;read from cylinder 0
    mov dh, 0x00    ;read from head 0
    mov cl, 0x02    ;read the second sector

    mov bx, 0
    mov es, bx
    mov bx, 0x7c00+512

    int 0x13

    jc disk_error   ;BIOS sets the carry flag if disk read was unsuccessful

    ret

disk_error:
    mov si, error_msg
    call print
    jmp $
;
;Functions
;
%include "functions/print.asm"
%include "functions/print_hex.asm"
%include "functions/print_nl.asm"
%include "functions/calc_len.asm"
%include "functions/find_string.asm"

;
;Data
;
error_msg:
    db 'Error reading disk', 0

times 510-($-$$) db 0    ;pad out the rest of the bootloader with zeros to increase the size to 512 bytes
dw 0xaa55                ;Magic bytes so BIOS recognizes the hard drive as bootable

;
;SECOND SECTOR
;

my_string:
    db 'Disk read successful', 0

times 512 db 0   ;need to pad out the rest of the sector with zeros since  QEMU requires it

As you can see, my_string lies after 512 bytes, in the second sector of the emulated hard drive. But when I compile and run the bootloader, it doesn't output anything. In the code I provided above, I'm printing my_string after the read_disk function is over. But strangely enough, If I move the two lines that print my_string inside the function, it works. This is the code that works:

[org 0x7c00]

mov bp, 0x8000
mov sp, bp

call read_disk

jmp $

read_disk
    mov ah, 0x02    ;read from disk
    mov al, 0x01    ;read one sector
    mov ch, 0x00    ;read from cylinder 0
    mov dh, 0x00    ;read from head 0
    mov cl, 0x02    ;read the second sector

    mov bx, 0
    mov es, bx
    mov bx, 0x7c00+512

    int 0x13

    jc disk_error   ;BIOS sets the carry flag if disk read was unsuccessful

    mov si, my_string
    call print          ;prints a string, si points to the string to be printed

    ret

disk_error:
    mov si, error_msg
    call print
    jmp $
;
;Functions
;
%include "functions/print.asm"
%include "functions/print_hex.asm"
%include "functions/print_nl.asm"
%include "functions/calc_len.asm"
%include "functions/find_string.asm"

;
;Data
;
error_msg:
    db 'Error reading disk', 0

times 510-($-$$) db 0    ;pad out the rest of the bootloader with zeros to increase the size to 512 bytes
dw 0xaa55                ;Magic bytes so BIOS recognizes the hard drive as bootable

;
;SECOND SECTOR
;

my_string:
    db 'Disk read successful', 0

times 512 db 0   ;need to pad out the rest of the sector with zeros since  QEMU requires it

I'd be very grateful if someone can explain this strange oddity to me.

like image 203
0xPictor Avatar asked Apr 29 '26 10:04

0xPictor


1 Answers

You should set SS:SP and not just as SP before reading into memory. SS may be zero or it may not be. If SS happens to be 0x0000 then your stack is at 0x0000:0x8000 and it will grow down from there.

Your code reads the second 512 byte sector on the disk to memory at 0x0000:0x7e00 which includes all the bytes up to and including the return address of the disk_read function that was placed on the stack at 0x0000:0x7ffe to 0x0000:0x7fff.

Since you clobbered the stack, the int 0x13 will likely never return because the internal data, return address and the flags have been corrupted. Corrupting the stack like this will have unpredictable results. Consider putting the stack at 0x0000:0x7c00 below the bootloader so as to not interfere with the data and code you load after the bootloader.

Note: You should set all the segment registers you need to values you expect. You shouldn't rely on any of the segment registers containing a specific value. The BIOS doesn't guarantee their values, although in most emulators they will be 0x0000.

like image 137
Michael Petch Avatar answered May 01 '26 00:05

Michael Petch