Esta pagina se ve mejor con JavaScript habilitado

MercuryOS Boot Sector

 ·  🎃 kr0m

Quien no ha fantaseado alguna vez en programar su propio SO, esta utopía puede llegar a materializarse con unos conocimientos mínimos sobre ensamblador y C, en este artículo se explicará el proceso de arranque de una computadora y como conseguir arrancar nuestro SO super reducido.

Para realizar todas nuestras pruebas necesitaremos un compilador de ASM y el emulador qemu, utilizaremos qemu y no un sistema de virtualización tipo VirtualBox ya que este último ejecutaría instrucciones directamente en nuestra CPU
a favor de un rendimiento superior pero el entorno puede no ser exactamente igual al de una máquina física, en cambio con un emulador si que conseguimos esto.

Dependiendo del SO instalaremos el software de una manera u otra:

FreeBSD:

pkg install nasm qemu

Gentoo:

emerge -av dev-lang/nasm app-emulation/qemu

El artículo se basa en el siguiente documento colgado en la web de la universidad de Birmingham 2 .

Antes de nada debemos tener en cuenta que cuando una computadora arranca lo primero que hace es cargar la BIOS del chip integrado en la placa a la RAM, acto seguido realiza un chequeo básico de hardware.

Una vez comprobado el hardware queda determinar desde donde arrancar, la BIOS simplemente lee los primeros 512bytes(boot sector) de todos los discos disponibles en el sistema, si alguno contiene los valores AA 55 en los bytes 511 y 512 considera que se trata de un disco bootable.

Estos primeros 512bytes se denominan boot sector, son las instrucciones que se cargarán en RAM para posteriormente ser ejecutadas por la CPU.

Nosotros vamos a crear un boot sector muy sencillo, simplemente marcaremos los bytes 511 y 512 con AA 55 para que la BIOS lo reconozca como un boot sector válido, rellenaremos 510 bytes de 0s para llegar a los 512 en total y nos
quedaremos ejecutando un bucle infinito.

Recordad que la CPU irá leyendo de la RAM ciegamente lo que hayamos cargado de los primeros 512bytes del disco, si no definiesemos el bucle infinito la CPU seguiría leyendo la RAM y ejecutando instrucciones aleatorias, restos de
la carga de programas anteriores, esto puede ser mas o menos peligroso dependiendo de las instrucciones en cuestión.

vi boot_sect_simple.asm
; Infinite loop (e9 fd ff)
loop:
    jmp loop 

; Fill with 510 zeros minus the size of the previous code
times 510-($-$$) db 0
; Magic number
dw 0xaa55

Generamos la imagen:

nasm -f bin boot_sect_simple.asm -o boot_sect_simple.bin

Podemos ver el contenido con xdd:

xxd boot_sect_simple.bin

00000000: ebfe 0000 0000 0000 0000 0000 0000 0000  ................  
00000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000120: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000130: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000140: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000150: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000160: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000170: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000180: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
00000190: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
000001a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
000001b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
000001c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
000001d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
000001e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................  
000001f0: 0000 0000 0000 0000 0000 0000 0000 55aa  ..............U.

Lo cargamos en qemu:

qemu-system-x86_64 boot_sect_simple.bin

SeaBIOS (version rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org)  
iPXE (http://ipxe.org) 00:03.0 C980 PCI2.10 PnP PMM+07F91410+07EF1410 C980  
  
Booting from Hard Disk...

Impresionante, hemos sido capaces de cargar nuestro SO desde cero.

Si te ha gustado el artículo puedes invitarme a un RedBull aquí