Page 1 of 1

Unknown problem reading kernel from second stage bootloader

Posted: Wed Dec 16, 2020 5:18 pm
by Bonfra
I'm not sure what the problem is, I use this function to read sectors from disk:

Code: Select all

;***********************************;
; Reads a series of sectors         ;
; Parameters:                       ;
;   dl => bootdrive                 ;
;   ax => sectors count             ;
;   ebx => address to load to       ;
;   ecx => LBA address              ;
; Returns:                          ;
;   cf => set if error              ;
;***********************************;
ReadSectorsLBA:
.init:
    pusha
    mov ah, 0x00   ; Reset disk function
    int 0x13
    jc .done
    popa

.readSec:
    mov [LBA_Packet.block_cout], ax
    mov [LBA_Packet.transfer_buffer], ebx
    mov [LBA_Packet.lba_value], ecx
    mov si, LBA_Packet
    mov ah, 0x42    ; Read sectors function
    int 0x13
.done
    ret

align 4
LBA_Packet:
    .packet_size     db 0x10 ; use_transfer_64 ? 10h : 18h
    .reserved        db 0x00 ; always zero    
    .block_cout      dw 0x00 ; number of sectors to read
    .transfer_buffer dd 0x00 ; address to load in ram
    .lba_value       dq 0x00 ; LBA addres value   
I use it load the VBR from the MBR and the second stage bootloader from the first stage so it should work perfectly.
I wrote some function to interact with the FAT16 and I use them in the first stage bootloader, so they should be fine. The only one that I doubt is the one that actually loads the file, since the second stage is not larger than a cluster I haven't tested it for multuple clusters. Anyway at least one cluster should be loaded, this is not the important part.
In the LoadFile function after checking if the file exists I load it into memory 1 cluster at a time. I read a cluster by reading a bunch of sectors using the function above.
This exact function works to load the second stage from the first stage but doesn't even load a sector when I try to load the kernel.
I checked all the register to be sure that the values were correct before calling ReadSectorsLBA and they are. I don't know what to do. I'm posting the important bits here but I'll also link the github repository, the code is in the boot folder: https://github.com/DefEnge/test-kernel.

Here is the LoadFile function:

Code: Select all

;************************************;
; Load file                          ;
; Parameters:                        ;
;   es:si => File name               ;
;   ebx => Buffer to load file to    ;
; Returns:                           ;
;   ax => -1 on error, 0 on success  ;
;************************************;
LoadFile:
.findFile:

    call FindFile       ; find file using name in es:si
    cmp ax, -1
    jne .loadFilePre

    ; file not found
    mov ax, -1          ; error code
    ret

.loadFilePre:
    ; get starting cluster
    mov edx, dword ROOT_OFFSET          ; edx points to the rood dir
    add edx, edi                        ; edx + edi is the index of the file retrived with findFile
    mov ax, word [dword edx + 0x001A]   ; 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

.nextCluster:

    xor ecx, ecx
    xor ax, ax
    mov cx, word [cluster]
    sub cx, 2
    add ecx, dword [first_cluster_sector]
    mov dl, byte [bpb_DriveNumber]
    mov al, byte [bpb_SectorsPerCluster]
    call ReadSectorsLBA

    ; "breakpoint" that activates only in second stage
    %ifdef SECOND_STAGE 
    jmp $
    %endif

    ; get next cluster from fat table
    xor eax, eax
    mov ax, word [cluster]
    add eax, FAT_OFFSET
    mov bx, word [dword eax]
    mov word [cluster], bx

    ; test if it was the last cluster
    cmp word [cluster], 0xFFFF
    je .done
    cmp word [cluster], 0xF8FF
    je .done

    ; increase address
    xor eax, eax
    mov ax, word [bpb_SectorsPerCluster]
    mul word [bpb_BytesPerSector]
    add ebx, eax

    jmp .nextCluster

.done:
    mov ax, 0
    ret

cluster dw 0x0000
first_cluster_sector dd 0x00000000
It relies on some functions, theese are the info about them (I'm 99.9% sure they work so I'll not post them here. If you want to check or if you obviusly need a minimail example to replicate they are in the repo linked above in boot/include/fat16.inc):

Code: Select all

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

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

;*********************************;
; Sets Rot Directory informations ;
; Returns:                        ;
;   edx => location in sectors.   ;
;   ax => size in sectors         ;
;*********************************;
GetRootInfo

Re: Unknown problem reading kernel from second stage bootloa

Posted: Wed Dec 16, 2020 6:48 pm
by astralorchid
Can you post what's in the packet's memory addresses right before int 0x13? Might make it easier to identify the issue.

Re: Unknown problem reading kernel from second stage bootloa

Posted: Wed Dec 16, 2020 7:26 pm
by Octocontrabass
Bonfra wrote:

Code: Select all

ReadSectorsLBA:
.init:
    pusha
    mov ah, 0x00   ; Reset disk function
    int 0x13
    jc .done
    popa

    <...>

.done
    ret
I found one problem. Do you see it? (I've removed the irrelevant parts of the code.)
Bonfra wrote:I checked all the register to be sure that the values were correct before calling ReadSectorsLBA and they are.
How did you check the values? What values were they?

Re: Unknown problem reading kernel from second stage bootloa

Posted: Wed Dec 16, 2020 10:34 pm
by Gigasoft
There are numerous things wrong here:
- Location of root directory
- Start of data area
- Cluster location calculation
- Reading FAT entries
- Address adjustment
- Address overwritten by cluster number

Re: Unknown problem reading kernel from second stage bootloa

Posted: Thu Dec 17, 2020 1:26 am
by Bonfra
Octocontrabass wrote: I found one problem. Do you see it? (I've removed the irrelevant parts of the code.)
Oh, I see, the popa is never reached if the init fails...
Octocontrabass wrote:
Bonfra wrote:I checked all the register to be sure that the values were correct before calling ReadSectorsLBA and they are.
How did you check the values? What values were they?
I used a "breakpoint", it's just a jmp $ right before the function call to check the registers, and right after to check if the sectors where loaded:

Code: Select all

xor ecx, ecx
    xor ax, ax
    mov cx, word [cluster]
    sub cx, 2
    add ecx, dword [first_cluster_sector]
    mov dl, byte [bpb_DriveNumber]
    mov al, byte [bpb_SectorsPerCluster]
    call ReadSectorsLBA

    ; "breakpoint" that activates only in second stage
    %ifdef SECOND_STAGE
    jmp $
    %endif
The ifdef part is to avoid hanging in the first stage since the code is shared.
Then I inspected the registers with gdb:

Code: Select all

interrupt
info register <register>
And the memory with qemu:

Code: Select all

xp /10 0x40000
where 10 is the amount of bytes that will be printed and 0x40000 is the address where the kernel should be loaded. My plan is to load it at 0x100000 but for testing i moved it to a lower address.
astralorchid wrote:Can you post what's in the packet's memory addresses right before int 0x13? Might make it easier to identify the issue.
Before calling ReadSectorsLBA the registers contains this values:

Code: Select all

dl => 0x80 ; drive number
ax => 0x4 ; sectors to read
ebx => 0x40000000 ; address to load to, calculated with: LIN_TO_FAR_ADDR(linaddr) (((linaddr >> 4) << 16) | (linaddr & 0xf)) where in this case linaddr is 040000
ecx => 0x202008a5 ; LBA starting sector
Then theese values populate the packed inside the function.
ecx is calculated by adding the cluster in the root/fat - 2 with the end of the root directory. This calculation is in .loadFilePre and .nextCluster.

Code: Select all

.loadFilePre:
    ; get starting cluster
    mov edx, dword ROOT_OFFSET          ; edx points to the rood dir
    add edx, edi                        ; edx + edi is the index of the file retrived with findFile
    mov ax, word [dword edx + 0x001A]   ; 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

.nextCluster:

    xor ecx, ecx
    xor ax, ax
    mov cx, word [cluster]
    sub cx, 2
    add ecx, dword [first_cluster_sector]
    mov dl, byte [bpb_DriveNumber]
    mov al, byte [bpb_SectorsPerCluster]
    call ReadSectorsLBA

Re: Unknown problem reading kernel from second stage bootloa

Posted: Thu Dec 17, 2020 1:45 am
by Bonfra
Gigasoft wrote:There are numerous things wrong here:
- Location of root directory
- Start of data area
- Cluster location calculation
- Reading FAT entries
- Address adjustment
- Address overwritten by cluster number
Can you explain deeper the errors so I can try fix them?

Re: Unknown problem reading kernel from second stage bootloa

Posted: Thu Dec 17, 2020 10:39 am
by Octocontrabass
Bonfra wrote:Before calling ReadSectorsLBA the registers contains this values:

Code: Select all

dl => 0x80 ; drive number
ax => 0x4 ; sectors to read
ebx => 0x40000000 ; address to load to, calculated with: LIN_TO_FAR_ADDR(linaddr) (((linaddr >> 4) << 16) | (linaddr & 0xf)) where in this case linaddr is 040000
ecx => 0x202008a5 ; LBA starting sector
The drive number and address match what you say they should be.
Did you verify the number of sectors and the LBA? (Hint: you can multiply the LBA by 512 to get the byte offset into your disk image.)

Re: Unknown problem reading kernel from second stage bootloa

Posted: Thu Dec 17, 2020 1:44 pm
by Bonfra
Octocontrabass wrote: Did you verify the number of sectors and the LBA? (Hint: you can multiply the LBA by 512 to get the byte offset into your disk image.)
Ok, I assumed some stuff during the calculations that revealed to be wrong. Now the LBA address is correct, it loads the sectors fine. Thanks for the suggestions, there are so many things to consider and I forgot about this one. It works.