OSDev.org
https://forum.osdev.org/

My bootloader does not load my stage 2
https://forum.osdev.org/viewtopic.php?f=1&t=33337
Page 1 of 1

Author:  Korek [ Sun Nov 25, 2018 6:51 am ]
Post subject:  My bootloader does not load my stage 2

Hello,

I wanted to write a small kernel but I failed at the bootloader. I created a img file formatted as FAT12 which contains my bootloader and the stage 2 called KRNLDR.SYS

I used the following commands:

Code:
nasm -fbin -o boot.bin boot.asm
dd if=/dev/zero of=test.img bs=512 count=2880
sudo losetup /dev/loop0 test.img
sudo mkdosfs -F 12 /dev/loop0


Now my system recognizes and mounts the device and I put my KRNLDR.SYS on it

Then I executed:

Code:
sudo dd if=boot.bin of=/dev/loop0 bs=512 count=1 conv=notrunc
sudo umount /dev/loop0
sudo losetup -d /dev/loop0


Now the file looks like a FAT12 floppy with a bootsector and the file KRNLDR.SYS

Now I execute it in QEMU:
Code:
qemu-system-i386 -device format=raw,file=test.img


And the output:
Code:
Operating System not found
Press any key to reboot


This is the output my bootloader prints to the screen if the file KRNLDR.SYS cannot be found by my bootloader

My Code:
Code:
org 0x0
bits 16

    jmp     word loader

bsOEMName:                  db "TestOS  "
bpbBytesPerSector:          dw 512
bpbSectorsPerCluster:       db 1
bpbReservedSectors:         dw 1
bpbNumberOfFATs:            db 2
bpbNumberOfRootEntries:     dw 224
bpbTotalSectors:            dw 2880
bpbMedia:                   db 0xf0
bpbSectorsPerFAT:           dw 9
bpbSectorsPerTrack:         dw 18
bpbNumberOfHeads:           dw 2
bpbHiddenSectors:           dd 0
bpbTotalSectorsBig:         dd 0
bsDriveNumber:              db 0
bsReserved:                 db 0
bsExtendedBootSignature:    db 0x29
bsVolumeID:                 dd 0x12345678
bsVolumeLabel:              db "TestOS     "
bsFileSystem:               db "FAT12   "

;-------------------------------------------------------------------------------
; SI = Zero terminated string to print
;-------------------------------------------------------------------------------

printMsg:
    push    ax

.printStart:
    lodsb
    or      al, al
    jz      .printEnd

    mov     ah, 0x0e
    int     0x10
    jmp     .printStart

.printEnd:
    pop     ax

    ret

;-------------------------------------------------------------------------------
; AX = Starting sector
; CX = Number of sectors to read
; ES:BX = Buffer
;-------------------------------------------------------------------------------

readSectors:
    mov     di, 0x0005

.readLoop:
    push    ax
    push    bx
    push    cx

    call    lbaToChs

    mov     ah, 0x02
    mov     al, 0x01
    mov     ch, byte [track]
    mov     cl, byte [sector]
    mov     dh, byte [head]
    mov     dl, byte [bsDriveNumber]
    int     0x13
    jnc     .success

    dec     di
    pop     cx
    pop     bx
    pop     ax
    jnz     .readLoop

.success:
    pop     cx
    pop     bx
    pop     ax

    inc     ax
    add     bx, word [bpbBytesPerSector]
    loop    readSectors

    ret

track:      db      0
head:       db      0
sector:     db      0

;-------------------------------------------------------------------------------
; AX = Logical sector
;-------------------------------------------------------------------------------

lbaToChs:
    xor     dx, dx
    div     word [bpbSectorsPerTrack]
    inc     dl
    mov     byte [sector], dl

    xor     dx, dx
    div     word [bpbNumberOfHeads]
    mov     byte [head], dl
    mov     byte [track], al

    ret

;-------------------------------------------------------------------------------
; AX = Cluster number
;-------------------------------------------------------------------------------

clusterToLba:
    sub     ax, 0x0002
    xor     cx, cx
    mov     cl, byte [bpbSectorsPerCluster]
    mul     cx

    ret

;-------------------------------------------------------------------------------

loader:
    cli

    mov     ax, 0x07c0
    mov     es, ax
    mov     gs, ax
    mov     fs, ax
    mov     ds, ax

    mov     ax, 0x0000
    mov     ss, ax
    mov     sp, 0xffff

    sti

    mov     byte [bsDriveNumber], dl

    xor     dx, dx
    xor     cx, cx
    mov     ax, 0x0020
    mul     word [bpbNumberOfRootEntries]
    div     word [bpbBytesPerSector]
    xchg    cx, ax ; Number of sectors of the root directory

    mov     al, byte [bpbNumberOfFATs]
    mul     word [bpbSectorsPerFAT]
    add     ax, word [bpbReservedSectors] ; Starting sector of the root directory

    mov     bx, 0x0200
    call    readSectors

    mov     cx, word [bpbNumberOfRootEntries]
    mov     di, 0x0200

searchRoot:
    push    cx
    mov     cx, 0x000b
    mov     si, stage2

    push    di
    rep cmpsb
    pop     di

    je      loadFat
    pop     cx
    add     di, 0x0020
    loop    searchRoot

    jmp     failure

loadFat:
    mov     dx, [di + 26] ; Starting address of entry
    xor     ax, ax
    mov     al, byte [bpbNumberOfFATs]
    mul     word [bpbSectorsPerFAT] ; Number of sectors used by the FATs

    mov     word [cluster], dx
    mov     cx, ax
    mov     ax, word [bpbReservedSectors]

    mov     bx, 0x0200
    call    readSectors

    mov     ax, 0x0050
    mov     es, ax
    mov     bx, 0x0000
    push    bx

loadFile:
    mov     ax, word [cluster]
    pop     bx
    call    clusterToLba
    xor     cx, cx
    mov     cl, byte [bpbSectorsPerCluster]
    call    readSectors
    push    bx

    mov     ax, word [cluster]

    mov     cx, ax
    mov     dx, ax
    shr     dx, 1
    add     cx, dx

    mov     bx, 0x0200
    add     bx, cx
    mov     dx, [bx]

    test    ax, 1
    jnz     oddCluster

evenCluster:
    and     dx, 0b0000111111111111
    jmp     next

oddCluster:
    shr     dx, 4

next:
    mov     word [cluster], dx
    cmp     dx, 0x0ff0
    jb      loadFile

    jmp     0x0050:0 ; Far jmp to KRNLDR.SYS

failure:
    mov     si, fail
    call    printMsg

    mov     si, anykey
    call    printMsg

    mov     ah, 0x00
    int     0x16 ; Await key press

    jmp     0xffff:0 ; Reboot with far jmp to BIOS

stage2:         db          "KRNLDR  SYS"
fail:           db          "Operating system not found", 0xd, 0xa, 0x0
anykey:         db          "Press any key to reboot", 0xd, 0xa, 0x0

cluster:        dw          0

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


What have I done wrong? Thanks for any help

Author:  nakst [ Sun Nov 25, 2018 7:02 am ]
Post subject:  Re: My bootloader does not load my stage 2

Korek wrote:
mov ax, 0x0000
mov ss, ax
mov sp, 0xffff


Your stack should be aligned to a 2-byte boundary in 16-bit mode.

Korek wrote:
What have I done wrong? Thanks for any help


Looking at the output of your program, it isn't finding the entry in the root directory.
How have you tried to debug this? There are several places where it could be going wrong:

  • The root directory isn't loaded properly from the floppy disk
  • The file wasn't copied correctly onto the disk and isn't in the root directory
  • Your code that scans the root directory isn't working

I highly recommend you run your bootloader in Bochs, and learn to use its debugger. You'll be able to step through your code, and inspect memory. When you can use the available debugging tools, you should be able to solve problems like this yourself.

Author:  Korek [ Sun Nov 25, 2018 8:45 am ]
Post subject:  Re: My bootloader does not load my stage 2

You're right. I should have used Bochs from the beginning and had to check if my code works instead of writing it completely without debugging

Author:  nullplan [ Sun Nov 25, 2018 12:45 pm ]
Post subject:  Re: My bootloader does not load my stage 2

Oh god, so much wrong here.

Your stack overlaps your entire code area. And your read buffer. Your read buffer starts at 0x7e00, so that's good, but where it ends is dependent on the BPB fields. With your defaults in the fields, the root directory is 14 sectors. So the root directory buffer is from 0x7e00 to 0x9a00. So you have about 26kB of stack before it clobbers your root directory.

Speaking of that, your algorithm for calculating the number of root directory sectors is inefficient and wrong. You take the number of root directory entries, mutiply them with 32 and divide them with the number of bytes per sector. But that last one is rounding down. Unless your number of root directory entries happens to be divisible by 16, your result will be 1 too low. Also, both the mul and the div can be replaced by a shift. In fact, you can combine the shift count beforehand, so there is no possibility of overflow: "Bytes per sector" must be a power of 2, right? So you can use "bsf" to log2 it, in all cases. Then subtract 5 and you have your right shift count. Take the number of root directory entries, add (1 << count), subtract 1, and >> count the result. That implements the division, but rounding up.

Also, you assume the root directory to be perfectly defragmented. It needn't be; that is sort of the purpose of the whole operation. The pseudo code for finding anything in any directory on any kind of FAT file system is:

Code:
cluster = startcluster; /* read from dir entry or BPB, for the root directory */
while (!found && cluster != endcluster) {
    readcluster(cluster); /* handle errors yourself here */
    for (i = 0; !found && i < clustersize/32; i++)
        if (!strncmp(buffer + i * 32, filename, 12))
            found = 1;
    cluster = FAT[cluster];
}

After that, you'll have the directory entry at buffer + i * 32. Of course, the indexing into the FAT is a bit of a problem. And the readcluster function is left as an exercise to the reader. :D

Also, please check if your memory allocation works out. From what I've seen, you are using the buffer at 7E00 both for the root directory and the FAT. I haven't read enough to know if this is a bug, but it is a code smell.

Also, why use segment 0x07c0? Isn't it easier to use segment 0? And what CPU are you targeting? The FS and GS references put it past the 8086 for sure (weren't these added with the 386 or even later?), but no 32-bit regs or variable shifts suggest otherwise

As for error handling: Why not let the user reboot, themselves? Just print "Press CTRL-ALT-DEL to reboot", then hang yourself into a loop that runs the HLT instruction with interrupts enabled. User can press CTRL-ALT-DEL, or they can turn of the computer. No int 16h required at all. As long as you have a stack set up, it'll work. Also, might want to think about continuous tone on error, in case the user of the code has no monitor and no keyboard (e.g. a server).

Author:  Korek [ Mon Nov 26, 2018 8:33 am ]
Post subject:  Re: My bootloader does not load my stage 2

I think my tutorial is bad. I learned this stuff from http://www.brokenthorn.com/Resources/OSDevIndex.html
The demo bootloader from this website doesn't even run. I'll try to fix my errors. Thanks for help

Author:  neon [ Mon Nov 26, 2018 5:56 pm ]
Post subject:  Re: My bootloader does not load my stage 2

Hi,

Due to differences between the provided code and the code as presented in the tutorials, in addition to some questionable control flow logic pertaining to the starting file cluster and data sector start in the presented code, we will ignore the presented code for the duration of this response. Instead, this response will be directed for the code as presented in the tutorials.

Regarding the stack location... The code loads the stack to 0:ffff which provides a safe 26k window. For performance, it would be better on a 2 byte boundary, however this is not a requirement. The code assumes this stack space is sufficient for the execution environment (the firmware.) We are not currently aware of any reported problems on this assumption not being met. However, you can relocate the stack for more space to be more cautious.

Regarding the buffer at 7e00... The original code as presented in the tutorials loads the root directory to 7e00. After finding the file, it copies the start cluster for later use. Since the root directory is no longer needed, we overwrite it when loading the FAT. This was intentional in order to conserve memory use.

A note on ReadSectors... There is a known error on the original implementation not supporting multi-segment reads. This occurs when the file being loaded exceeds 64k in size. If you are using a custom boot loader that exceeds this size, this could be the problem. We can provide an updated version if you believe it might help.

If possible, please provide a disk image of the demo for testing here.

Author:  Korek [ Fri Dec 07, 2018 10:20 am ]
Post subject:  Re: My bootloader does not load my stage 2

I just made it work. Thanks for help :D

Page 1 of 1 All times are UTC - 6 hours
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/