Here is my MBR code:
config/boot.asm:
%ifndef BOOT_H
%define BOOT_H
; Kernel loader address.
LOADER_BASE_ADDR: equ 0x900
; The logical sector address of the kernel loader on the disk.
LOADER_START_SECTOR: equ 0x2
; The number of sectors occupied by the kernel loader.
LOADER_SECTORS: equ 4
%endif
mbr.asm:
section MBR vstart=0x7C00
jmp main
%include "config/boot.asm"
[bits 16]
main:
mov ax, 0
mov ss, ax
mov ds, ax
mov fs, ax
mov es, ax
mov sp, $$
push bp
mov bp, sp
call init
; Read starting sector from disk.
call read_disk
; Started supporting OS loader.
jmp LOADER_BASE_ADDR
init:
push bp
mov bp, sp
; Clean up BIOS output.
mov ax, 0xB800
mov es, ax
mov cx, 2000
mov di, 0
clean_screen:
mov word [es: di], 0
add di, 2
loop clean_screen
leave
ret
; Read OS loader from disk.
read_disk:
push bp
mov bp, sp
; Set the number of sectors to read.
mov dx, 0x1F2
mov ax, LOADER_SECTORS
out dx, al
; Set the logical sector address.
mov ax, LOADER_START_SECTOR
mov dx, 0x1F3
out dx, al
mov cl, 8
mov dx, 0x1F4
shr ax, cl
out dx, al
shr ax, cl
mov dx, 0x1F5
out dx, al
shr ax, cl
and al, 0x0F
or al, 0xE0
mov dx, 0x1F6
out dx, al
; Send a read command.
mov dx, 0x1F7
mov al, 0x20
out dx, al
; Wait for the hard drive to prepare the data.
mov dx, 0x1F7
.read_disk_wait:
nop
in al, dx
and al, 0x88
cmp al, 0x08
jnz .read_disk_wait
; The hard disk is ready to start reading data.
mov di, LOADER_BASE_ADDR
mov ax, LOADER_SECTORS
mov dx, 256
mul dx
mov cx, ax
mov dx, 0x1F0
.read_disk_read:
in ax, dx
mov [di], ax
add di, 2
loop .read_disk_read
leave
ret
times 510-($-$$) db 0
db 0x55, 0xAA
Here is the code of my kernel loader:
section CORE_LOADER vstart=LOADER_BASE_ADDR
jmp main
%include "config/boot.asm"
%include "config/gdt.asm"
%include "print.asm"
MESSAGE: db "Hello World", 0
STRLEN: equ $ - MESSAGE
[bits 16]
main:
mov sp, $$
push bp
mov bp, sp
; Load GDT, turn on protected mode.
in al, 0x92
or al, 2
out 0x92, al
lgdt [GDT_PTR]
mov eax,cr0
or eax, 1
mov cr0, eax
jmp dword SELECTOR_CODE:p_mode_start
[bits 32]
p_mode_start:
mov esp, $$
mov ax, SELECTOR_DATA
mov ds, ax
mov ss, ax
mov gs, ax
mov es, ax
; clean screen
call clean_screen
; Output "Hello World".
push MESSAGE
call print
add esp, 8
jmp $
I didn't give the code for the "print" and the structure of the "GDT", but I can guarantee that there are no problems with them.
Here is my Makefile:
SOURCE=src
BUILD_DIR=build
ASSEMBLER=@nasm -I $(SOURCE)
DD=@dd
OS_IMG=$(BUILD_DIR)/aszswaz.img
MBR=$(BUILD_DIR)/mbr.bin
OS_LOADER=$(BUILD_DIR)/os-loader.bin
IMG_SECTOR=60
all: $(BUILD_DIR) \
$(OS_IMG)
$(BUILD_DIR):
@mkdir -p $@
.PHONY: clean
clean:
@rm -rf $(BUILD_DIR)
# Build an OS image.
$(OS_IMG): $(MBR) $(OS_LOADER)
$(DD) if=/dev/zero of=$@ bs=1M count=$(IMG_SECTOR) >> /dev/null 2>&1
$(DD) if=$(MBR) of=$@ bs=512 count=1 conv=notrunc >> /dev/null 2>&1
$(DD) if=$(OS_LOADER) of=$@ bs=512 seek=2 conv=notrunc >> /dev/null 2>&1
$(MBR): $(SOURCE)/mbr.asm $(SOURCE)/print.asm $(SOURCE)/config/boot.asm
$(ASSEMBLER) $< -o $@
$(OS_LOADER): $(SOURCE)/os-loader.asm $(SOURCE)/print.asm $(SOURCE)/config/boot.asm $(SOURCE)/config/gdt.asm
$(ASSEMBLER) $< -o $@
Here is my bochs configuration:
megs: 32
# Set BIOS and vga.
romimage: file=/usr/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
boot: disk
log: bochs.log
mouse: enabled=0
keyboard: keymap=/usr/share/bochs/keymaps/x11-pc-us.map
# Set up the hard disk.
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path=build/aszswaz.img, mode=flat, cylinders=121, heads=16, spt=63
Here is my qemu configuration:
$ qemu-system-i386 \
-name 'guest=aszswaz' \
-m 1M \
-boot 'menu=on,strict=on' \
-drive 'file=build/aszswaz.img,format=raw'
Here are my bochs and qemu versions:
$ bochs --help
========================================================================
Bochs x86 Emulator 2.7
Built from SVN snapshot on August 1, 2021
Timestamp: Sun Aug 1 10:07:00 CEST 2021
========================================================================
...
$ qemu-system-i386 -version
QEMU emulator version 7.1.0
Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers
I checked the memory at 0x900 through GDB, and the MBR running in qemu doesn't seem to be reading the OS loader normally, but the IO port doesn't give any exceptions. But this works fine in bochs.
Following Brendan's suggestion, I tried to use the BIOS to read the OS loader, the modified MBR code is as follows:
section MBR vstart=0x7C00
jmp main
%include "config/boot.asm"
%include "print16.asm"
%include "config/memory-management.asm"
DISK_READ_ERROR: db "OS loader read failed, the error code is: 0x", 0
[bits 16]
main:
mov ax, 0
mov ss, ax
mov ds, ax
mov fs, ax
mov es, ax
mov sp, $$
call clean_screen
; Read OS loader via BIOS interrupt.
; Set the function number, 2 means read sector.
mov ah, 2
; Set the number of sectors to read.
mov al, LOADER_SECTORS
; Set the cylinder.
mov ch, 0
; Set the head.
mov dh, 0
; Set sector.
mov cl, LOADER_START_SECTOR
; Set the drive letter, 0 ~ 0x7F is floppy disk, 0x80 ~ 0xFF is hard disk.
mov dl, 0x80
; Set the destination address.
mov bx, LOADER_BASE_ADDR
int 0x13
; If the disk read error, the BIOS will set the CF of the flags register to 1, and AH is the error code.
jc disk_error
jmp LOADER_BASE_ADDR
disk_error:
mov [REGISTER_AX], ax
mov ax, DISK_READ_ERROR
call print
mov ax, [REGISTER_AX]
call print_hex
jmp $
times 510-($-$$) db 0
db 0x55, 0xAA
Another thing to note is that interrupts need to be disabled using the cli
command before entering the protection, otherwise in qemu this will result in an infinite restart of the computer.