I have written simple first stage bootloader which displays \"Hello world\" using interrupt to bios. Now as a next obvious step to write a second stage, but where code for t
On x86 you would do the following (simplified):
retf
). A better alternative is to search the filesystem for a certain filename (e.g. KERNEL.BIN) -- but you'd need to know the file system type (e.g. FAT12 if you're testing from a floppy image).EXTERN _mykernel
(for example) and calls that symbol.mykernel
.Okay that was a short overview of what I did a few years ago (with lots of copy&paste from the Internet ;). If that isn't helpful, here are some good web resources on OS development:
Hope that helps ^^
Look at the GRUB implementation here (stage 1):
http://src.illumos.org/source/xref/illumos-gate/usr/src/grub/grub-0.97/stage1/stage1.S
First noticed the starting point at 0x7c00 and the end signature of 0xaa55 for this first sector. From within the disassembly, u can see this:
349 copy_buffer: 350 movw ABS(stage2_segment), %es 351 352 /* 353 * We need to save %cx and %si because the startup code in 354 * stage2 uses them without initializing them. 355 */ 356 pusha 357 pushw %ds 358 359 movw $0x100, %cx 360 movw %bx, %ds 361 xorw %si, %si 362 xorw %di, %di 363 364 cld 365 366 rep 367 movsw 368 369 popw %ds 370 popa 371 372 /* boot stage2 */ 373 jmp *(stage2_address) 374 375 /* END OF MAIN LOOP */ 376
Essentially the logic is to copy the stage 2 code into another part of memory, and after that jump directly there, and that is "boot stage2". In other words, "boot stage1" is effectively triggered from BIOS after it has loaded the sector into memory, whereas stage2 is where you jump there - it can be anywhere.
Minimal runnable NASM BIOS example that loads stage 2 and jumps to it
use16
org 0x7C00
; You should do further initializations here
; like setup the stack and segment registers.
; Load stage 2 to memory.
mov ah, 0x02
; Number of sectors to read.
mov al, 1
; This may not be necessary as many BIOS set it up as an initial state.
mov dl, 0x80
; Cylinder number.
mov ch, 0
; Head number.
mov dh, 0
; Starting sector number. 2 because 1 was already loaded.
mov cl, 2
; Where to load to.
mov bx, stage2
int 0x13
jmp stage2
; Magic bytes.
times ((0x200 - 2) - ($ - $$)) db 0x00
dw 0xAA55
stage2:
; Print 'a'.
mov ax, 0x0E61
int 0x10
cli
hlt
; Pad image to multiple of 512 bytes.
times ((0x400) - ($ - $$)) db 0x00
Compile and run:
nasm -f bin -o main.img main.asm
qemu-system-i386 main.img
Expected outcome: a
gets printed to the screen, and then the program halts.
Tested on Ubuntu 14.04.
Saner GAS example using a linker script and more correct initialization (segment registers, stack) on my GitHub.