user15511921
user15511921

Reputation: 21

My bootloader does not correctly loads full files (FAT16)

I've been coding a small OS for a while and I've always tested it using VMs, specifically it works on QEMU, VMWare, and VirtualBox. The kernel expects to be booted from an HDD so to test it on real hardware I burned it to a real HDD and connected it to a real pc. From there it was a disaster! Anything was working. I've partially fixed all the errors that I could find but I really don't know what causes this: the VBR, after being loaded by the MBR, achieve to load only a part of the second stage bootloader; In BOCHS it loads only the first cluster but in the real pc even less code is loaded in memory. All the code that handles FAT16 is in a single file but it still too large to be posted here so I'll provide its link on GitHub. This file (/boot/include/fat16.inc) is included by the main VBR file (/boot/src/vbr.asm). Some initialization may miss in vbr.asm but they are treated in the MBR file (/boot/src/mbr.asm)'cause of space limitation.

EDIT:

If for some reasons the GitHub repo gets lost I'm posting the code of fat16.inc for future readers:

%ifndef __FAT12_INC__
%define __FAT12_INC__

; this file relies on a word variable called partition_offset
; partition_offset should contain the address in memory of
; the current partition entry in the table

bits 16

%define ROOT_OFFSET Mem.Loader.FAT16.Data
%define ROOT_SEG LIN_TO_FAR_ADDR(ROOT_OFFSET)

%define FAT_OFFSET Mem.Loader.FAT16.Data
%define FAT_SEG LIN_TO_FAR_ADDR(FAT_OFFSET)

%include "include/disk.inc"

;*********************************;
; Sets Rot Directory informations ;
; Returns:                        ;
;   ecx => location in sectors.   ;
;   eax => size in sectors        ;
;*********************************;
GetRootInfo:
    ; clear registers
    xor eax, eax
    xor ecx, ecx

    ; compute location of root directory in sectors and store in "ecx" 
    mov al, byte [bpb_NumberOfFATs]     ; number of FATs
    mul word [bpb_SectorsPerFAT]        ; sectors used by FATs
    add ax, word [bpb_ReservedSectors]  ; reserved sectors
    mov cx, ax                          ; move into cx
    mov ax, word [partition_offset]
    add cx, word [eax + 8]              ; offset from partition information

    ; compute size of root directory in sectors and store in "ax" 
    mov ax, 32                          ; 32 byte directory entry
    mul word [bpb_RootEntries]          ; total size of directory
    div word [bpb_BytesPerSector]       ; sectors used by directory

    ret

;***************************************;
; Load Root Directory Table to ROOT_SEG ;
;***************************************;
LoadRoot:
    call GetRootInfo

    ; read Root into memory at ROOT_SEG
    mov dl, byte [bpb_DriveNumber]
    mov ebx, dword ROOT_SEG
    call ReadSectorsLBA
    ret

;****************************;
; Loads FAT table to FAT_SEG ;
;****************************;
LoadFAT:
    pushad

    ; clear registers
    xor ax, ax
    xor cx, cx

    ; compute size of FAT and store in "ax" 
    mov ax, word [bpb_SectorsPerFAT]    ; number of FATs

    ; compute location of FAT and store in "ecx"
    mov cx, word [partition_offset]
    mov cx, word [ecx + 8]              ; offset from partition information
    add cx, word [bpb_ReservedSectors]

    ; read FAT into memory at FAT_SEG
    mov dl, byte [bpb_DriveNumber]
    mov ebx, dword FAT_SEG
    call ReadSectorsLBA

    popad
    ret

;***********************************************;
; Search for filename in root table             ;
; Parameters:                                   ;
;   si => File name                             ;
; Returns:                                      ;
;   ax => File index number in directory table. ;
;   di => Location of the file root entry       ;
;***********************************************;
FindFile:
    ; store registers   
    push cx
    push bx
    push es

    mov bx, si                      ; save filename for later

    push ROOT_SEG >> 16             ; locate first root entry
    pop es
    mov di, ROOT_SEG                ; ES:DI is memory location of root dir
    mov cx, word [bpb_RootEntries]  ; load loop counter
    ; browse root directory for binary image
    cld                             ; clear direction flag
.loop:
    push cx
    mov cx, 11                      ; eleven character name. Image name is in SI
    mov si, bx                      ; image name is in BX
    push di
    rep cmpsb                       ; test for entry match [ DS:SI - ES:DI ]
    pop di
    je .Found
    pop cx
    add di, 32                      ; queue next directory entry
    loop .loop

.NotFound:
    ; set error code
    mov ax, -1
    ; restore registers and return
    pop es
    pop bx
    pop cx
    ret

.Found:
    ; return value into AX contains entry of file
    pop ax
    ; restore registers and return
    pop es
    pop bx
    pop cx
    ret

;**********************************************;
; Gain information about the file to be loaded ;
; Parameters:                                  ;
;   edi => Location of the file root entry     ;
;**********************************************;
PrepareFile:
    ; get starting cluster
    push word ROOT_SEG >> 16
    pop es
    mov ax, word [es:edi + 0x1A]   ; retrive cluster from root entry
    mov word [cluster], ax

    ; get 0th cluster address
    call GetRootInfo
    add eax, ecx
    mov dword [first_cluster_sector], eax

    call LoadFAT
    ret

;*********************************************;
; Read the current selected cluster in memory ;
; Parameters:                                 ;
;   ebx => Buffer to load file to             ;
;*********************************************;
LoadNextCluster:
    ; zero out registers for calculations
    xor cx, cx
    xor dx, dx

    ; convert the cluster in lba
    mov ax, word [cluster]
    sub ax, 2
    mov dl, byte [bpb_SectorsPerCluster]
    mul dx
    xchg cx, ax
    add ecx, dword [first_cluster_sector]

    ; sets the others parameters and read the disk
    mov dl, byte [bpb_DriveNumber]
    mov al, byte [bpb_SectorsPerCluster]
    call ReadSectorsLBA

    ; get next cluster from fat table
    xor eax, eax
    mov ax, word [cluster]
    mov dx, 2
    mul dx ; since fat table is an array of words (2 byte)

    push word FAT_SEG >> 16
    pop es
    mov dx, word [dword es:eax]
    mov word [cluster], dx

    ret

;*******************************************************;
; Check if the current reading file has been compleated ;
; Returns:                                              ;
;   cf => set on compleated                             ;
;*******************************************************;
FileReadCompleated:
    ; test if it was the last cluster
    cmp word [cluster], 0xFFFF
    je .complete
    cmp word [cluster], 0xFFF8
    je .complete

.incomplete:
    clc ; clear carry flag
    ret
.complete:
    stc ; set carry flag
    ret

;************************************;
; Load file                          ;
; Parameters:                        ;
;   es:si => File name               ;
;   ebx => Buffer to load file to    ;
; Returns:                           ;
;   ax => -1 on error, 0 on success  ;
;************************************;
LoadFile:
.findFile:
    ; find file using name in es:si
    call FindFile
    cmp ax, -1
    je .done ; file not found

.loadFilePre:
    call PrepareFile

.nextCluster:
    call LoadNextCluster

    call FileReadCompleated
    jc .success

    ; increase address
    xor eax, eax
    mov ax, word [bpb_SectorsPerCluster]
    mul word [bpb_BytesPerSector]
    mov ecx, eax
    shr eax, 4
    shl eax, 16
    and ecx, 0xF
    or eax, ecx
    add ebx, eax
    ; check if riporto
    mov eax, ebx
    and eax, (1 << 4)
    cmp eax, 0
    je .nextCluster
    ; fix riporto
    xor ebx, 1 << 4
    add ebx, 1 << 16
    jmp .nextCluster

.success:
    xor ax, ax
.done
    ret

cluster dw 0x0000
first_cluster_sector dd 0x00000000


%endif ; __FAT12_INC__

Upvotes: 1

Views: 340

Answers (1)

user15511921
user15511921

Reputation: 21

This is not a full solution to the problem I have but still, I want to post this as an answer 'cause it can be useful for future readers.
Turns out that is not sure that bios functions will leave register as they are, in fact in my case int 13.42 was destroying the content of ebx.
Now I can boot my image in both BOCHS and Microsoft Hyper-V, It still does not boot on real hardware but surely this is an important thing.

Upvotes: 1

Related Questions