0

I am using assembly to make a BIOS bootloader for my OS. I use int 13h/ah=42h to read the disk by LBA, but it always throws an error.

Why is this error thrown?

My code :

; Bootloader stage 1

[BITS 16]
[ORG 0x0]

IMGADDR     equ 0x60

[SECTION .text]

jmp _START
nop

bsOemName :                     DB      'OS      '      ; 0x03
bpbBytesPerSector :             DW      '__'            ; 0x0B
bpbSectorsPerCluster :          DB      'e'             ; 0x0D
bpbReservedSectors :            DW      'xt'            ; 0x0E
bpbNumberOfFATs :               DB      '~'             ; 0x10
bpbRootEntries :                DW      0x00            ; 0x11
bpbTotalSectors :               DW      0x00            ; 0x13
bpbMedia :                      DB      0x00            ; 0x15
bpbSectorsPerFAT :              DW      0x00            ; 0x16
bpbSectorsPerTrack :            DW      0x00            ; 0x18
bpbHeadsPerCylinder :           DW      0x00            ; 0x1A
bpbHiddenSectors :              DD      0x00            ; 0x1C
bpbTotalSectorsBig :            DD      0x00            ; 0x20
 
bsSectorsPerFAT32 :             DD      0x00            ; 0x24
bsExtendedFlags :               DW      0x00            ; 0x28
bsFSVersion :                   DW      0x00            ; 0x2A
bsRootDirectoryClusterNo :      DD      0x00            ; 0x2C
bsFSInfoSectorNo :              DW      0x00            ; 0x30
bsBackupBootSectorNo :          DW      0x00            ; 0x32
bsreserved :           times 12 DB      0x00            ; 0x34
bsDriveNumber :                 DB      0x00            ; 0x40
bsreserved1 :                   DB      0x00            ; 0x41
bsExtendedBootSignature :       DB      0x00            ; 0x42
bsVolumeSerialNumber :          DD      0x00            ; 0x43
bsVolumeLabel :                 DB      '           '   ; 0x47
bsFileSystemName :              DB      '       ~'      ; 0x52

_START:
    cld
    
    mov ax, 0x9000
    mov es, ax
    
    sub ax, 2048 / 16
    mov ss, ax
    mov sp, 2048
    
    ; Copy boot sector to top
    mov cx, 256
    mov si, 0x7C00
    xor di, di
    mov ds, di
    rep movsw
    
    jmp 0x9000:_ENTRY

_ENTRY:
    push cs
    pop ds
    
    mov [ bsDriveNumber ], dl
    
    and byte [ bsRootDirectoryClusterNo + 3 ], 0x0F
    mov esi, [ bsRootDirectoryClusterNo ]
    
ReadRootDir:
    push byte IMGADDR
    pop es
    xor bx, bx
    call ReadCluster
    push esi
    pushf
    
    push byte IMGADDR
    pop es
    xor di, di
    mov si, ProgName
    
    mov al, [ bpbSectorsPerCluster ]
    cbw
    mul word [ bpbBytesPerSector ]
    shr ax, 5
    mov dx, ax
    
FindName:
    mov cx, 11

.FNAMELOOP:
    cmp byte [es : di], ch
    je .FNAMEERR
    
    pusha
    repe cmpsb
    popa
    je .FNAMEFOUND
    
    add di, 32
    dec dx
    jnz .FNAMELOOP
    popf
    pop esi
    
    jc ReadRootDir
    jmp .FNAMEERR

.FNAMEFOUND:
    push word [ es : di + 0x14 ]
    push word [ es : di + 0x1A ]
    
    pop esi

    push byte IMGADDR
    pop es
    xor bx, bx
    jmp ReadLoader

.FNAMEERR:
    mov byte [ ErrorChar ], 'N'
    jmp ShowError

ReadLoader:
    call ReadCluster
    jc ReadLoader
    
    push byte IMGADDR
    pop ds
    mov ax, ds

    sub ax, 0x10
    mov es, ax
    mov ds, ax
    mov ss, ax
    xor sp, sp
    push es
    push word 0x100
    jmp short RunBinary

RunBinary:
    mov dl, [ cs : bsDriveNumber ]
    
    mov si, 16384 - 83

    jmp 0x0060:0x0000

ReadCluster:
    mov ax, [ bpbBytesPerSector ]
    shr ax, 2
    cwde
    
    mov ebp, esi
    xchg eax, esi
    cdq
    
    div esi
    movzx edi, word [ bpbReservedSectors ]
    add edi, [ bpbHiddenSectors ]
    add eax, edi
    
    push dx
    mov cx, 1
    call ReadSectorLBA
    
    pop si
    add si, si
    add si, si
    and byte [ es : si + 3 ], 0x0F
    mov esi, [ es : si ]
    
    lea eax, [ ebp - 2 ]
    movzx ecx, byte [ bpbSectorsPerCluster ]
    mul ecx
    mov ebp, eax
    
    movzx eax, byte [ bpbNumberOfFATs ]
    mul dword [ bsSectorsPerFAT32 ]
    
    add eax, ebp
    add eax, edi
    
    call ReadSectorLBA
    
    mov ax, [ bpbBytesPerSector ]
    shr ax, 4
    mul cx
    
    mov cx, es
    add cx, ax
    mov es, cx
    cmp esi, 0x0FFFFFF8
    ret

ReadSectorLBA:
    pushad
    
.RSLBALOOP:
    pusha
    
    mov byte [ dapSize ] , 0x10
    mov word [ dapReadSector ] , 0x0001
    mov word [ dapDestOffset ] , bx
    mov word [ dapDestSegment ] , es
    mov dword [ dapReadStart ], eax
    
    mov si, DAP
    mov dl, [ bsDriveNumber ]
    mov ax, 0x9000
    mov ds, ax
    mov ah, 0x42
    int 0x13
    push cs
    pop ds
    
    jc short .RSLBAERR
    
    popa
    dec cx
    jz .RSLBAFIN
    
    add bx, [ bpbBytesPerSector]
    add eax, byte 1
    jmp short .RSLBALOOP

.RSLBAFIN:
    popad
    ret

.RSLBAERR:
    mov byte [ ErrorChar ], 'D'
    jmp ShowError

ShowError:
    mov ah, 0x0E
    mov al, [ ErrorChar ]
    mov bx, 7
    int 10h
    jmp $

ProgName:   db "STARTUP BIN"
ErrorChar : db ' '

DAP:
dapSize :              db 0x10;
dapReserved :          db 0x00;
dapReadSector :        dw 0x01;
dapDestOffset :        dw 0x00;
dapDestSegment :       dw 0x00;
dapReadStart :         dq 0x00;

times 510 - ( $ - $$ ) db 0
dw 0xAA55

This is the GDB register info and the DAP (Disk Address Packet) memory dump:

eax            0xe44               3652
ecx            0x1                 1
edx            0x80                128
ebx            0x7                 7
esp            0x7ca               0x7ca
ebp            0x2                 0x2
esi            0x1d3               467
edi            0x20                32
eip            0x1c5               0x1c5
eflags         0x203               [ IOPL=0 IF CF ]
cs             0x9000              36864
ss             0x8f80              36736
ds             0x9000              36864
es             0x60                96
fs             0x0                 0
gs             0x0                 0
fs_base        0x0                 0
gs_base        0x0                 0
k_gs_base      0x0                 0
cr0            0x10                [ ET ]
cr2            0x0                 0
cr3            0x0                 [ PDBR=0 PCID=0 ]
cr4            0x0                 [ ]
cr8            0x0                 0
efer           0x0                 [ ]

0x901d3:        0x10    0x00    0x01    0x00    0x00    0x00    0x60    0x00
0x901db:        0x20    0x00    0x00    0x00    0x00    0x00    0x00    0x00

I think my code generates the correct DAP and passes the correct address of the DAP. What could be the cause of this problem in my code?

[edit]

In response to Michael Petch's comment, I have already placed mov ah, 0x42 int 0x13 after mov ax, 0x9000 mov ds, ax.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
권민수
  • 31
  • 1
  • 5
  • You set AH=0x42 and then clobber AX right after with `mov ax, 0x9000` `mov ds, ax` before doing `int 0x13` – Michael Petch Dec 12 '20 at 14:41
  • @MichaelPetch turn your comment into an answer. That's exactly what it's happening – mcleod_ideafix Dec 12 '20 at 14:47
  • @MichaelPetch I fixed, but now it jumps to 0x9D443 while executing int 0x13 bios call, and does nothing. – 권민수 Dec 12 '20 at 14:51
  • 1
    @권민수 Please post your updated code. – fuz Dec 12 '20 at 14:56
  • @mcleod_ideafix: That wouldn't be worth posting as an answer. Instead we'd mark it as a duplicate of [How do AX, AH, AL map onto EAX?](https://stackoverflow.com/q/15191178), but I'm going to hold off on that since the OP reports a different problem after fixing that. Single-stepping in a debugger like the one built-in to BOCHS would probably help spot whatever else is wrong. – Peter Cordes Dec 12 '20 at 15:09
  • @권민수 - no you didn't, you just added a line of text separate from the old code. Also, the question text still describes the old problem. So it's not a [mcve] of the new possibly-less-trivial problem. Single-step in BOCHS to see what's happening. (GDB doesn't know about segmentation, so remote attaching GDB to qemu works less well.) – Peter Cordes Dec 12 '20 at 15:10
  • @PeterCordes Thank you for your advice. It copied data properly to memory and ran it but the second stage code was wrong and jumped wrong address. – 권민수 Dec 13 '20 at 02:05
  • [edit] to make the question a [mcve], even if comments get deleted by a moderator. – Peter Cordes Dec 13 '20 at 02:07
  • After reading the comments I am closing the question as a typo (well not exactly a typo but I think an unintended mistake) or not really useful to future readers. The follow up error identified was for the next stage that we couldn't see so that problem is quite different than the one for the original question. – Michael Petch Dec 13 '20 at 22:13

1 Answers1

3

The calculation for the FAT32 data sector number is wrong.

Following is the calculation to convert a data cluster number N into a sector number (1st sector in that cluster):

bpbHiddenSectors + bpbReservedSectors + bpbNumberOfFATs * bsSectorsPerFAT32 + (N - 2) * bpbSectorsPerCluster

Thus we code:

; IN (es:bx is DestinationBuffer, esi is ClusterNumber)
ReadCluster:
    lea   edi, [esi-2]
    mov   eax, byte [ bpbSectorsPerCluster ]
    imul  edi, eax

    movzx eax, byte [ bpbNumberOfFATs ]
    imul  eax, [ bsSectorsPerFAT32 ]
    add   edi, eax

    movzx eax, word [ bpbReservedSectors ]
    add   eax, edi
    add   eax, [ bpbHiddenSectors ]
        
    push  dx
    mov   cx, 1              ; Read 1x one sector
    call  ReadSectorLBA
    ...

mov dl, [ bsDriveNumber ]   <<< already requires DS=0x9000
mov ax, 0x9000              <<< redundant
mov ds, ax                  <<< redundant
mov ah, 0x42                ; BIOS.ExtendedRead

In ReadSectorLBA, you can safely remove setting up DS because, if DS wasn't already equal to 0x9000 at that point, none of the memory accesses would have been correct.


Lastly but maybe too obvious:

  • Obvious n°1, are the IBM/MS INT 13 Extensions available to you ?
  • Obvious n°2, who fills the bpb and bs fields ?
  • Obvious n°3, is the drive number in the range 80h-FFh ?
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • 2
    In the question's register output EDX is 0x80 so I assume that they probably ran QEMU booting from a hard disk image (unless they did something very nonstandard) and QEMU supports Int 13h/ah-42h on hard disk images. It is a good question about what fills in the values in the floppy, although if it had been me I probably would have used `mtools` `mformat` command to do a format but use on of the options that doesn't involve destroying the entire VBR. – Michael Petch Dec 12 '20 at 22:13