Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

INT 13, AH=42h fails with AH=1, CF=1

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.

like image 971
Alex Gendelbergen Avatar asked Dec 14 '25 02:12

Alex Gendelbergen


1 Answers

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)
like image 170
Sep Roland Avatar answered Dec 15 '25 22:12

Sep Roland



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!