|
|
Line 1: |
Line 1: |
- | =Usando GRUB como bootloader=
| + | Esta página ha sido movida a mi nueva wiki :) puedes verla en [http://wiki.todoprogra.com/index.php?title=Kernel_usando_GRUB Usando GRUB]. |
- | | + | |
- | GRUB es un poderoso bootloader capaz de cargar un SO de 32-bit desde un floppy, un disco duro, a travez de la red, etc.
| + | |
- | | + | |
- | El desarrollo de un SO debe ser fácil como crear cualquier archivo ejecutable, GRUB se encarga de eso, nos permite crear un solo archivo ejecutable en cualquiera de los formatos reconocidos y se encarga de preparar el sistema para cargarlo en la memoria y ejecutarlo. Claro que no hace todo por nosotros, aún quedan algunas cosas pendientes, pero nos ahorra bastante trabajo (escribir un bootloader no es tarea fácil ni demasiado emocionante).
| + | |
- | | + | |
- | Algunas de las cosas que GRUB hará por nosotros:
| + | |
- | *Entrar en PMode (Protected Mode) y mapear 4GB de memoria para nuestro kernel
| + | |
- | **Todo lo que eso conlleva (A20...)
| + | |
- | *Cargar la imagen del kernel por nosotros y ponerla en la memoria
| + | |
- | *Poner el modo de video que deseamos (próximo tutorial)
| + | |
- | | + | |
- | Para que nuestro SO sea capaz de cargar con GRUB debe tener el Multiboot Header (cabecera).
| + | |
- | Eso nos permite usar cualquier bootloader capaz de cargar un SO con un multiboot header.
| + | |
- | | + | |
- | Algunas de las cosas que tendremos que hacer luego son:
| + | |
- | *Mover nuestro Stack (pila)
| + | |
- | *Instalar un nuevo GDT
| + | |
- | *También el IDT
| + | |
- | *Manejar paginados (paging)
| + | |
- | *Manejar IRQ
| + | |
- | *Etc
| + | |
- | | + | |
- | ==Multiboot Header==
| + | |
- | | + | |
- | Esta cabecera debe estar contenida completamente en los primeros 8192 bytes de la imagen del SO. Por lo general mientras más pronto se encuentra mejor.
| + | |
- | | + | |
- | El layout de la cabecera es así:
| + | |
- | | + | |
- | <pre>
| + | |
- | Offset Tipo Campo Nota
| + | |
- | 0 u32 magic requerido
| + | |
- | 4 u32 flags requerido
| + | |
- | 8 u32 checksum requerido
| + | |
- | 12 u32 header_addr si la bandera[16] está puesta
| + | |
- | 16 u32 load_addr si la bandera[16] está puesta
| + | |
- | 20 u32 load_end_addr si la bandera[16] está puesta
| + | |
- | 24 u32 bss_end_addr si la bandera[16] está puesta
| + | |
- | 28 u32 entry_addr si la bandera[16] está puesta
| + | |
- | 32 u32 mode_type si la bandera[2] está puesta
| + | |
- | 36 u32 width si la bandera[2] está puesta
| + | |
- | 40 u32 height si la bandera[2] está puesta
| + | |
- | 44 u32 depth si la bandera[2] está puesta
| + | |
- | </pre>
| + | |
- | | + | |
- | Como se puede ver en la tabla solo las primeras 3 son requeridas.
| + | |
- | | + | |
- | + El campo 'magic' es el número mágico que identifica la cabecera, debe ser el valor hexadecimal: 0x1BADB002.
| + | |
- | | + | |
- | + El campo 'flags' indica que es lo que necesita del bootloader para funcionar bien, los bits 0 al 15 indican los requerimientos, si el bootloader no entiende alguna de estas banderas o no puede cumplir con los requerimientos entonces no carga el SO y avisa al usuario. Los bits 16 al 31 indican otras opciones para el bootloader.
| + | |
- | Si el bit 0 está puesto todos los modulos cargados con el sistema operativo deben estar alineados a 4bytes.
| + | |
- | Si el bit 1 está puesto entonces la información disponible al menos por la via de los campos 'mem_' de la estructura de información del multiboot debe ser incluida (ver acá). Si el bootloader es capaz de pasar un mapa de memoria (campos 'mmap_') entonces debe pasarlo.
| + | |
- | Si el bit 2 está puesto la información acerca de la tabla de modo de video debe estar disponible para el kernel.
| + | |
- | SI el bit 16 está puesto los campos con offset 12 al 28 en el multiboot header son validos y el bootloader debe usarlos en lugar de los campos actuales en lugar del formato del ejecutable actual para calcular en dónde cargar el SO. Esto es necesario si el ejecutable no está en formato ELF sino en un a.out estandar o algún otro tipo de ejecutable.
| + | |
- | | + | |
- | + El campo checksum es un valor de 32 bits sin signo que al sumarlo a los otros campos (magic+flags) debe tener una suma de 32bits sin signo de 0 (cero).
| + | |
- | | + | |
- | ==Creando un Kernel con la cabecera Multiboot==
| + | |
- | | + | |
- | Para que nuestro kernel funcione con GRUB debe cumplir ciertos requisitos:
| + | |
- | *Ser un kernel de 32bit
| + | |
- | *Tener el Multiboot Header
| + | |
- | **Debe estar en los primeros 8kb del kernel
| + | |
- | | + | |
- | Este es un ejemplo de un kernel que cumple con los requisitos, los archivos son:
| + | |
- | *boot.asm
| + | |
- | *main.c
| + | |
- | *link.ld
| + | |
- | *build.sh
| + | |
- | | + | |
- | Claro que el kernel no hace aún nada, más adelante agregaremos una consola y quien sabe quizá hasta una interfaz de usuario.
| + | |
- | | + | |
- | ====''boot.asm :''====
| + | |
- | <pre>
| + | |
- | ;--------------------------;
| + | |
- | ; NULL OS (boot.asm) ;
| + | |
- | ; version: 0.0.1 ;
| + | |
- | ; autor: movaxes ;
| + | |
- | ; fecha: 6/2/07 ;
| + | |
- | ;--------------------------;
| + | |
- | | + | |
- | [BITS 32]
| + | |
- | | + | |
- | global boot ;donde inicia nuestro kernel
| + | |
- | extern _main ;esta funcion se encuentra en main.c
| + | |
- | | + | |
- | ;Multiboot Header
| + | |
- | | + | |
- | MULTIBOOT_PAGE_ALIGN equ 1<<0
| + | |
- | MULTIBOOT_MEMORY_INFO equ 1<<1
| + | |
- |
| + | |
- | MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
| + | |
- | MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO
| + | |
- | MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
| + | |
- | | + | |
- | SECTION .text
| + | |
- | ALIGN 4
| + | |
- | multiboot_header:
| + | |
- | | + | |
- | dd MULTIBOOT_HEADER_MAGIC
| + | |
- | dd MULTIBOOT_HEADER_FLAGS
| + | |
- | dd MULTIBOOT_CHECKSUM
| + | |
- | | + | |
- | ;nuestro stack (pila) de 16kb:
| + | |
- | | + | |
- | KERNEL_STACK equ 0x4000
| + | |
- | | + | |
- | boot:
| + | |
- | | + | |
- | mov esp,stack+KERNEL_STACK ;apuntamos a nuestra pila
| + | |
- |
| + | |
- | push eax ;ponemos en la pila MULTIBOOT_HEADER_MAGIC
| + | |
- | push ebx ;ponemos en la pila MULTIBOOT_MEMORY_INFO
| + | |
- |
| + | |
- | call _main ;llamamos a main en main.c
| + | |
- | hlt ;detiene el CPU
| + | |
- |
| + | |
- | SECTION .bss
| + | |
- | ALIGN 32
| + | |
- | stack:
| + | |
- | | + | |
- | resb KERNEL_STACK
| + | |
- | | + | |
- | </pre>
| + | |
- | | + | |
- | ====''main.c :''====
| + | |
- | <pre>
| + | |
- | /*--------------------------;
| + | |
- | ; NULL OS (main.c) ;
| + | |
- | ; version: 0.0.1 ;
| + | |
- | | + | |
- | ; autor: movaxes ;
| + | |
- | ; fecha: 6/2/07 ;
| + | |
- | ;--------------------------*/
| + | |
- | | + | |
- | //esta es nuestra funcion _main llamada desde boot.asm
| + | |
- | //los argumentos que pasamos son:
| + | |
- | //MULTIBOOT_HEADER_MAGIC
| + | |
- | //MULTIBOOT_MEMORY_INFO
| + | |
- | //los cuales estan en la pila (stack)
| + | |
- | void _main(void* mm, unsigned int magic)
| + | |
- | {
| + | |
- | //TODO: todo!!! :P
| + | |
- | }
| + | |
- | </pre>
| + | |
- | | + | |
- | ====''link.ld :''====
| + | |
- | <pre>
| + | |
- | ENTRY (boot)
| + | |
- | SECTIONS{
| + | |
- | . = 0x00100000;
| + | |
- | | + | |
- | .text :{
| + | |
- | *(.text)
| + | |
- | }
| + | |
- | | + | |
- | .rodata ALIGN (0x1000) : {
| + | |
- | *(.rodata)
| + | |
- | }
| + | |
- | | + | |
- | .data ALIGN (0x1000) : {
| + | |
- | *(.data)
| + | |
- | }
| + | |
- | | + | |
- | .bss : {
| + | |
- | _sbss = .;
| + | |
- | *(COMMON)
| + | |
- | *(.bss)
| + | |
- | _ebss = .;
| + | |
- | }
| + | |
- | }
| + | |
- | </pre>
| + | |
- | | + | |
- | ====''buil.sh :''====
| + | |
- | <pre>
| + | |
- | #!/bin/bash
| + | |
- | echo --- compilando *.asm
| + | |
- | nasm -f elf -o boot.o boot.asm
| + | |
- | echo --- compilando *.c
| + | |
- | gcc -o main.o -c main.c -Wall -Werror -nostdlib -nostartfiles -nodefaultlibs
| + | |
- | echo --- linking *.o
| + | |
- | ld -T link.ld -o kernel.bin boot.o main.o
| + | |
- | echo --- EXITO!!
| + | |
- | echo --- borrando *.o
| + | |
- | rm *.o
| + | |
- | echo --- FIN
| + | |
- | </pre>
| + | |
- | | + | |
- | ====''Compilando el Kernel''====
| + | |
- | | + | |
- | Para compilar nuestro binario kernel.bin solo debes poner en el terminal:
| + | |
- | <pre>$sh build.sh</pre>
| + | |
- | y listo, ahora sigue las instrucciones para hacer una imagen con tu kernel y probarlo (abajo).
| + | |
- | | + | |
- | ==¿Cómo cargar nuestro kernel con GRUB?==
| + | |
- | ===¿Cómo crear una imagen con GRUB y el Kernel?===
| + | |
- | | + | |
- | ''Necesitas tener instalado GRUB (si tienes linux en tu computadora lo mas seguro es que ya lo tienes).''
| + | |
- | | + | |
- | Primero necesitamos crear una imagen de 1.44MiB del floppy:
| + | |
- | <pre>$dd if=/dev/zero of=floppy.img bs=1024 count=1440</pre>
| + | |
- | | + | |
- | Ahora un loopback:
| + | |
- | <pre>$sudo losetup /dev/loop1 floppy.img</pre>
| + | |
- | | + | |
- | Ahora creamos nuestro sistema de ficheros Ext2fs:
| + | |
- | <pre>$sudo mkfs /dev/loop1</pre>
| + | |
- | | + | |
- | Lo montamos:
| + | |
- | <pre>$sudo mount -o loop /dev/loop1 /mnt</pre>
| + | |
- | | + | |
- | Ahora copiamos grub a la imagen montada (debes buscar stage1 y stage2 en tu sistema de ficheros, normalmente está en /boot/grub):
| + | |
- | <pre>$sudo mkdir -p /mnt/boot/grub</pre>
| + | |
- | <pre>$sudo cp /boot/grub/stage1 /boot/grub/stage2 /mnt/boot/grub/</pre>
| + | |
- | | + | |
- | Creamos un archivo de texto con la configuración para GRUB (lo llamamos: ''grub.conf''):
| + | |
- | <pre>$sudo gedit /mnt/boot/grub/menu.lst</pre>
| + | |
- | Una vez lo tienes abierto escribes:
| + | |
- | <pre>
| + | |
- | # Configuración de GRUB
| + | |
- | | + | |
- | title = Kernel de Prueba | + | |
- | root (fd0)
| + | |
- | kernel /kernel.bin
| + | |
- | </pre>
| + | |
- | | + | |
- | Desmontamos la imagen:
| + | |
- | <pre>$sudo umount /mnt</pre>
| + | |
- | | + | |
- | Instalamos grub en la imagen:
| + | |
- | <pre>$sudo grub --device-map=/dev/null</pre>
| + | |
- | En la interfaz de GRUB ingresa los comandos:
| + | |
- | <pre>
| + | |
- | grub> device (fd0) /dev/loop1
| + | |
- | grub> root (fd0)
| + | |
- | grub> setup (fd0)
| + | |
- | grub> quit
| + | |
- | </pre>
| + | |
- | | + | |
- | Quitamos el loopback:
| + | |
- | <pre>$sudo losetup -d /dev/loop1</pre>
| + | |
- | | + | |
- | Listo!!!
| + | |
- | | + | |
- | ===¿Cómo agregar un kernel a la imagen?===
| + | |
- | | + | |
- | Primero montas la imagen:
| + | |
- | <pre>
| + | |
- | $sudo losetup /dev/loop1 floppy.img
| + | |
- | $sudo mount -o loop /dev/loop1 /mnt
| + | |
- | </pre>
| + | |
- | | + | |
- | Ahora puedes editar el menu de grub (/boot/grub/menu.lst) o agregar tu kernel (kernel.bin), etc. <br>
| + | |
- | Para grabar tu kernel.bin puedes usar este comando:
| + | |
- | <pre>$sudo cp kernel.bin /mnt</pre>
| + | |
- | | + | |
- | Para editar el menu de GRUB usa el comando:
| + | |
- | <pre>$sudo gedit /mnt/boot/grub/menu.lst</pre>
| + | |
- | | + | |
- | Por ultimo desmontas la imagen:
| + | |
- | <pre>
| + | |
- | $sudo umount /mnt
| + | |
- | $sudo losetup -d /dev/loop1
| + | |
- | </pre>
| + | |
- | | + | |
- | ===¿Cómo grabar nuestra imagen a un floppy real?===
| + | |
- | | + | |
- | Si quieres probar tu kernel en una computadora real (no usando BOCHS) debes escribir este comando (luego de poner un floppy en el driver):
| + | |
- | <pre>$sudo dd if=floppy.img of=/dev/fd0</pre>
| + | |
- | | + | |
- | Listo, ahora puedes reiniciar tu computadora desde el floppy.
| + | |
- | | + | |
- | ===¿Cómo cargar la imagen en BOCHS?===
| + | |
- | | + | |
- | Primero copia tu floppy.img (imagen del floppy) en tu folder de usuario.
| + | |
- | | + | |
- | Para probar el kernel en BOCHS necesitas editar el archivo ''.bochsrc'':<br>
| + | |
- | - si instalaste bochs desde Ubuntu (añadir y quitar aplicaciones) o con apt-get tu archivo 'bochscr' puede estar en ''/usr/share/doc/bochs/examples'' dentro del archivo comprimido ''bochsrc.gz'', extrae el archivo en tu directorio de usuario con el nombre ''.bochscr''
| + | |
- | - si no lo encuentras puedes buscar en: /usr/share/doc/bochs o mira el archivo de ayuda en tu bochs (''readme'') para más información.
| + | |
- | | + | |
- | Ahora abre el archivo en tu editor de texto y edita las líneas que dicen 'floppya...' y 'boot =...' para que queden:
| + | |
- | <pre>
| + | |
- | floppya: 1_44=floppy.img, status=inserted
| + | |
- | boot: floppy
| + | |
- | </pre>
| + | |
- | | + | |
- | Abres bochs:
| + | |
- | <pre>sudo bochs</pre>
| + | |
- | | + | |
- | Listo!! Bochs debe estar mostrando a GRUB iniciando con tu menu.
| + | |
- | | + | |
- | ===¿Cómo cargar un Kernel en el GRUB instalado en Linux?===
| + | |
- | | + | |
- | Si ya tienes instalado alguna distro de Linux en tu computadora (con GRUB) y quieres probar tu SO en hardware real, esto es lo que debes hacer:
| + | |
- | | + | |
- | Primero abre el archivo del menu de GRUB para agregar nuestro kernel usando el comando en un terminal (ejemplo usando UBUNTU):
| + | |
- | | + | |
- | <pre>$ sudo gedit /boot/grub/menu.lst</pre>
| + | |
- | | + | |
- | Ahora que tenemos abierto el archivo en gedit vamos a agregar al final:
| + | |
- | | + | |
- | <pre>
| + | |
- | title Kernel de Prueva
| + | |
- | root (fd0)
| + | |
- | kernel /boot/kernel.bin
| + | |
- | boot
| + | |
- | </pre>
| + | |
- | | + | |
- | Eso agrega al menu de GRUB la opción de cargar nuestro kernel de prueva desde un floppy (fd0):<br>
| + | |
- | '''title''' es el nombre que aparecerá en el menu (puedes poner lo que quieras)<br>
| + | |
- | '''root''' es el lugar en donde está el kernel (fd0 es nuestro floppy)<br>
| + | |
- | '''kernel''' le indica a GRUB en donde se encuentra el kernel (en este caso en un folder llamado 'boot' del floppy)
| + | |
- | | + | |
- | Eso es todo, cuando tengas un kernel listo solo lo grabas en el directorio ''boot'' del floppy y reinicias la computadora, cuando veas la opción en el menu de GRUB la seleccionas y presions ENTER, y ya está!!
| + | |
- | | + | |
- | <br>
| + | |
- | <br>
| + | |