I am currently trying to make the second stage of a bootloader in order to enable 32-bit protected mode. I have written some x86 assembly for the NASM assembler to do so, but when I compiled and ran the code (in a disk image using QEMU emulator), it does not work. When the computer executes the jmp 0x08:protected_mode instruction, it crashes and resets my emulator. I am very new to low-level programming like this, so please bare with me.
EDIT: This code is not from the bootloader; rather, it the the second stage of it and is properly loaded into memory through BIOS interrupts at physical address 0x10000
Here is the code for the bootloader (bootloader.asm):
org 0x7c00
[bits 16]
start:
jmp boot
load_msg db "bootloader: loaded", 0ah, 0dh, 0h
diskerr_msg db "bootloader: disk read failed", 0ah, 0dh, 0h
jmperr_msg db "bootloader: kernel jump failed", 0ah, 0dh, 0h
CursorX db 0
CursorY db 0
boot:
cli
cld
call clear_screen
mov si, load_msg
call print
mov ax, 0x1000
mov es, ax
xor bx, bx
mov al, 17
mov ch, 0
mov cl, 2
mov dh, 0
mov dl, 0
mov ah, 0x02
int 0x13
jc disk_error
mov ax, 0x1000
mov es, ax
mov bx, 17 * 512
mov ah, 0x02
mov al, 8
mov ch, 0
mov cl, 1
mov dh, 1
mov dl, 0
int 0x13
jc disk_error
; mov ax, 0x3000
; mov es, ax
; xor bx, bx
; mov ah, 0x02
; mov al, 31
; mov ch, 0
; mov cl, 9
; mov dh, 1
; mov dl, 0
; int 0x13
; jc disk_error
jmp 0x1000:0x0000
disk_error:
mov si, diskerr_msg
call print
hlt
clear_screen:
mov ah, 06h
mov al, 0
mov bh, 07h
mov cx, 0
mov dx, 184fh
int 10h
mov byte [CursorX], 0
mov byte [CursorY], 0
call mov_cursor
ret
mov_cursor:
mov ah, 02h
mov bh, 0
mov dl, [CursorX]
mov dh, [CursorY]
int 10h
ret
put_char:
mov ah, 0eh
mov bh, 0
int 10h
inc byte [CursorX]
call mov_cursor
ret
print:
.loop:
lodsb
or al, al
jz .done
call put_char
jmp .loop
.done:
ret
times 510 - ($ - $$) db 0
dw 0xAA55
Here is the code for the second stage of the bootloader (bootloader2.asm):
org 0x10000
[bits 16]
start:
jmp real_mode
gdt32_start:
dd 0x00000000
dd 0x00000000
dd 0x0000FFFF
dd 0x00CF9A00
dd 0x0000FFFF
dd 0x00CF9200
gdt32_end:
align 4
gdt32_location:
dw gdt32_end - gdt32_start - 1
dd gdt32_start
real_mode:
cli
lgdt [gdt32_location]
in al, 0x92
or al, 0x02
out 0x92, al
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp 0x08:protected_mode ; jump error right here
[bits 32]
protected_mode:
hlt
jmp $
Here are the commands to compile the assembly:
nasm -f bin bootloader.asm -o bootloader.o
nasm -f bin bootloader2.asm -o bootloader2.o
Here are the commands to build a disk image:
dd if=/dev/zero of=disk.img bs=512 count=2880
dd conv=notrunc if=bootloader.o of=disk.img bs=512 count=1 seek=0
dd conv=notrunc if=bootloader2.o of=disk.img bs=512 count=25 seek=1
Here is the command to run the OS (with QEMU emulator):
qemu-system-i386 -machine q35 -fda disk.img -gdb tcp::26000 -S
I do not even know where to start trying to fix this; that is why I am asking my question here. I have checked most of my code and everything is working (registers are correct values, etc.), but the jump instructions seems to crash the emulator every time. I feel that the only thing it could be is a bad GDT, but I think I made it properly.
I have already tried using the org directive, which did not work.
ORGdirective, and have not initialized the segment registers. So any attempt to access data by absolute address is going to fail, since the addresses generated by the assembler won't be right. TryORG 0x7c00and initializingDSto zero when you start in real mode.jmp real_modewill jump to uninitialized memory. It's your responsibility to ensure the first 512 bytes contains code to read from the disk and load everything else.