Como hacer un Bootloader y no morir en el intento

Cuando la computadora es encendida ( arquitectura intel IA32), el microprocesador se ecuentra operando en modo real. En la última etapa del POST el BIOS llama la int 19h, la cual carga el primer sector del disco (duro o flexible) si encuentra en el número 0xAA55 que empieza en el offset ( desplazamiento) 510.

De aparecer ese número la int 19h copia los 512 bytes del sector en la dirección de memoria 7c00:0000, luego realiza un salto incodicional a esa dirección, lo cual significa que el registro cs= 7c00 y ip=0000 por lo que realiza algo como:

 
	  
	   mov ax,7c00
	   mov cs,ax
	   jmp 0
	   
	  

Una forma realmente sencilla de ver el contenido de tu sector de arranque es con lo siguiente ( como usuario root)

	  
	  kb@cobalt:/home/labs/src/osdev/miniboot$ dd if=/dev/hda bs=1 count=512 of=mbr.bin
          kb@cobalt:/labs/labs/src/osdev/miniboot$cat mbr.bin | od -x
	  0000000 37ea c000 4307 7261 6167 646e 206f 6c65
	  0000020 5320 7369 6574 616d 2e20 2e2e 2e2e 2e2e
	  0000040 2e2e 2e2e 2e2e 2e2e 2e2e 2e2e 2e2e 2e2e
	  0000060 2e2e 2e2e 2e2e 8c00 8ec8 8ed8 bec0 0005
	  0000100 3cac 7400 b409 bb0e 0007 10cd f2eb 00b8
	  0000120 8e10 31c0 b4db b002 b501 b100 b602 b200
	  0000140 cd00 7213 eae9 0000 1000 0000 0000 0000
	  0000160 0000 0000 0000 0000 0000 0000 0000 0000
	  0000760 0000 0000 0000 0000 0000 0000 0000 aa55 <-- marca de arranque

	  
	  
La clave esta en que dd es un programa que extrae bytes raw de cualquier archivo (normal o dispositivo) basicamente los parametros dicen 512 paquetes de 1 byte leidos de /dev/hda guardarlos en el archivo mbr.bin

Si la marca de arranque no existe la int19h no copia el sector a memoria e imprime el famoso "Operating System no found"

Entonces hay que cuidar ciertos detalles de la siguiente lista:

  1. Como guardo un archivo en el primer sector de un disco?
  2. Como hago un programa de 512 bytes de tamaño exactamente
  3. Mi compilador/ensamblador genera archivos elf/coff/exe necesito unicamente el codigo binario sin formato pues al cargar a memoria el registro %ip ejecuta directamente la instruccion que se encuentre localizada en 7c00:0000.

Para resolver el problema 1:

Para resolver el problema 2:

La mayoría de los ensambladores poseen una pseudoinstrucción que permite "rellenar" el archivo generado con algun valor un número determinado de veces o hasta que el archivo tenga algun tamaño. En nuestro caso NASM, utiliza una instrucción llamada rep la cual veremos como funciona más adelante. Para resolver el problema 3:

Debemos programar el bootloader en ensamblador, en el caso de nasm la opción para generar un binario plano es llamar nasm sin opciones, ejemplo:

	
	$nasm boot.s -o bootloader.raw
	
	

En fin el programa de ejemplo aqui:

	
;archivo mboot.s
%define BUFFER 0x0600 ; comienzo de la memoria a donde se copiara a si mismo el codigo
%define PART_TABLE 446 ;localizacion de la tabla de particion dentro de este codigo
%define PENTRYSIZE 16        ; tamaoñ de una entrada de la tabla de particion
%define MAGIC 510 ; donde se encuentra AA55

global  begtext , begdata , begbss, endtext , enddata , endbss , _main

[org 0x7C00:0000 ]
        jmp start
msg     db 'Cargando el Sistema .............................',0


.data
        begdata:

        .bss
        begbss:

        .text
        begtext:
        _main:


start:
        mov ax,cs
        mov ds,ax
        mov es,ax

        mov si , msg
imprime:
        lodsb

        cmp al,0
        je rellena

        mov ah,0Eh
        mov bx,7
        int 10h

        jmp imprime

rellena:


        mov ax,1000h
        mov es, ax
        xor bx,bx


        mov ah,2
        mov al,1  ; lee  1 sector
        mov ch,0  ; cilindro 0
        mov cl,2  ;   sector  2

        mov dh , 0    ; cabeza 0
        mov dl , 0    ; drive = 0

        int 0x13

        jc rellena   ; si hay error  repite

        lgdt    [gdt_desc]

        jmp PmodeStart

        PmodeStart:

        jmp  1000h:0000




times 510-($-$$) db 0  ; repetir  0  510-(diferencia_en_bytes_inicio - actual )
dw 0AA55h              ; ultimos 2 bytes para completar  512


	
	
Este es la segunda parte del bootloader, pero no se puede copiar a memoria junto con la primera por que int 19h solo copia 512 bytes, esta copiada inmediatamente en el segundo sector y es ejecutada mediante al primera parte ( es por eso que grub tiene stage1 y stage2).
	
;archivo hello.s
mov ah, 9
mov al, '='

mov cx , 10
mov bh, 0  ; pagina de video 0
mov bl , 0xf  ; blanco

int 0x10

jmp $  ; salto aqui mismo formando un bucleº

	
	
	
kb@cobalt:/mnt/hda7/labs/src/osdev/miniboot/miniboot$ ls
boot.bin  hello.s  mboot.s boot.s
kb@cobalt:/mnt/hda7/labs/src/osdev/miniboot/miniboot$ cat boot.bin | od -x
0000000 37ea c000 4307 7261 6167 646e 206f 6c65
0000020 5320 7369 6574 616d 2e20 2e2e 2e2e 2e2e
0000040 2e2e 2e2e 2e2e 2e2e 2e2e 2e2e 2e2e 2e2e
0000060 2e2e 2e2e 2e2e 8c00 8ec8 8ed8 bec0 0005
0000100 3cac 7400 b409 bb0e 0007 10cd f2eb 00b8
0000120 8e10 31c0 b4db b002 b501 b100 b602 b200
0000140 cd00 7213 eae9 0000 1000 0000 0000 0000
0000160 0000 0000 0000 0000 0000 0000 0000 0000
*
0000760 0000 0000 0000 0000 0000 0000 0000 aa55
0001000 09b4 3db0 0ab9 b700 b300 cd0f eb10 00fe
0001017

Contenido de boot.s
kb@cobalt:/mnt/hda7/labs/src/osdev/miniboot/miniboot$ cat boot.s
%include mboot.s
%include hello.s

kb@cobalt:/mnt/hda7/labs/src/osdev/miniboot/miniboot$ nasm boot.s -o bootloader.raw
kb@cobalt:/mnt/hda7/labs/src/osdev/miniboot/miniboot$ cat boot.bin2 | od -x
0000000 37ea c000 4307 7261 6167 646e 206f 6c65
0000020 5320 7369 6574 616d 2e20 2e2e 2e2e 2e2e
0000040 2e2e 2e2e 2e2e 2e2e 2e2e 2e2e 2e2e 2e2e
0000060 2e2e 2e2e 2e2e 8c00 8ec8 8ed8 bec0 0005
0000100 3cac 7400 b409 bb0e 0007 10cd f2eb 00b8
0000120 8e10 31c0 b4db b002 b501 b100 b602 b200
0000140 cd00 7213 eae9 0000 1000 0000 0000 0000
0000160 0000 0000 0000 0000 0000 0000 0000 0000
*
0000760 0000 0000 0000 0000 0000 0000 0000 aa55<--marca que limita los 512 bytes y fin de mboot.s
0001000 09b4 3db0 0ab9 b700 b300 cd0f eb10 00fe<--codigo extra empieza en segundo sector
0001017 |
        |------inicio del codigo de archivo hello.s

	
	

Obviamente faltan muchas cosas que explicar pero ya puse algo de código asi que pueden guiarse. Por favor si tienes algun comentario hazlo y mandame un mail siempre es grato comunicarse con alguien interesado en lo mismo que a uno.