I am trying to finish the first part of my operating system project. This part involves running a boot loader assembly file through the emulator qemu
and having that boot loader load and execute an arbitrary amount (hopefully over 512 bytes) of my kernel.
I'm having problems accomplishing this, I can't pinpoint it exactly but I've possibly narrowed it down to two things.
One, I am lacking in my understanding of memory addresses, and using the stack to load my kernel and execute it.
Two, I am copying my two files (the boot loader and the kernel files) incorrectly into the floppy disk image.
Here is my code for the first part, the boot loader. boot.asm
;;;
;;; Header information
;;;
BITS 16
ORG 0
START: jmp MAIN
;;;
;;; Parameters
;;;
SectorsPerTrack dw 18
HeadsPerTrack dw 2
BytesPerSector dw 512
DriveNumber db 0
;;;
;;; Variables
;;;
absSector db 0x00
absHead db 0x00
absTrack db 0x00
;;;
;;; Print function
;;; Input:
;;; - SI => Null terminated string
;;; Output:
;;; - None
;;;
PRINT:
lodsb
or al, al
jz PRINTDONE
int 10h
jmp PRINT
PRINTDONE:
ret
;;;
;;; LBA to CHS
;;; Input:
;;; - AX => LBA address
;;; - SectorsPerTrack => Sectors per track
;;; - HeadsPerTrack => Heads per track
;;; Output:
;;; - absSector => CHS sector address
;;; - absHead => CHS head address
;;; - absTrack => CHS track address
;;;
LBACHS:
xor dx, dx
div WORD [SectorsPerTrack]
inc dl
mov BYTE [absSector], dl
xor dx, dx
div WORD [HeadsPerTrack]
mov BYTE [absHead], dl
mov BYTE [absTrack], al
ret
;;;
;;; Read sectors
;;; Input:
;;; - CX => Number of sectors to read
;;; - AX => LBA address to start from
;;; Output:
;;; - ES:BX => Loaded sector address:offset
;;;
READSECTORS:
READSECTORSMAIN:
mov di, 0x0005
READSECTORSLOOP:
push ax
push bx
push cx
call LBACHS
mov ah, 0x02
mov al, 0x01
mov ch, BYTE [absTrack]
mov cl, BYTE [absSector]
mov dh, BYTE [absHead]
mov dl, BYTE [DriveNumber]
int 0x13
jnc READSECTORSDONE
xor ax, ax
int 0x13
dec di
pop cx
pop bx
pop ax
jnz READSECTORSLOOP
int 0x18
READSECTORSDONE:
pop cx
pop bx
pop ax
add bx, WORD [BytesPerSector]
inc ax
loop READSECTORSMAIN
ret
;;;
;;; Main section
;;;
MAIN:
cli ; Move registers for offset of BIOS 0x07C0 load point
mov ax, 0x07C0 ; OFFSET
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ax, 0x0000 ; Initialize the stack
mov ss, ax
mov sp, 0xFFFF
sti
mov ax, 0x01 ; LBA number 1 for sector number 2
mov cx, 0x01 ; Read one sector from the floppy disk
call READSECTORS ; Call the read sectors function
jmp [es:bx] ; Address ES offset BX returned from read sectors
;;;
;;; Footer information
;;;
times 510-($-$$) db 0
dw 0xAA55
Here is my code for the second part, the kernel. kernel.asm
BITS 16
ORG 0
START: jmp MAIN
message db 'Kernel Loaded', 13, 10, 0
PRINT:
lodsb
or al, al
jz PRINTDONE
int 10h
jmp PRINT
PRINTDONE:
ret
MAIN:
mov si, message
call PRINT
jmp $
times 510-($-$$) db 0
dw 0xAA55
Here is how I am compiling, copying, and emulating both files and their binaries, I am running on Ubuntu Desktop x64.
nasm -f bin -o boot.bin boot.asm
nasm -f bin -o kernel.bin kernel.asm
cat kernel.bin >> boot.bin
dd status=noxfer conv=notrunc if=boot.bin of=floppy.fda
qemu-system-x86_64 -fda floppy.fda
If you attempt to run, compile, and emulate this. You will see that the kernel is never loaded and does not print out what it should.
I have no idea what to do beyond this point, I have tried researching how to properly load second stage boot loaders into a floppy image and came up short. I have also hit a brick wall with what I might be doing wrong in loading my kernel from my boot loader.
If you have any advice or fixes, it would be greatly appreciated. Also, if there are any errors in my code or anything you need clarified in order to better help me, please don't hesitate to ask.
EDIT
Here are the tutorials I've been using to create this code if you need to reference them.
http://www.brokenthorn.com/Resources/
int 13/02
expects es:bx
as input, but you only set up es
, bx
is uninitialized. You should set it to point where you want to load your data, careful not to overwrite your own boot sector. As such, mov bx, 512
sounds like a good idea.
Another problem is that your READSECTORS
returns the final value of bx
, that is the next available address and not the original load address.
Also, jmp [es:bx]
will not jump to es:bx
, rather, it will fetch a pointer from that address and jump wherever that points to. You also need to consider that your kernel also used org 0
so you should do a far jump using an offset of 0
. If you load your kernel after the boot sector, that means jmp 0x7e0:0
should do the trick. Just spotted that your kernel also expects ds
to be set up as well.
PS: you should set up a debugging environment, otherwise you will have a hard time fixing problems.
Here is a working version:
;;;
;;; Header information
;;;
BITS 16
ORG 0
START: jmp MAIN
;;;
;;; Parameters
;;;
SectorsPerTrack dw 18
HeadsPerTrack dw 2
BytesPerSector dw 512
DriveNumber db 0
;;;
;;; Variables
;;;
absSector db 0x00
absHead db 0x00
absTrack db 0x00
;;;
;;; Print function
;;; Input:
;;; - SI => Null terminated string
;;; Output:
;;; - None
;;;
PRINT:
lodsb
or al, al
jz PRINTDONE
mov ah, 0eh
int 10h
jmp PRINT
PRINTDONE:
ret
;;;
;;; LBA to CHS
;;; Input:
;;; - AX => LBA address
;;; - SectorsPerTrack => Sectors per track
;;; - HeadsPerTrack => Heads per track
;;; Output:
;;; - absSector => CHS sector address
;;; - absHead => CHS head address
;;; - absTrack => CHS track address
;;;
LBACHS:
xor dx, dx
div WORD [SectorsPerTrack]
inc dl
mov BYTE [absSector], dl
xor dx, dx
div WORD [HeadsPerTrack]
mov BYTE [absHead], dl
mov BYTE [absTrack], al
ret
;;;
;;; Read sectors
;;; Input:
;;; - CX => Number of sectors to read
;;; - AX => LBA address to start from
;;; Output:
;;; - ES:BX => Loaded sector address:offset
;;;
READSECTORS:
READSECTORSMAIN:
mov di, 0x0005
READSECTORSLOOP:
push ax
push bx
push cx
call LBACHS
mov ah, 0x02
mov al, 0x01
mov ch, BYTE [absTrack]
mov cl, BYTE [absSector]
mov dh, BYTE [absHead]
mov dl, BYTE [DriveNumber]
int 0x13
jnc READSECTORSDONE
xor ax, ax
int 0x13
dec di
pop cx
pop bx
pop ax
jnz READSECTORSLOOP
int 0x18
READSECTORSDONE:
pop cx
pop bx
pop ax
add bx, WORD [BytesPerSector]
inc ax
loop READSECTORSMAIN
ret
;;;
;;; Main section
;;;
MAIN:
cli ; Move registers for offset of BIOS 0x07C0 load point
mov ax, 0x07C0 ; OFFSET
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ax, 0x0000 ; Initialize the stack
mov ss, ax
mov sp, 0xFFFF
sti
mov ax, 0x01 ; LBA number 1 for sector number 2
mov cx, 0x01 ; Read one sector from the floppy disk
mov bx, 0x200
call READSECTORS ; Call the read sectors function
jmp 0x7e0:0
;;;
;;; Footer information
;;;
times 510-($-$$) db 0
dw 0xAA55
kernel:
BITS 16
ORG 0
START: jmp MAIN
message db 'Kernel Loaded', 13, 10, 0
PRINT:
lodsb
or al, al
jz PRINTDONE
mov ah, 0eh
int 10h
jmp PRINT
PRINTDONE:
ret
MAIN:
mov ax, cs
mov ds, ax
mov si, message
call PRINT
jmp $
times 512-($-$$) db 0
The example code uses an org of 0, it probably should be an org of 7c00h. PC's load the partition or boot sector at hex 0000:7c00. In the case of Microsoft partition sectors, the code moves itself down to hex 0000:0600, jumps to some point after 0000:0600 to continue, and eventually loads a boot sector at hex 0000:7c00.
Also, does version of nasm that you are using produce 16 bit real mode code?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.