Функция чтения с диска не работает должным образом в загрузчике

Aug 16 2020

Я пытаюсь разработать базовый загрузчик, но столкнулся с проблемой, когда попытался создать функцию для чтения дополнительных секторов с жесткого диска. Я разрабатываю его на Kali Linux в NASM и использую QEMU в качестве эмулятора. Это мой основной файл загрузчика:

[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

Как видите, my_stringлежит после 512 байт, во втором секторе эмулируемого жесткого диска. Но когда я компилирую и запускаю загрузчик, он ничего не выводит. В коде, который я предоставил выше, я печатаю my_string после завершения read_diskфункции. Но как ни странно, если я перемещаю две строки, которые печатаются my_string внутри функции, она работает. Вот код, который работает:

[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

Буду очень признателен, если кто-нибудь сможет объяснить мне эту странную странность.

Ответы

4 MichaelPetch Aug 16 2020 at 14:38

Вы должны установить SS: SP, а не просто SP перед чтением в память. SS может быть нулевым, а может и не быть. Если SS оказывается равным 0x0000, тогда ваш стек находится в 0x0000: 0x8000, и он будет расти оттуда.

Ваш код считывает второй 512-байтовый сектор на диске в память по адресу 0x0000: 0x7e00, который включает все байты до адреса возврата disk_readфункции, которая была помещена в стек с 0x0000: 0x7ffe до 0x0000: 0x7fff, включительно.

Поскольку вы засорили стек, int 0x13вероятно, он никогда не вернется, потому что внутренние данные, адрес возврата и флаги были повреждены. Подобное повреждение стека приведет к непредсказуемым результатам. Рассмотрите возможность размещения стека по адресу 0x0000: 0x7c00 ниже загрузчика, чтобы не мешать данным и коду, которые вы загружаете после загрузчика.

Примечание: вы должны установить все сегментные регистры, которые вам нужны, на ожидаемые значения. Вы не должны полагаться ни на один из сегментных регистров, содержащих определенное значение. BIOS не гарантирует их значения, хотя в большинстве эмуляторов они будут равны 0x0000.