19 agosto 2020

Emulando el Raspberry Pi OS con QEMU en Linux

QEMU es una herramienta de emulación poderosa. Actualmente nos permite probar una infinidad de herramientas y plataformas. Desarrollar para dispositivos embebidos tiene sin embargo ciertos retos. En esta entrada explicaré una forma fácil de emular una Raspberry Pi para simplificar el trabajo de desarrollo.

 Contenido:

Opciones para desarrollar en la Raspberry Pi

La forma más "fácil" obviamente pasa por comprar una Raspberry Pi, conectar un monitor, teclado, mouse y ponerse a trabajar. Aunque las versiones más recientes son relativamente "potentes" trabajar directamente en la Raspberry puede ser incómodo debido a los cables y accesorios. Una segunda opción es conectarla a la red y accederla vía SSH esto nos reduce el número de cables y conexiones pero aún requiere que tengamos una tarjeta conectada y configurada.

Una tercera opción es la que exploraremos en esta entrada. Utilizaremos el emulador QEmu para poder arrancar una imagen del Raspberry Pi OS. Esto nos permitirá emular una (o varias Raspberrys) sin necesidad de tener un montón de cables. La idea es que podamos usar el emulador para acelerar y facilitar el desarrollo y luego, cuando tengamos nuestro sistema listo, podemos migrar nuestros programas y configuraciones a una Raspberry Pi física.

Instalando QEMU

Ejecuta alguno de los siguientes comandos dependiendo de tu distribución:

  • Arch: pacman -S qemu
  • Debian/Ubuntu: apt-get install qemu
  • Fedora: dnf install @virtualization
  • Gentoo: emerge --ask app-emulation/qemu
  • RHEL/CentOS: yum install qemu-kvm
  • SUSE: zypper install qemu

También puedes compilar qemu desde el código fuente si tu distribución no está directamente soportada.

Descarga una versión reciente de Raspberry Pi OS

En este ejemplo vamos a utilizar Raspberry Pi OS Lite, aunque también puedes utilizar la versión completa.

Primero crea un directorio de trabajo para que sea más fácil trabajar con los archivos que utilizaremos.

mkdir qemu-raspberrypi
cd qemu-raspberrypi

Adentro de ese directorio descarga la versión más reciente del Raspberry Pi OS con el siguiente comando:

unzip raspios_lite_armhf_latest.zip

Nota: El nombre del archivo será algo como esto "2020-05-27-raspios-buster-lite-armhf.img".

Descomprime ahora la imagen descargada. Esto creará un archivo ".img" que utilizaremos como la imagen para emular

wget https://downloads.raspberrypi.org/raspios_lite_armhf_latest -O raspios_lite_armhf_latest.zip

Descargando un Kernel Linux reciente para QEmu

El siguiente paso es descargar un Kernel de Linux compatible con el emulador QEMU. Normalmente tendrías que compilar uno desde el código fuente. Pero para facilitarte el trabajo he compilado un Kernel listo para utilizar.

Dentro de la carpeta que creamos en el paso anterior utilizamos git para clonar el repositorio del Hackerspace San Salvador:

git clone https://github.com/hackerspacesv/qemu-rpi-kernel

El comando anterior creará una carpeta "qemu-rpi-kernel" dentro de nuestro directorio de trabajo con algunos Kernels pre-compilados que podamos usar con QEMU. En el repositorio también están las definiciones de árbol de dispositivo (dtd). Este es un archivo que se utiliza en Linux para describir el hardware conectado a una plataforma en particular.

Preparando la imagen de disco

Si utilizamos la imagen de disco descargada tal-cual posiblemente nos encontremos cortos de espacio rápidamente al intentar instalar software. Así que antes de lanzar la emulación por primera vez podemos ejecutar el siguiente comando para incrementar el tamaño del disco. Puedes cambiar el (+10G) por el tamaño que necesites. En la mayoría de casos con que agregues unos 8GB adicionales debería de ser suficiente.

qemu-img resize 2020-05-27-raspios-buster-lite-armhf.img +8G

Importante: Esto solo incrementa el espacio disponible pero no re-dimensiona el sistema de archivos. Esto lo haremos más adelante una vez logremos iniciar la emulación.

Iniciando el Emulador

Para no tener que escribir un largo comando cada vez que inicies la emulación lo recomendable es crear un pequeño script de bash para facilitar el arranque. Puedes crear un nuevo archivo con el nombre "start_qemu.sh" y pegar el siguiente contenido:

#!/bin/bash

export QEMU=$(which qemu-system-arm)
export TMP_DIR=~/tmp/qemu-rpi
export RPI_KERNEL=$1
export DTB_FILE=$2
export RPI_FS=$3

$QEMU -kernel ${RPI_KERNEL} \
    -cpu arm1176 \
    -M versatilepb \
    -dtb ${DTB_FILE} -no-reboot \
    -serial stdio -append "root=/dev/vda2 panic=1 rootfstype=ext4 rw" \
    -drive "file=${RPI_FS},if=none,index=0,media=disk,format=raw,id=disk0" \
    -device "virtio-blk-pci,drive=disk0,disable-modern=on,disable-legacy=off" \
    -net "user,hostfwd=tcp::5022-:22" -net nic \

Cambiamos los permisos del archivo para volverlo ejecutable:

chmod +755 start_qemu.sh

Antes de ejecutar el script, explico rápidamente las opciones de configuración incluídas:

  • QEMU=$(which qemu-system-arm): Busca la ubicación del ejecutable 'qemu-system-arm'
  • TMP_DIR: Directorio temporal de trabaoj para QEMU.
  • RPI_KERNEL, DTB_FILE, RPI_FS: Especifican el archivo de Kernel, el archivo DTB y la imagen del sistema de archivos. Los pasamos como parámetros al script al momento de ejecutarlo.
  • -kernel: Kernel de Linux utilizado para la emulación.
  • -cpu arm1176: Para la emulación utilizaremos el CPU arm1176.
  • -M versatilepb: Esta opción especifica la "plataforma" o "tarjeta" que deseamos emular. En este caso especificamos la versatilepb que es una tarjeta compatible con la Raspberry Pi..
  • -dtb: Aquí especificamos el archivo DTB a utilizar.
  • -no-reboot: Con esta opción la máquina virtual se apagará cuando reciba la señal de reinicio.
  • -serial stdio: Especificamos que queremos que el puerto serie emulado se redirija a la salida estándar para poder interactuar con la máquina virtual.
  • -append "root=/dev/vda2 panic=1 rootfstype=ext4 rw": Estas son opciones que se pasan como parámetros al Kernel de Linux. Estamos diciendo al Kernel que la imágen de disco estará disponible en /dev/vda1 y el tipo del sistema de archivos es ext4
  • -drive "file=${RPI_FS},if=none,index=0,media=disk,format=raw,id=disk0": Estos son los parámetros pasados a QEMU donde especificamos la ubicación y formato del disco virtual.
  • -device "virtio-blk-pci,drive=disk0,disable-modern=on,disable-legacy=off": Estas son configuraciones adicionales que se pasan al dispositivo virtual. Esto afecta como QEMU presenta el dispositivo a Linux. En esta línea estamos diciendo que ocupe la interfaz virtio de alto desempeño para imágenes virtuales
  • -net "user,hostfwd=tcp::5022-:22" -net nic: Por último especificamos a QEMU que redireccione el puerto 22 de la máquina virtual, al puerto local 5022. Esto nos permitirá ingresar a la máquina virtual utilizando SSH.

Iniciamos la ejecución pasando como parámetros el kernel a utilizar, el archivo dtb y la imagen de la siguiente manera:

./qemu_start.sh qemu-rpi-kernel/kernel-qemu-5.4.51-buster qemu-rpi-kernel/versatile-pb-buster-5.4.51.dtb 2020-05-27-raspios-buster-lite-armhf.img

Despues de unos segundos deberíamos de poder ver algo como lo siguiente:

En este punto tenemos nuestra máquina virtual con Raspberry Pi OS corriendo de forma emulada en nuestra computadora.

Configuración final

Ahora que la máquina virtual está funcionando iniciamos sesión con el usuario:clave pi:raspberry y ejecutamos el comando raspi-config para redimensionar el disco y habilitar SSH.

sudo raspi-config

Habilitando SSH

En el menú elegimos la opción "Interfacing Options" (5).

Luego elegimos la opción SSH (P2).

Confirmamos nuestra elección YES. raspi-config muestra una advertencia de seguridad de que no es seguro habilitar SSH con el usuario y clave por defecto. En el entorno de emulación esto no debería de ser un problema. Sin embargo si piensas conectar la máquina emulada a redes públicas debes cambiar la clave por defecto y/o deshabilitar el inicio de sesión con clave.

Un mensaje de configuración de indicará que SSH ha sido habilitado. Ahora también podrás accederla vía SSH conectandote al puerto local 5022 de la máquina anfitrión.

Expandiendo el sistema de archivos

Dentro de raspi-config en el menú principal elegimos "Advanced Options" (7).

La primera opción nos permite expandir el sistema de archivos para que ocupe todo el espacio disponible "Expand Filesystem" (A1).

Esperamos el mensaje de confirmación indicando que el sistema de archivos se expandió correctamente.

raspi-config nos preguntará si deseamos reiniciar. Podemos marcar que sí. Solo recuerda que la configuración no permite a la máquina virtual reiniciar, así que la arrancaremos nuevamente con el comando que vimos en la sección anterior.

Accediendo vía SSH

Para acceder al Raspberry Pi OS emulado desde otra terminal simplemente ejecutamos el comando siguiente (Debes asegurarte de que el emulador se esté ejecutando.) Toma en consideración de que el acceso SSH puede tardar un par de minutos en estar disponible:

ssh pi@localhost -p 5022

Actualizando el Raspberry Pi OS

Es recomendable que luego de que redimensiones del sistema de archivo. Ejecutes el siguiente comando para actualizar el software en tu Raspberry Pi:

sudo apt-get update && sudo apt-get upgrade

Algunas consideraciones a tomar en cuenta

Recuerda que la emulación sirve mayormente para probar software y configuraciones del sistema operativo. Sin embargo no será posible emular "hardware" o periféricos específicos de la Raspberry Pi. Esto significa que si quieres probar como funcionan dispositivos externos lo mejor será que pruebes en una Raspberry Pi de verdad.

Sin embargo, la emulación resulta sumamente útil cuando quieres comprobar el funcionamiento de software dentro del sistema o quieres cambiar configuraciones sin temor a terminar con una Raspberry-Pi que no arranca. Puedes también hacer copias de seguridad del sistema de archivos que te permitirán recuperar tu trabajo si "dañas" algo en el proceso.

Sin más por ahora me despido. ¡Espero que te haya gustado este tutorial!

¡Hasta la próxima!


No hay comentarios: