I am trying to make a bootloader and when I try to read my kernel from disk it shows:
Reading from disk failed!
I have checked all the registers to the function and they appear to be correct as suggested on wikipedia.
So I checked the memory address where the kernel is supposed to be loaded and found that it has been loaded correctly. Then I removed the error handling and tried to jump to the kernel location but it didn't execute.
So I tried running it on bochs and it does jump to the kernel location and executes that code, but I don't seem to see any results (I run it on qemu and debug on bochs)
[bits 16]
[org 0x7c00]
%define ENDL 0x0D, 0x0A, 0
start: jmp boot
%include "bootloader/out.asm"
%include "bootloader/disk.asm"
halt:
hlt
jmp halt
boot:
xor ax, ax ; sets ax to 0
; sets segments to 0
mov ds, ax
mov es, ax
mov ss, ax
mov si, wlcm_msg
call print
mov dh, 1
mov bx, kernel
call read_kernel
jmp kernel
jmp halt
wlcm_msg: db "Booted to 16 bit real mode", ENDL
times 510-($-$$) db 0
dw 0xaa55
kernel: db 0
;
; @params:
; dx(dl) - number of sectors to load
; bx - loaction in ram to store the read data
;
read_kernel:
pusha
push dx
mov ah, 02h
mov al, dh ; sectors to read
mov cl, 02h ; the sector to read 1 is our bootloader
mov ch, 0
mov dl, 0
mov dh, 0
int 13h
jc read_err ; if carry flag is set then there is an error
pop dx
cmp al, dh ; sector read count
jne sector_err
popa
ret
read_err:
mov si, read_err_msg
call print
mov dh, ah
jmp halt
sector_err:
mov si, sector_err_msg
call print
jmp halt
read_err_msg: db "Reading from disk failed!", ENDL
sector_err_msg: db "Incorrect number of sectors to read!", ENDL
[bits 16]
%define ENDL 0x0D, 0x0A, 0
start: jmp main
main:
mov ah, 9
xor bh, bh
mov cx, 1
mov al, 'E'
int 10h
cli
hlt
ASM = nasm
BOOT_SRC = bootloader/main.asm
KENREL_SRC = kernel/main.asm
BUILD_DIR = build
.PHONY: all clean create run debug
all: $(BUILD_DIR)/disk.img
$(BUILD_DIR)/disk.img: $(BUILD_DIR)/bootloader.bin $(BUILD_DIR)/kernel.bin
dd if=/dev/zero of=$@ bs=512 count=2880
dd if=$< of=$@ bs=512 conv=notrunc seek=0
dd if=$(word 2,$^) of=$@ bs=512 conv=notrunc seek=1
$(BUILD_DIR)/bootloader.bin: $(BOOT_SRC) create
$(ASM) -f bin $< -o $@
$(BUILD_DIR)/kernel.bin: $(KENREL_SRC) create
$(ASM) -f bin $< -o $@
create:
mkdir -p $(BUILD_DIR)
clean:
rm -rf $(BUILD_DIR)/*
run: $(BUILD_DIR)/disk.img
qemu-system-i386 -machine q35 -drive file=$<,format=raw
run_floppy: $(BUILD_DIR)/disk.img
qemu-system-i386 -machine q35 -fda $<
debug: bochs_config $(BUILD_DIR)/disk.img
bochs -f $<
I tried lots of ways to fix this but none seem to work, so I am pretty much convinced that this is an issue with my build script or the way I set up segments. I've found many questions with the same problem here, but I didn't really understand how that would apply here.
You have this line in your code:
mov ss, ax
The SS register is closely linked to the SP register. If SS changes, then the existing SP will 99.9% of the time not make sense in the new segment, so it needs to change too!
Add a line such as:
mov sp, 0x7C00
Since your bootloader is located in the region 0x7C00-0x7DFF
, placing the stack at 0x7C00
(growing downwards) is the most common choice (but feel free to choose anything else that doesn't conflict with already existing data such as the interrupt table, the BDA or your own code).
Setting SP should be done in the next instruction after the mov ss, ax
instruction, because the CPU disables interrupts briefly after it, so that you have time to set SP without interrupts corrupting the stack (this behavior is implemented on all x86 processors, save for a few old steppings of the ancient 8088, for which you have to enclose the stack pointer change in cli
- sti
).
If you already know that the machine you are working with has a 32-bit CPU (meaning you have access to EAX
, EBX
etc), you can also use the lss sp, [mem]
instruction, which loads the SS:SP pair from the memory location in a single instruction. However, it could be best to avoid this in bootloader code because the machine's hardware might still be unknown at this stage in the boot process.