I am trying to write a simple bootloader which loads my program from LBA=1 into memory and jumps to its start. I emulate my bootloader in qemu with -drive flag passed. When I try to read blocks from disk with int 13, ah=42h, I fail for some reason. What's the issue? How can I solve it? I looked up my issue in osdev forum but most of the times the members had their problem miraculously solved. Sources (compiled with gnu as) boot0.S:
.code16
.include "source/constants.S"
.global _start0
.global PRINT_STRING
.global HALT
.section .text
.global _start0
_start0:
ljmp $0, $L0 # Canonicalize (%CS):%IP.
L0:
xorw %ax, %ax
movw %ax, %ds # Load Segment Registers.
movw %ax, %es
movw %ax, %ss
movw %ax, %fs
movw %ax, %gs
movw $STACK_TOP, %sp # Prepare stack.
sti # Set interrupt flag.
movw $BOOT0SEQ_STR, %si
movb $0x07, %dh
call PRINT_STRING
INIT_ATTEMPT1:
movb $0, %ah # Reset drive controller.
int $0x13
jnc INIT_SUCCESS
INIT_ATTEMPT2:
movb $0, %ah # Try again.
int $0x13
jc FAILURE
INIT_SUCCESS:
cmpb $0x80, %dl # Check whether we boot from hard drive or floppy.
jb LBA_FAILURE_OR_FLOPPY
HARDDRIVE: # We boot from a hard-drive.
movb $0x41, %ah # Check whether we support Disk Address Packet.
movw $0x55AA, %bx
int $0x13
jc LBA_FAILURE_OR_FLOPPY
cmpw $0xAA55, %bx
jne LBA_FAILURE_OR_FLOPPY
test $1, %cx
jz LBA_FAILURE_OR_FLOPPY
LBA_SUCCESS: # We can read from a disk using Disk Address Packet.
movb $0x42, %ah
movw $DAP, %si # %ds is arleady set to zero.
int $0x13
jc FAILURE # Test whether read was successful.
jmp SUCCESS
LBA_FAILURE_OR_FLOPPY: # We boot from a floppy disk or the extension check failed.
FLOPPY_ATTEMPT1:
movw $0x0210, %ax
movw $0x0001, %cx
movb $0, %dh
movw $BOOT1_START, %bx # %es is arleady set to zero.
int $0x13
jnc FLOPPY_SUCCESS
FLOPPY_ATTEMPT2: # Try reading for a second time!
movw $0x0210, %ax
movw $0x0001, %cx
movw $BOOT1_START, %bx
int $0x13
jc FAILURE
FLOPPY_SUCCESS:
jmp SUCCESS
SUCCESS:
movw $BOOT0_SUCCESS_STR, %si
movb $0x0A, %dh # 0x0A -- Light Green.
call PRINT_STRING
jmp BOOT1_START
FAILURE: # Something went wrong!
movw $ERROR_MESSAGE, %si
movb $0x04, %dh # 0x04 -- Red.
call PRINT_STRING
jmp HALT
HALT:
cli
1: hlt
jmp 1b
PRINT_STRING: # PRINT_STRING(string : %si, color : %dh)
movb (%si), %al
inc %si
orb %al, %al
jz PRINT_RET
call PRINT_CHAR
jmp PRINT_STRING
PRINT_RET:
ret
PRINT_CHAR:
movb $0x0E, %ah
movb $0x00, %bh
movb %dh, %bl
int $0x10
ret
.balign 4
DAP: # Disk Adress Packet structure.
DAP_packet_size: .byte 0x10
DAP_zero: .byte 0x00
DAP_Nsectors: .word 0x01
DAP_buffer: .long BOOT1_START
DAP_lower_bits: .long 0x01
DAP_upper_bits: .long 0x00
.byte 0x00
BOOT0SEQ_STR: .asciz "[[ BootSeq0 ]]: "
BOOT0_SUCCESS_STR: .asciz "Succes!\n\r"
ERROR_MESSAGE: .asciz "Error: Could not find Boot1!\n\r"
.space 510 - (. - _start0)
.word 0xAA55 # Boot Signature.
constants.S:
.set BOOT1_START, 0x1000
.set BOOT1_END, 0x6000
.set STACK_TOP, 0x9000
Makefile (NOTE: I am using binutils for i686-elf target, but this should probably compile and execute properly under ordinary gnu binutils):
ARCH := i686-elf
CC := $(ARCH)-gcc
AS := $(ARCH)-as
LD := $(ARCH)-ld
BUILD_DIR := ./binaries
SRC_DIR := .
qemu: $(BUILD_DIR)/bootloader.img
qemu-system-i386 -drive format=raw,file=$< # -S -gdb tcp::1234
$(BUILD_DIR)/bootloader.img: $(BUILD_DIR)/boot0.o
$(LD) -T $(SRC_DIR)/linker.ld -format binary -o $@ $<
$(BUILD_DIR)/boot0.o: $(SRC_DIR)/boot0.S $(BUILD_DIR)
$(AS) $< -o $@
$(BUILD_DIR):
mkdir $(BUILD_DIR)
$(SRC_DIR)/boot0.S:
echo "Error: no boot0.S file found!"
clean:
rm -rf $(BUILD_DIR)
linker.ld:
ENTRY(_start0)
SECTIONS
{
. = 0x7C00;
.boot0 : {
binaries/boot0.o(.text*)
}
}
Program state before executing int13, ah=42h:
eax 0x4200 16896
ecx 0x7 7
edx 0x780 1920
ebx 0xaa55 43605
esp 0x9000 0x9000
ebp 0x0 0x0
esi 0x7c9c 31900
edi 0x0 0
eip 0x7c48 0x7c48
eflags 0x202 [ IOPL=0 IF ]
cs 0x0 0
ss 0x0 0
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
As far as I can tell, I am passing arguments correctly and my disk address packet structure is properly set up.
It is curious that you say that it fails with "AX=1, CF=1". The extended read function 42h reports an error code through the AH register. In "AX=1", AH is zero which would mean "successful completion".
If it had been AH=1 (typo perhaps?) then the error message would say "invalid function in AH or invalid parameter".
cmpb $0x80, %dl # Check whether we boot from hard drive or floppy. jl LBA_FAILURE_OR_FLOPPY
This is not correct. In a signed comparison 0x80 stands for -128 and no byte-sized value can be less than -128. Therefore this conditional jump will never be taken, thus never reaching the traditional disk read function. Either use the unsigned jb LBA_FAILURE_OR_FLOPPY, or else test DL to itself followed by jns LBA_FAILURE_OR_FLOPPY.
Please note that the LBA method through its DAP is requesting 1 sector whereas the old function requests 16 sectors (movw $0x0210, %ax)!
Something to verify:
The extended read function 42h will in case of error have set the disk address packet's block count field to the number of blocks successfully transferred, so in your case: was this field zeroed?
As Michael Petch found, your disk image will be too short (just one sector) for the disk read function (be it 42h or 02h) to read the second sector. For verification you can add the following right after .word 0xAA55 # Boot Signature.:
movw $0x0E4F, %ax # teletypes a blue 'OK'
movw $0x0001, %bx
int $0x10
movb $0x4B, %al
int $0x10
sti
hlt
jmp (. - 1)
.space 1024 - (. - _start0)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With