assemblykernelnasmqemubootloader

Kernel not executed, after being loaded


Issue

Kernel seems to be loaded, by the bootloader, but no further execution. It seems like there is a problem either with the way I run the image with qemu or with the way the kernel is loaded.

Source code

makefile

bold=`tput bold`

black=`tput setaf 0`
red=`tput setaf 1`
green=`tput setaf 2`
yellow=`tput setaf 3`
blue=`tput setaf 4`
magenta=`tput setaf 5`
cyan=`tput setaf 6`
white=`tput setaf 7`

reset=`tput sgr0`


default: build run

build:
    @echo "${bold}${green}[INFO]${reset} Compiling Bootloader"
    @nasm -f bin src/bootloader.asm -o bin/boot.bin

    @echo "${bold}${green}[INFO]${reset} Compiling Kernel"
    @nasm -f bin src/kernel.asm -o bin/kernel.bin

    @echo "${bold}${green}[INFO]${reset} Compiling the OS"
    @cat bin/boot.bin bin/kernel.bin > bin/os.bin

    @echo "${bold}${green}[INFO]${reset} Compiled."

run:
    @echo "${bold}${green}[INFO]${reset} Launching the VM:"
    @qemu-system-x86_64 -enable-kvm -drive format=raw,file=bin/os.bin -m 4G -cpu host -smp 2 -vga virtio -display sdl,gl=on

clean:
    @echo "${bold}${green}[INFO]${reset} Cleaning..."
    @rm -rfv bin/*.bin
    @echo "${bold}${green}[INFO]${reset} Cleaned"

helper functions: src/utils/print.asm

print_string_buffer:
    ; The function prints a string from the buffer 
    ; stored in the SI register, it expects a 
    ; null symbol to be found in the buffer.  
    ; Parameters:
    ;   si - memory offset to the buffer 

    push si
    push ax

.loop:
    lodsb
    or al, al
    jz .done

    ; display character
    mov ah, 0x0A
    mov bh, 0x00
    mov cx, 1
    int 0x10

    ; move cursor
    mov ah, 0x02
    inc dl
    int 0x10

    jmp .loop

.done:
    pop  ax
    pop  si
    ret

src/disk.asm

disk_load:
    ; The function reads DH number of sectors
    ; into ES:BX memory location from drive DL
    ; Parameters:
    ;   es:bx - buffer memory address 

    push dx             ; store dx on stack for error handling later 

    mov ah, 0x02        ; INT 13H 02H, BIOS read disk sectors into memory
    mov al, dh          ; number of sectors
    mov ch, 0x00        ; cylinder  
    mov dh, 0x00        ; head
    mov cl, 0x02        ; start reading sector (2 is the sector after the bootloader)
    int 0x13            ; BIOS interrupt for disk functions 

    jc disk_error       ; checks if CF (carry flag) set to 1

    pop dx              ; restore dx value from stack
    cmp dh, al          ; checks dh (number of read sectors) vs al (number of desired read sectors) 
    jne disk_error      ; if not the desired amount of sectors were read, then error 

    call disk_success
    ret                 ; return to caller

disk_error:
    mov si, DISK_ERROR_MESSAGE
    call print_string_buffer
    hlt

disk_success:
    mov si, DISK_SUCCESS_MESSAGE
    call print_string_buffer

    ; move cursor
    mov ah, 0x02
    inc dh
    mov dl, 0
    int 0x10

    ret

DISK_ERROR_MESSAGE: db "could not read from disk", 0
DISK_SUCCESS_MESSAGE: db "successfully loaded the disk", 0

bootloader src/bootloader.asm

org 0x7C00

%define color_black   0
%define color_blue    1
%define color_green   2
%define color_cyan    3
%define color_red     4
%define color_magenta 5
%define color_orange  6
%define color_gray    7
%define color_yellow  14
%define color_white   15

;;;;;;;;
; INIT ;
;;;;;;;;

clear_screen:
    ; Int 0x10
    ; AH = 06h
    ; AL = number of lines by which to scroll up (00h = clear entire window)
    ; BH = attribute used to write blank lines at bottom of window
    ; CH, CL = row, column of window's upper left corner
    ; DH, DL = row, column of window's lower right corner

    mov ax, 0x0600                              ; AH = 6 = Scroll Window Up, AL = 0 = clear window
    mov bh, color_black << 4 | color_magenta    ; Attribute to clear screen with (White on Red)
    xor cx, cx                                  ; Clear window from 0, 0
    mov dx, 25 << 8 | 80                        ; Clear window to 24, 80
    int 0x10                                    ; Clear the screen

    mov ah, 0x02                                ; Set cursor
    mov bh, 0x00                                ; Page 0
    mov dx, 0x00                                ; Row = 0, col = 0
    int 0x10



set_custom_cursor:
    ; Int 0x10
    ; AH = 01h
    ; CH = start scan line of character matrix (0-1fH; 20H=no cursor)
    ; CL = end scan line of character matrix (0-1fH)

    mov ax, 0x0100                              ; AH = 1 = Set Cursor Shape & Size, AL = 0 = nothing
    mov ch, 0x1                                 ; Sets the width of the cursor, the higher the thicker
    mov cl, 0x10                                ; Sets the height of the cursor, the less the higher
    int 0x10

; bootloader start-up message
mov si, start_up_message 
call print_string_buffer


; set up DX for disk loading
mov dh, 0x1       ; number of sectors that will be loaded into memory
mov dl, 0x0       ; drive number to load (0 = boot disk)

; set up ES:BX memory address to load sectors into
mov bx, 0x1000  ; load sector to memory address 0x1000
mov es, bx      ; ES = 0x1000 
mov bx, 0x0       ; ES:BX = 0x1000:0 (segment:offset)

; set up segment registers for RAM
mov ax, 0x1000
mov ds, ax      ; data segment
mov es, ax      ; extra segment
mov fs, ax       
mov gs, ax
mov ss, ax      ; stack segment


;;;;;;;;;;;;;;;;;;;;;;;;;;
; BOOTLOADER ENTRY POINT ; 
;;;;;;;;;;;;;;;;;;;;;;;;;;
call disk_load  ; loads kernel into memory
jmp 0x1000:0x0


;;;;;;;;;;;
; IMPORTS ;
;;;;;;;;;;;

%include "src/utils/print.asm"
%include "src/disk.asm"


;;;;;;;
; VAR ;
;;;;;;;

start_up_message: db "Bootloader bootstrap", 0


;;;;;;;;;;;;;;;;;
; MAGIC NUMBERS ;
;;;;;;;;;;;;;;;;;

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

kernel src/kernel.asm

;;;;;;;;;;;;;;;
; ENTRY POINT ;
;;;;;;;;;;;;;;;

start:
    mov si, OS_VERSION 
    call print_string_buffer

    hlt


;;;;;;;;;;;
; IMPORTS ;
;;;;;;;;;;;

%include "src/utils/print.asm"


OS_VERSION: db "OS v1.0", 0

; sector padding
times 512-($-$$) db 0

After running make, the following project's structure is seen:

.
├── bin
│   ├── boot.bin
│   ├── kernel.bin
│   └── os.bin
├── makefile
└── src
    ├── bootloader.asm
    ├── disk.asm
    ├── kernel.asm
    └── utils
        └── print.asm

I tried to make the jump from the bootloader to the kernel at a different location, by setting ES:BX registers to values, like 0x0:0x1000. Also, I increased the memory of the virtual machine to 4GB of RAM. None of the following helped.

Any theories or ideas will be helpful. Thanks.


Update on new findings

Making a floopy image and running it in the VirtualBox seems to work just fine. Whereas the same floopy image still freezes after the execution of the bootloader, not reaching the kernel. Thus the source code should be right, and the issue is with the qemu arguments.

changes of the makefile:

...
build:
    @echo "${bold}${green}[INFO]${reset} Compiling Bootloader"
    @nasm -f bin src/bootloader.asm -o bin/boot.bin

    @echo "${bold}${green}[INFO]${reset} Compiling Kernel"
    @nasm -f bin src/kernel.asm -o bin/kernel.bin

    @echo "${bold}${green}[INFO]${reset} Compiling the OS"
    @cat bin/boot.bin bin/kernel.bin > bin/os.bin

    @echo "${bold}${green}[INFO]${reset} Compiling the Floopy image"
    @dd if=/dev/zero of=bin/floopy.img bs=1024 count=1440
    @dd conv=notrunc if=bin/os.bin of=bin/floopy.img

    @echo "${bold}${green}[INFO]${reset} Compiled."

run:
    @echo "${bold}${green}[INFO]${reset} Launching the VM:"
    @qemu-system-x86_64 -enable-kvm -drive format=raw,file=bin/floopy.img -m 4G -cpu host -smp 2 -vga virtio -display sdl,gl=on
...

Solution

Saving the initial value of the DL register, which was set up by BIOS, before the control was given to the bootloader, solved the issue.

fixed bootloader

org 0x7C00

%define color_black   0
%define color_blue    1
%define color_green   2
%define color_cyan    3
%define color_red     4
%define color_magenta 5
%define color_orange  6
%define color_gray    7
%define color_yellow  14
%define color_white   15

;;;;;;;;
; INIT ;
;;;;;;;;

; save DL (drive number) value from the BIOS
mov [drive_number], dl


clear_screen:
    ; Int 0x10
    ; AH = 06h
    ; AL = number of lines by which to scroll up (00h = clear entire window)
    ; BH = attribute used to write blank lines at bottom of window
    ; CH, CL = row, column of window's upper left corner
    ; DH, DL = row, column of window's lower right corner

    mov ax, 0x0600                              ; AH = 6 = Scroll Window Up, AL = 0 = clear window
    mov bh, color_black << 4 | color_magenta    ; Attribute to clear screen with (White on Red)
    xor cx, cx                                  ; Clear window from 0, 0
    mov dx, 25 << 8 | 80                        ; Clear window to 24, 80
    int 0x10                                    ; Clear the screen

    mov ah, 0x02                                ; Set cursor
    mov bh, 0x00                                ; Page 0
    mov dx, 0x00                                ; Row = 0, col = 0
    int 0x10



set_custom_cursor:
    ; Int 0x10
    ; AH = 01h
    ; CH = start scan line of character matrix (0-1fH; 20H=no cursor)
    ; CL = end scan line of character matrix (0-1fH)

    mov ax, 0x0100                              ; AH = 1 = Set Cursor Shape & Size, AL = 0 = nothing
    mov ch, 0x1                                 ; Sets the width of the cursor, the higher the thicker
    mov cl, 0x10                                ; Sets the height of the cursor, the less the higher
    int 0x10

; bootloader start-up message
print_start_up_message:
    mov si, start_up_message 
    call print_string_buffer

    ; move cursor
    mov ah, 0x02
    inc dh
    mov dl, 0
    int 0x10

; set up DX for disk loading
mov dh, 0x1       ; number of sectors that will be loaded into memory
mov dl, [drive_number]       ; drive number to load (0 = boot disk)

; set up ES:BX memory address to load sectors into
mov bx, 0x1000  ; load sector to memory address 0x1000
mov es, bx      ; ES = 0x1000 
mov bx, 0x0       ; ES:BX = 0x1000:0 (segment:offset)

; set up segment registers for RAM
mov ax, 0x1000
mov ds, ax      ; data segment
mov es, ax      ; extra segment
mov fs, ax       
mov gs, ax
mov ss, ax      ; stack segment


;;;;;;;;;;;;;;;;;;;;;;;;;;
; BOOTLOADER ENTRY POINT ; 
;;;;;;;;;;;;;;;;;;;;;;;;;;
call disk_load  ; loads kernel into memory
jmp 0x1000:0x0


;;;;;;;;;;;
; IMPORTS ;
;;;;;;;;;;;

%include "src/utils/print.asm"
%include "src/disk.asm"


;;;;;;;
; VAR ;
;;;;;;;

start_up_message: db "Bootloader bootstrap", 0
drive_number: resb 8


;;;;;;;;;;;;;;;;;
; MAGIC NUMBERS ;
;;;;;;;;;;;;;;;;;

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

Solution

  • mov dl, 0x0       ; drive number to load (0 = boot disk)
    

    You already solved the main issue with the help of @Jester and @MichaelPetch.
    A number of problems still remain: