Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assembly Bootloader Not Jumping to Kernel

I'm trying to write a bootloader and kernel in assembly for learning purposes. When I assemble my code, and boot a virtual machine with it, the bootloader seems to work properly, but the kernel never starts. I'm thinking that I'm jumping to the wrong instruction, but don't know what corrections I need to make to resolve my issue. There's also the possibility that I'm trying to make the floppy incorrectly, but I don't think this is the issue. When I look at the image files in a hex editor it appears that they were appended correctly. Another cause might be reading the wrong sector from the floppy disk. I'm trying to get the kernel to be in, and be read from, the sector right after the bootloader.

To build and run this code I'm doing the following in Windows Vista x64:

nasm bootloader_2.asm -f bin -o bootloader_2.bin 
nasm kernel_2.asm -f bin -o kernel.bin 
partcopy bootloader_2.bin bootloader_2.img 0d 511d 
partcopy kernel_2.bin kernel_2.img 0d 511d 
copy bootloader.img /b + kernel.img POS_2.img

I'm then using Oracle VM Virtual Box to mount the POS_2.img as a floppy drive, and run it on a guest system.

The results are

Patrick's Bootloader Started. Floppy has been reset. Reading Kernel Sector Kernel Sector Loaded

. And the kernel never starts.

Here's my code

bootloader_2.asm

bits 16
org 0x7C00
boot: jmp loader

; OEM Parameter block / BIOS Parameter block (wtf is this for?)
times 0Bh-$+boot DB 0
bpbBytesPerSector:        DW 512
bpbSectorsPerCluster:    DB 1
bpbReservedSectors:        DW 1
bpbNumberOfFATs:        DB 2
bpbRootEntries:            DW 224
bpbTotalSectors:        DW 2880
bpbMedia:                DB 0xF0
bpbSectorsPerFAT:        DW 9
bpbSectorsPerTrack:        DW 18
bpbHeadsPerCylinder:    DW 2
bpbHiddenSectors:        DD 0
bpbTotalSectorsBig:        DD 0
bsDriveNumber:            DB 0
bsUnused:                DB 0
bsExtBootSignature:        DB 0x29
bsSerialNumber:            DD 0xa0a1a2a3
bsVolumeLabel:            DB "MOS FLOPPY"
bsFileSystem:            DB "FAT12"
; END PARAMETER BLOCK

; ----- Variables -----

started db "Patrick's Bootloader Started...", 0x0D, 0x0A, 0
floppy_reset_done db "Floppy has been reset.", 0x0D, 0x0A, 0
loading_msg db "Reading Kernel Sector", 0x0D, 0x0A, 0
loading_sucess db "Kernel Sector Loaded", 0x0D, 0x0A, 0
done db "Bootloader Done.", 0x0D, 0x0A, 0

; ----- End Variables -----

; ----- Calls -----

reset_floppy:
    mov ah, 0
    mov dl, 0
    int 0x13
    jc reset_floppy
    mov si, floppy_reset_done
    call print_string
    ret

read_kernel:
    mov si, loading_msg
    call print_string
    mov si, 0x0

    mov ax, 0x1000    ; setting up the address to read into
    mov es, ax        ; moving the value in to es
    xor bx, bx        ; clearing bx
    mov ah, 0x02    ; floppy function
    mov al, 1        ; read 1 sector
    mov ch, 1        ; track
    mov cl, 2        ; sector to read
    mov dh, 0        ; head number
    mov dl, 0        ; drive number

    int 0x13        ; BIOS Interrupt Call

    jc read_kernel

    mov si, loading_sucess
    call print_string

    ret

print_string:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    int 0x10
    jmp print_string
.done:
    ret

; input is ax, cx is destroyed    
print_hex:
    mov cx, 4
    .next_digit:
        push cx
        mov cl, 4
        rol ax, cl
        push ax
        and al, 0x0F
        add al, '0'
        cmp al, '9'
        jle .not_a_leter
        add al, 'A'-'9'-1
    .not_a_leter:
        mov ah, 0x0E
        int 0x10
        pop ax
        pop cx
        loop .next_digit
    ret

; ----- End of Calls -----

; ===== Bootloader Main =====

loader:
    mov si, started
    call print_string
    call reset_floppy
    call read_kernel

    jmp 0x1000:0x0

    mov si, done        ; never reached. Intentional for debugging
    call print_string    ; these lines failure to produce a result tell us that the jmp was attempted

; ===== End of Bootloader Main =====

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

kernel_2.asm

kernel:
    jmp k_main

welcome_msg db "Welcome to Patrick's Operating System!", 0x0D, 0x0A, 0

print_string:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    int 0x10
    jmp print_string
.done:
    ret

k_main:

    mov si, welcome_msg
    call print_string

    .k_main_loop:

    jmp .k_main_loop

cli
hlt

times 512-($-$$) db 0
like image 555
Patrick Sanford Avatar asked Aug 30 '14 13:08

Patrick Sanford


1 Answers

At the risk of pointing out the obvious, your script doesn't copy the right file. In some places you refer to kernel.bin and in others kernel_2.bin. Try this instead:

nasm bootloader_2.asm -f bin -o bootloader_2.bin 
nasm kernel_2.asm -f bin -o kernel_2.bin 
partcopy bootloader_2.bin bootloader_2.img 0d 511d 
partcopy kernel_2.bin kernel_2.img 0d 511d 
copy bootloader.img /b + kernel_2.img POS_2.img

You may also find it easier for such things to use dd instead. It's available as part of Cygwin and has the advantage that it can write to either files or physical devices such as a real floppy disk.

Also, you should consider using a Makefile instead of script. It will help as you expand your project further.

That said, there are three basic problems with the code. First, the next sector to be loaded is on cylinder 0, not cylinder 1. Second, the code can't simply safely ret to the kernel - you must jump there explicitly. That portion of the code would then look like this:

read_kernel:
    mov si, loading_msg
    call print_string
    mov si, 0x0

    mov ax, 0x1000   ; setting up the address to read into
    mov es, ax       ; moving the value in to es
    xor bx, bx       ; clearing bx
    mov ah, 0x02     ; floppy function
    mov al, 1        ; read 1 sector
    mov ch, 0        ; cylinder
    mov cl, 2        ; sector to read
    mov dh, 0        ; head number
    mov dl, 0        ; drive number

    int 0x13         ; BIOS Interrupt Call

    jc read_kernel
    push es          ; either push the address and retf or use far jmp
    push bx          ;

    mov si, loading_sucess
    call print_string

    ; jmp  0x1000:0  ; alternative to push/retf is simple long jump

    retf

The third problem is the kernel. You haven't changed the DS register to point to the new offset so even if you get to that code, it won't print the string you intend. Change that code to look like this instead:

k_main:
    push cs          ; save the cs reg
    pop  ds          ; use as ds also

    mov si, welcome_msg
    call print_string

    .k_main_loop:

    jmp .k_main_loop
like image 105
Edward Avatar answered Oct 17 '22 01:10

Edward