disassembler
disassembler

Reputation: 31

Why does my bootloader crash after adding this line?

I am following Nick Blundell's tutorials for boot sector programming (https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf and https://www.youtube.com/watch?v=YvZhgRO7hL4). My code works just fine in my qemu emulator, however when I run it on a physical machine it will crash whenever I begin to reference the segment registers. My teachers at school are not familiar with low level programming and cannot help me. Here is my bootloader, here I have annotated the lines that cause it to crash with the string CRASH (note: when I say crash, it actually just goes on to load my OS from the next disk. I am loading this code from an external HDD) :

[bits 16]
[org 0x7c00]        

mov bp, 0xffff
mov sp, bp
mov ax, 0x0000
mov ds, ax
;; mov es, ax ;; CRASH
;; mov ss, ax ;; CRASH

mov si, BOOT_MSG
call print_string
call print_newline

mov si, INIT_SEG_MSG
call print_string
call print_newline

;; mov dx, ds ;; CRASH
;; call print_hex
;; call print_newline

;;mov dx, cs ;; CRASH
;;call print_hex
;;call print_newline

;;mov dx, es ;; CRASH
;;call print_hex
;;call print_newline

;;mov dx, ss ;; CRASH
;;call print_hex
;;call print_newline

;; mov dl, 0x80         ;; disk where kernel is
;; mov cl, 3            ;; start sect
;; mov al, 1            ;; num sect
;; mov bx, 0x7ef0       ;; RAM addr
;; call load_kernel

;; mov si, KERN_MSG
;; call print_string
;; call print_newline

;;  call switch_to_pm

jmp $

%include "print.asm"
%include "print_hex.asm"
%include "disk.asm"
%include "pm.asm"

[bits 32]
pm :
    mov esi, PM_MSG
    call print_string_pm
    jmp 0x7ef0
    jmp $
[bits 16]

BOOT_MSG : db 'booted 16-bit to 0x7c00',0
KERN_MSG : db 'loaded kernel to es 0x7ef0',0
PM_MSG : db 'switched to 32-bit mode',0
INIT_SEG_MSG : db 'init segment registers',0

times 510-($-$$) db 0
dw 0xaa55                   `

I am sure I have a fundamental misunderstanding, any help will be appreciated. Here are my printing routines:

print_string :
    push ax
    _loop :
        lodsb
        cmp al, 0
        je _end

        mov ah, 0x0e
        int 0x10
        jmp _loop
    _end :
        pop ax
        ret

print_hex :
    mov si, HEX_TEMPLATE

    mov bx, dx
    shr bx, 12
    mov bx, [bx+HEXABET]
    mov [HEX_TEMPLATE+2], bl

    mov bx, dx      ;; bx -> 0x1234
    shr bx, 8       ;; bx -> 0x0012
    and bx, 0x000f  ;; bx -> 0x0002
    mov bx, [bx+HEXABET]
    mov [HEX_TEMPLATE+3], bl

    mov bx, dx      
    shr bx, 4
    and bx, 0x00f   
    mov bx, [bx+HEXABET]
    mov [HEX_TEMPLATE+4], bl

    mov bx, dx      
    and bx, 0x0f    
    mov bx, [bx+HEXABET]
    mov [HEX_TEMPLATE+5], bl

    call print_string
    ret 

    HEX_TEMPLATE : db '0x???? ',0
    HEXABET : db '0123456789abcdef'

print_newline :
    pusha
    mov ah, 0x0e
    mov al, 0x0d
    int 0x10
    mov al, 0x0a
    int 0x10
    popa
    ret

Upvotes: 1

Views: 607

Answers (2)

disassembler
disassembler

Reputation: 31

So after many hard resets, I have found the solution. The problem was that what I thought was the problem, WASN'T THE PROBLEM. So I decided to take my flash drive and boot from my dad's pc. It worked perfectly. So I continued to write my bootloader on my own pc. I wrote the code to enter PM mode, ran it, perfect, no problems. Then I made another change. Copied the bytes to the flash drive using dd, and ran it on my dad's pc. But wait. The change didn't show up... So I ran dd a few more times, zeroed the drive, and still, nothing. So I REBOOTED MY COMPUTER, RAN DD AGAIN, AND IT WORKED. The problem seemed to be the way that my OS (Ubuntu 16, not sure if it is relevant) issued the dd command. It seems the /dev/sdb is a sort of buffer that doesn't actually get written to the disk, at least after I remove the flash drive the first time. Maybe it is a bug in the OS. Maybe it is a bug in dd. Maybe I'm missing something. Who knows. The problem (as stated in the comments) is that I was unaware of how Linux block devices work. See: sync. Apparently the changes I made were CACHED and not written. Either way I will be using an emulator from now on. Thanks everyone!

Upvotes: 2

Shift_Left
Shift_Left

Reputation: 1243

BIOS's have a peculiarity about them in that the state of CS is unknown. In Bochs and probably Qemu CS = 0, therefore code with an origin of 0x7C00 will work. Real hardware may possibly pass CS = 0x7C0, so without an appropriate far jump at the beginning of code calls to near absolute functions would be skewed by 0x7C00 bytes with the origin set to 0x7C00.

Solutions:

    org    0x7C00

    jmp    0:Begin                   ; Far jump so CS = 0
Begin:
    mov    ax, cs
    mov    ds, ax
    mov    es, ax

or

    org    0
    jmp    0x7C0:0                   ; Far jump so CS = 0x7C0

This is probably what's happening and the crash is coming @ call print_string which is actually looking for code @ 0xF8??.

Upvotes: 2

Related Questions