How to load second stage boot loader from first stage?

On x86 you would do the following (simplified):

  • Have the bootloader load the n-th sector of the disk/floppy (wherever you're booting from) into memory and execute it (i.e. load segment/offset and do 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).
  • The kernel would then start in real mode. It sets up code descriptors, GDT, and so on, activates 32-bit addressing (you should have heard of "A20") and finally enters protected mode. Then you need a far jump to a 32-bit code segment (kernel file must be linked together in a way that the 32-bit code is at an absolute position, e.g. at offset 512, right after the 16-bit real mode stuff).
  • The 32-bit kernel assembly, then, just defines EXTERN _mykernel (for example) and calls that symbol.
  • Then you can begin writing your kernel as C function 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:

  • http://www.brokenthorn.com/Resources/OSDevIndex.html
  • http://wiki.osdev.org/Main_Page
  • http://lowlevel.brainsware.org/wiki/index.php/Hauptseite (wiki with many hobbyist OS developers, German only...)

Hope that helps ^^


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.