penvirtual

Hace unos días, el compañero Jinkros publicó una guía sobre cómo arrancar entre varias imágenes ISO en una memoria USB sobre BIOS y UEFI usando tan solo GRUB2.

Muchos sabréis que esto implica tener que realizar reinicios una y otra vez en tu ordenador para asegurarse que las imágenes ISO arranquen correctamente desde el gestor de arranque que le hemos añadido a la memoria USB, cosa que puede ser un lastre para el usuario y hasta puede ser un castigo para los discos duros mecánicos que tengas conectados en el equipo, pues es el componente que más se puede dañar en un ordenador entre repetidos reinicios.

¿Hay alguna solución más inmediata que no requiera ni siquiera reiniciar el ordenador y reducir el riesgo de acortar la vida de algún componente? Por supuesto, y esa solución se llama virtualización. En esta guía veremos cómo crear y particionar una imagen de disco en crudo (RAW) para poder luego instalar GRUB2 sobre ella y realizar en esta misma imagen de disco las pruebas que nos hagan falta con ayuda del virtualizador QEMU, para cerciorarnos de que tengamos el menú de GRUB2 correctamente configurado para poder arrancar las distribuciones que tengamos en las imágenes ISO tanto con arranque BIOS como con arranque EFI, y así luego transferir los archivos que hayamos cambiado definitivamente a la memoria USB.

Creando una imagen de disco nueva

Lo primero que vamos a hacer es reservar un poco de espacio en nuestro disco duro para crear una imagen lo suficientemente grande como para que quepa al menos una imagen ISO de cualquier distribución que queramos probar. Esta imagen de disco nos servirá como si de un disco duro virtual se tratase, pero sin ningún formato especial, de forma que podamos montar su(s) sistema(s) de archivos directamente en el sistema, o incluso clonarlos junto con su tabla de particiones en otro dispositivo físico de almacenamiento.

La imagen la podemos crear con fallocate (inmediato, compatible con algunos sistemas de archivos POSIX) o bien con dd (lento). Nosotros por ejemplo vamos a crear una imagen de 8 GiB que llamaremos imagen_prueba.img nos imaginaremos que conceptualmente se trata de un archivo de pendrive virtual. Para ello tendremos que introducir en la terminal uno de los dos comandos:

$ fallocate -l 8G imagen_prueba.img # Opción 1
$ dd if=/dev/zero of=imagen_prueba.img count=8192 bs=1M # Opción 2

Con esto ya tenemos un archivo de 8 GiB que no contiene por el momento ninguna información:

$ ls -l imagen_prueba.img
-rw-r--r-- 1 alex users 8G jun 5 16:11 imagen_prueba.img

Hasta ahora los comandos introducidos no requerían privilegios de superusuario. A partir de ahora y hasta nuevo aviso, los siguientes comandos serán introducidos como superusuario (se distinguirán no obstante si van precedidos o no de un “#” en el prompt). Puedes sustituir a este usuario con el comando su o con sudo -s si la cuenta de root está bloqueada.

Preparando y particionando la imagen

gpartedsamp

En estos momentos la imagen está totalmente vacía y sin sistemas de archivos, por lo que no podremos montarla todavía para formatearla, y para eso necesitamos antes que cuente con una tabla de particiones.

Podemos particionar la imagen directamente con herramientas conocidas como fdisk, cfdisk o parted, pero el problema de hacerlo en caliente vendrá cuando queramos formatear las particiones que hayamos creado, que no lo podremos hacer si antes no tenemos la imagen asociada como un dispositivo de almacenamiento más en el sistema.

Para crear un dispositivo de bucle (loop) asociada a nuestra imagen de disco recién creada, usaremos la herramienta losetup. Para ello, basta con introducir el siguiente comando:

# losetup /dev/loop0 imagen_prueba.img

Tal vez nos devolverá un error diciendo que el dispositivo o recurso está ocupado, tal vez porque el bloque /dev/loop0 ya esté ocupado por otro archivo. En tal caso, probamos con otro número de dispositivo (hasta loop7), o bien desmontamos el dispositivo /dev/loop0 para poder liberarlo tras introducir el comando losetup -d /dev/loop0 como superusuario.

Si en cambio no devuelve ninguna salida, quiere decir que todo ha salido bien. Ahora tendremos el archivo imagen_prueba.img que hemos creado antes reflejado en el bloque de dispositivo /dev/loop0. Para asegurarnos de que es así, lanzaremos cat /proc/partitions para ver todos los sistemas de archivos localizados:

# cat /proc/partitions
major minor  #blocks  name

8 0 488386584 sda
8 1 41943040 sda1
8 2 438052445 sda2
8 3 8387591 sda3
11 0 1048575 sr0
7 0 8388608 loop0

Lo que nos queda por hacer ahora es particionar la imagen como si de un pendrive real se tratase con el editor de particiones GNU parted. Abrimos el dispositivo de bucle con parted y escribimos el comando “p” dentro del prompt del editor:

# parted /dev/loop0
GNU Parted 3.2
Usando /dev/loop0
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) p
Error: /dev/loop0: unrecognised disk label
Model: Loopback device (loopback)
Disk /dev/loop0: 8590MB
Sector size (logical/physical): 512B/512B
Partition Table: unknown
Disk Flags:
(parted)

No nos preocuparemos por el error que nos da, ya que nos dice que no se reconoce la etiqueta del disco (es normal, ahora mismo la imagen está totalmente vacía). Por lo que sí que nos tendremos que preocupar es por las unidades, ya que por defecto parted está configurado para medir los tamaños de discos y de particiones en megabytes (1 MB = 1000 KB), lo cual puede llevarnos a confusiones a la hora de alinear correctamente las particiones o de especificar su tamaño. Si no nos aclaramos con esta unidad, podemos introducir el comando “unit s” para medir los tamaños en sectores, o bien “unit MiB” si preferimos medir los tamaños en mebibytes (1 MiB = 1024 KiB).

Ahora, si intentamos mostrar de nuevo la lista de particiones con el comando “p”, veremos que en lugar de tener 8590 MB de capacidad en el dispositivo, nos dirá que tenemos 8192 MiB:

(parted) unit mib
(parted) p
Error: /dev/loop0: unrecognised disk label
Model: Loopback device (loopback)
Disk /dev/loop0: 8192MiB
Sector size (logical/physical): 512B/512B
Partition Table: unknown
Disk Flags:
(parted)

Para una configuración básica, como es crear una única partición FAT32 arrancable desde BIOS que ocupe toda la imagen de nuestro pseudo-pendrive (para que podamos arrancar también sobre UEFI sin necesidad de crear otra partición adicional), tendríamos que escribir los siguientes comandos en parted:

(parted) mkt msdos
(parted) mkp primary fat32 1 -1s
(parted) set 1 boot on

Por partes:

  • El comando mkt o mktable sirve para crear una nueva tabla de particiones. Con el parámetro msdos le estamos diciendo a parted que nos escriba sobre el primer sector de la imagen de disco una tabla de particiones de tipo MBR.
  • El comando mkp o mkpart se encarga de crear una nueva partición en la imagen de disco. Con el primer parámetro le estamos diciendo que queremos crear una partición primaria. Con el segundo, estamos indicando que la partición será formateada en FAT32. Los dos siguientes parámetros son localizaciones numéricas del disco virtual y sirven para especificar el tamaño de la partición que queremos crear. Como queremos alinearlo al primer mebibyte de la imagen, escribimos en el tercer parámetro el número 1. Y si queremos que abarque hasta el último sector de la imagen, escribiremos en el cuarto parámetro: “-1s”.
  • Por último, con el comando set 1 boot on estamos pidiendo a parted que nos marque la primera partición como arrancable.

El disco se quedará con este esquema de particiones:

(parted) p
Model: Loopback device (loopback)
Disk /dev/loop0: 8192MiB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Numero Inicio Fin Tamaño Typo Sistema de ficheros Banderas
1 1,00MiB 8192MiB 8191MiB primary fat32 lba

(parted)

Salimos de ahí con el comando “q”, y si volvemos a lanzar cat /proc/partitions, nos daremos cuenta que se ha creado un nuevo bloque de control llamado /dev/loop0p1, que significa literalmente “la primera partición del primer dispositivo de bucle”:

# cat /proc/partitions
major minor #blocks name

8 0 488386584 sda
8 1 41943040 sda1
8 2 438052445 sda2
8 3 8387591 sda3
11 0 1048575 sr0
7 0 8388608 loop0
259 0 8387584 loop0p1

Si es así, ya podremos darle formato a la partición con mkfs.vfat o mkdosfs (en nuestro caso, porque hemos elegido formatearlo en FAT32). Importante: hay que formatear el bloque /dev/loop0p1, no el bloque /dev/loop0 porque de lo contrario sobrescribiremos la tabla de particiones y luego no podremos instalar el cargador GRUB2 sobre el sector de arranque del MBR.

# mkfs.vfat -F 32 /dev/loop0p1
mkfs.fat 4.1 (2017-01-24)

Hecho esto, ya podremos montar la(s) particion(es) que hayamos formateado en la imagen. Podemos hacerlo con el comando mount siguiendo la misma sintaxis que mencionó Jinkros en este artículo, o bien con el ayudante udisks2 (sin necesidad de tener permisos de superusuario) de la siguiente forma:

$ udisksctl mount --block-device /dev/loop0p1
Mounted /dev/loop0p1 at /run/media/alex/E247-D5F8.

Ahora tenemos que saber dónde está montado nuestro “pendrive virtual”, que en nuestro caso lo está en /run/media/alex/E247-D5F8, tal y como nos ha dicho udisks2 después de montar el volumen del disco; pues lo necesitaremos para poder instalar GRUB2 sobre él, siguiendo las mismas indicaciones del post ya mencionado:

# grub-install --target=x86_64-efi --efi-directory=/run/media/alex/E247-D5F8 --boot-directory=/run/media/alex/E247-D5F8/boot --removable --recheck
# grub-install --target=i386-pc --boot-directory=/run/media/alex/E247-D5F8/boot --recheck /dev/loop0

En este segundo comando sí que tenemos que especificar que el dispositivo donde queremos instalar GRUB2 es en /dev/loop0, pues es aquí donde se encuentra el sector de arranque de la imagen que se sobrescribirá.

Si ha salido todo correctamente, ya podemos seguir con el tutorial de Jinkros para terminar de configurar GRUB2 y de copiar las ISOs que queramos probar en la imagen de disco. Por ejemplo, nuestra configuración de grub es la siguiente:

set imgdevpath="/dev/disk/by-uuid/E247-D5F8"
insmod png
insmod part_msdos
insmod fat
insmod ntfs
insmod ext2
set root='hd0,msdos1'
set timeout=30
set default=0
if loadfont /boot/grub/fonts/unicode.pf2 ; then
    set gfxmode=640x480
    insmod gfxterm
    terminal_output gfxterm
    if [ "${grub_platform}" == "efi" ]; then
        insmod efi_gop
            insmod efi_uga
    fi
   
    if [ "${grub_platform}" == "pc" ]; then
            insmod vbe
            insmod vga
    fi
fi
if background_image /boot/grub/fond.png ; then
    set color_normal=white/black
    set color_highlight=yellow/dark-gray
else
    set menu_color_normal=yellow/red
    set menu_color_highlight=white/black
fi
menuentry "Debian 8.8 MATE Live" {
    set isofile='/isos/debian-live-8.8.0-i386-mate-desktop.iso'
    loopback loop $isofile
    linux (loop)/live/vmlinuz1 boot=live findiso=${isofile} locales=es_ES.UTF-8 config quiet splash
    echo -e "Cargando - Espere, por favor..."
    initrd (loop)/live/initrd1.img
}

Asumiendo que:

  • El número de serie del dispositivo es E247-D5F8.
  • Las imágenes ISO las tenemos guardadas en /isos dentro de la imagen del pendrive.
  • Nuestra imagen de fondo (opcional) se ubica en /boot/grub/font.png dentro de la imagen.

Ya tenemos la imagen lista. Podemos desmontar la imagen gráficamente desde el entorno de escritorio que estemos usando, con umount o con el ayudante udisks2:

$ udisksctl unmount --block-device /dev/loop0p1

Si queremos realizar modificaciones en la imagen de pendrive que hemos creado, podemos volver a montarla siguiendo los pasos anteriores (usando losetup para crear un dispositivo de bucle y mount o udisks2 para montarlo), o directamente con el comando mount usando las opciones loop para que se asocie a un dispositivo de bucle, y offset para especificar en qué byte de la imagen se encuentra el sistema de archivos a montar. Para esto último, necesitamos saber primero cómo está alineada la partición que queremos montar en la imagen. Lo podemos calcular por ejemplo con fdisk:

# mount -o loop,offset $((2048*512)) imagen_prueba.img /mnt

Y lo desmontaremos con umount como lo haríamos normalmente:

# umount /mnt

Iniciando una máquina virtual con la imagen

maquina-virtual

Antes de iniciar la máquina virtual, necesitamos asegurarnos de que tenemos el virtualizador QEMU instalado, y sus extensiones para otras arquitecturas de computadores. También necesitaremos instalar los módulos de BIOS ovmf (Tianocore UEFI firmware) para poder virtualizar con firmware UEFI en QEMU.

Los comandos de instalación para las distribuciones más conocidas son:

Debian/Ubuntu y derivados

# apt install qemu qemu-system-x86 ovmf

Arch Linux y derivados

# pacman -S qemu qemu-arch-extra ovmf

Fedora/RHEL y derivados

Gentoo y derivados

# emerge app-emulation/qemu sys-firmware/edk2-ovmf

OpenSuSE y derivados

# zypper in qemu ovmf

Hecha la instalación, podemos empezar a comprobar si funciona correctamente el arranque de la imagen con el siguiente comando para lanzar el emulador:

$ qemu-system-x86_64 \
-k es \
-vga std \
-m 2048 \
-hda imagen_prueba.img

Con esto iniciaremos una máquina de 64 bits con arranque BIOS, con el teclado configurado al idioma español (-k es), con gráficos SVGA estándar (-vga std), con 2 GiB de memoria RAM (-m 2048) y usando como disco duro principal la imagen que acabamos de preparar (-hda imagen_prueba.img).

En caso de querer probarlo con firmware UEFI, tendremos que añadir la opción -bios especificando la ruta del firmware UEFI que hemos bajado del paquete ovmf (normalmente se ubica en /usr/share/ovmf/ovmf_code_x64.bin), y la opción -no-kvm para forzar a que QEMU no use KVM, ya que puede dar problemas de compatibilidad con el firmware EFI virtualizado.

$ qemu-system-x86_64 \
-k es \
-vga std \
-m 2048 \
-hda imagen.img \
-bios /usr/share/ovmf/ovmf_code_x64.bin \
-no-kvm

Una vez comprobado que arranque correctamente, ya podemos copiar definitivamente los archivos de configuración que hayamos cambiado en el disco virtual de pruebas al pendrive multiboot que ya teníamos configurado. Al principio puede parecer engorroso, pero una vez aprendida la mecánica y a lo mejor haciendo el arreglo con scripts, puede resultar más ágil y práctico probar el arranque de un pendrive multiboot que estemos configurando sin necesidad de reiniciar el equipo.

Por supuesto que este tutorial también puede abarcar otros propósitos y necesidades, eso ya es cuestión también de echarle algo de imaginación.

 

Fuente: lignux

¿Quién está en línea?

Hay 11997 invitados y ningún miembro en línea