• Crear una imagen personalizada de Debian para el router BPI-R3

    En este artículo crearemos desde un ordenador con Debian una imagen personalizada de Debian 13 «Trixie» para el router Banana Pi BPI-R3. La imagen resultante podrá grabarse directamente en una tarjeta microSD e incluirá el sistema de archivos raíz, un kernel Linux compilado por nosotros, los módulos correspondientes, el Device Tree de la placa y toda la cadena de arranque necesaria.

    Este procedimiento está pensado específicamente para la Banana Pi BPI-R3 basada en el SoC MediaTek MT7986A. No debemos utilizarlo sin modificaciones en una BPI-R3 Mini, BPI-R4 u otro modelo. El kernel Linux mantiene tanto el Device Tree base de la BPI-R3 como las superposiciones específicas para SD, eMMC, NAND y NOR. En este caso aplicaremos la superposición correspondiente a la tarjeta SD.

    En ARM64 no utilizaremos un archivo zImage. Compilaremos el archivo Image sin comprimir, que puede ser arrancado directamente por U-Boot mediante el comando booti.

    Entender la cadena de arranque de la BPI-R3

    La BPI-R3 no puede arrancar desde una microSD escribiendo simplemente el archivo u-boot.bin en el sector 2. El arranque se realiza mediante varias etapas:

    • BootROM: código incluido en el propio SoC MT7986A.
    • BL2: inicializa el hardware básico, incluida la memoria DDR4.
    • FIP: contiene las siguientes etapas de Trusted Firmware-A y el binario de U-Boot.
    • U-Boot: carga el kernel, el initramfs y el Device Tree.
    • Linux: monta el sistema de archivos raíz de Debian.

    U-Boot incluye una configuración específica denominada mt7986a_bpir3_sd_defconfig para arrancar la BPI-R3 desde SD. Para generar BL2 y FIP utilizaremos la implementación de Trusted Firmware-A mantenida para los SoC MediaTek empleados por OpenWrt.

    No escribiremos directamente sobre una tarjeta durante la construcción. Primero generaremos un archivo debian-bpi-r3.img, lo montaremos mediante un dispositivo loop y, cuando esté terminado, lo grabaremos en la microSD. Así evitaremos destruir por error otro disco del ordenador.

    Instalar las herramientas necesarias

    Actualizamos la información de los repositorios e instalamos el compilador cruzado para ARM64, las dependencias del kernel, U-Boot, Trusted Firmware-A, debootstrap y las herramientas necesarias para crear la imagen:

    sudo apt update
    
    sudo apt install -y \
      bc \
      binfmt-support \
      bison \
      build-essential \
      ca-certificates \
      crossbuild-essential-arm64 \
      debootstrap \
      device-tree-compiler \
      dosfstools \
      e2fsprogs \
      flex \
      gdisk \
      git \
      libelf-dev \
      libgnutls28-dev \
      libncurses-dev \
      libssl-dev \
      parted \
      pkg-config \
      python3 \
      python3-dev \
      python3-jsonschema \
      python3-pyelftools \
      python3-setuptools \
      python3-yaml \
      qemu-user-static \
      rsync \
      swig \
      u-boot-tools \
      uuid-dev \
      xz-utils

    El metapaquete crossbuild-essential-arm64 ya instala el compilador y las binutils para AArch64. No necesitamos instalar el compilador arm-linux-gnueabihf, ya que ese compilador está destinado a ARM de 32 bits.

    Definimos las rutas y versiones que utilizaremos. Los siguientes bloques deben ejecutarse desde la misma terminal para conservar las variables:

    vProyecto="$HOME/debian-bpi-r3"
    vRootfs="$vProyecto/rootfs"
    vKernel="$vProyecto/linux"
    vUBoot="$vProyecto/u-boot"
    vTfa="$vProyecto/arm-trusted-firmware"
    vImagen="$vProyecto/debian-bpi-r3.img"
    
    vSuiteDebian="trixie"
    vRamaKernel="linux-6.12.y"
    vVersionUBoot="v2025.10"
    
    mkdir -p "$vProyecto"
    
    rm -rf "$vKernel"
    rm -rf "$vUBoot"
    rm -rf "$vTfa"
    rm -f "$vImagen"
    rm -f "$vImagen.xz"
    sudo rm -rf "$vRootfs"

    Utilizaremos Debian 13 Trixie para el sistema ARM64.

    Compilar el kernel Linux

    Clonamos la rama estable 6.12 del kernel:

    git clone \
      --depth 1 \
      --branch "$vRamaKernel" \
      https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git \
      "$vKernel"
    
    cd "$vKernel"

    Partimos de la configuración genérica de ARM64. Es preferible hacer esto en lugar de desactivar todas las demás familias de SoC desde menuconfig, ya que podríamos eliminar dependencias necesarias para controladores compartidos.

    make \
      ARCH=arm64 \
      CROSS_COMPILE=aarch64-linux-gnu- \
      defconfig

    Nos aseguramos de que el soporte necesario para arrancar desde la microSD, montar ext4 y utilizar la consola serie quede integrado directamente en el kernel y no dependa de módulos que todavía no podrían cargarse:

    scripts/config --enable ARCH_MEDIATEK
    scripts/config --enable BLK_DEV_INITRD
    scripts/config --enable DEVTMPFS
    scripts/config --enable DEVTMPFS_MOUNT
    scripts/config --enable EXT4_FS
    scripts/config --enable FAT_FS
    scripts/config --enable VFAT_FS
    scripts/config --enable MMC
    scripts/config --enable MMC_BLOCK
    scripts/config --enable MMC_MTK
    scripts/config --enable SERIAL_8250
    scripts/config --enable SERIAL_8250_CONSOLE
    scripts/config --enable SERIAL_OF_PLATFORM
    
    make \
      ARCH=arm64 \
      CROSS_COMPILE=aarch64-linux-gnu- \
      olddefconfig

    Si queremos añadir o quitar controladores, abrimos la configuración interactiva:

    make \
      ARCH=arm64 \
      CROSS_COMPILE=aarch64-linux-gnu- \
      menuconfig

    Para mantener la funcionalidad del router conviene conservar los controladores de red de MediaTek, DSA, el switch MT7530/MT7531, phylink, SFP, PCI Express y los controladores inalámbricos MediaTek.

    Compilamos el kernel, los módulos y todos los Device Trees:

    make -j"$(nproc)" \
      ARCH=arm64 \
      CROSS_COMPILE=aarch64-linux-gnu- \
      Image \
      modules \
      dtbs

    Obtenemos el identificador exacto de la versión compilada:

    vVersionKernel="$(
      make -s \
        ARCH=arm64 \
        CROSS_COMPILE=aarch64-linux-gnu- \
        kernelrelease
    )"
    
    echo "$vVersionKernel"

    Generar el Device Tree final para la microSD

    El kernel proporciona un Device Tree base para la placa y diferentes superposiciones. Los archivos utilizados en este caso son:

    • mt7986a-bananapi-bpi-r3.dtb: descripción base de la BPI-R3.
    • mt7986a-bananapi-bpi-r3-sd.dtbo: configuración adicional que habilita el uso de la tarjeta SD.

    Aunque U-Boot puede aplicar superposiciones en tiempo de arranque, generaremos previamente un DTB completo. De esta forma no dependeremos de que la configuración concreta de U-Boot aplique automáticamente el overlay correcto.

    vDtbBase="$vKernel/arch/arm64/boot/dts/mediatek/mt7986a-bananapi-bpi-r3.dtb"
    vDtboSd="$vKernel/arch/arm64/boot/dts/mediatek/mt7986a-bananapi-bpi-r3-sd.dtbo"
    vDtbFinal="$vProyecto/mt7986a-bananapi-bpi-r3-sd.dtb"
    
    test -f "$vDtbBase"
    test -f "$vDtboSd"
    
    fdtoverlay \
      -i "$vDtbBase" \
      -o "$vDtbFinal" \
      "$vDtboSd"
    
    fdtdump "$vDtbFinal" > /dev/null

    El último comando comprueba que el archivo generado puede ser interpretado correctamente por las herramientas de Device Tree.

    Compilar U-Boot para arrancar desde la microSD

    Clonamos una versión estable de U-Boot y seleccionamos la configuración específica de la BPI-R3 para SD:

    git clone \
      --depth 1 \
      --branch "$vVersionUBoot" \
      https://source.denx.de/u-boot/u-boot.git \
      "$vUBoot"
    
    cd "$vUBoot"
    
    make \
      CROSS_COMPILE=aarch64-linux-gnu- \
      mt7986a_bpir3_sd_defconfig
    
    make -j"$(nproc)" \
      CROSS_COMPILE=aarch64-linux-gnu-
    
    test -f "$vUBoot/u-boot.bin"

    El archivo u-boot.bin todavía no es una imagen arrancable por sí mismo. Debemos introducirlo como etapa BL33 dentro del FIP de MediaTek.

    Generar BL2 y FIP con Trusted Firmware-A

    Clonamos la implementación de Trusted Firmware-A utilizada para los SoC MediaTek MT798x:

    git clone \
      --depth 1 \
      https://github.com/mtk-openwrt/arm-trusted-firmware.git \
      "$vTfa"
    
    cd "$vTfa"

    Compilamos una cadena de arranque para SD y memoria DDR4, incluyendo el binario de U-Boot como BL33:

    make -j"$(nproc)" \
      CROSS_COMPILE=aarch64-linux-gnu- \
      PLAT=mt7986 \
      BOOT_DEVICE=sdmmc \
      DRAM_USE_DDR4=1 \
      USE_MKIMAGE=1 \
      BL33="$vUBoot/u-boot.bin" \
      all \
      fip

    Localizamos los dos archivos que escribiremos en la zona reservada de la imagen:

    vBl2="$(
      find "$vTfa/build/mt7986" \
        -type f \
        -name "bl2.img" \
        | sed -n '1p'
    )"
    
    vFip="$(
      find "$vTfa/build/mt7986" \
        -type f \
        -name "fip.bin" \
        | sed -n '1p'
    )"
    
    if [ -z "$vBl2" ] || [ ! -f "$vBl2" ]; then
      echo "No se ha generado bl2.img"
      exit 1
    fi
    
    if [ -z "$vFip" ] || [ ! -f "$vFip" ]; then
      echo "No se ha generado fip.bin"
      exit 1
    fi
    
    echo "BL2: $vBl2"
    echo "FIP: $vFip"

    También guardamos los commits exactos utilizados. Esto nos permitirá repetir posteriormente la compilación con las mismas revisiones:

    git -C "$vKernel" rev-parse HEAD | tee "$vProyecto/commit-linux.txt"
    git -C "$vUBoot" rev-parse HEAD | tee "$vProyecto/commit-u-boot.txt"
    git -C "$vTfa" rev-parse HEAD | tee "$vProyecto/commit-tfa.txt"

    Crear el sistema de archivos raíz de Debian

    Como el ordenador de construcción normalmente será AMD64 y el sistema de destino es ARM64, realizaremos debootstrap en dos etapas. La primera etapa descarga y desempaqueta Debian. La segunda se ejecuta dentro del sistema ARM64 mediante QEMU y binfmt. Podemos consultar todos los parámetros disponibles en la página del manual de debootstrap.

    sudo mkdir -p "$vRootfs"
    
    sudo debootstrap \
      --arch=arm64 \
      --foreign \
      "$vSuiteDebian" \
      "$vRootfs" \
      http://deb.debian.org/debian

    Habilitamos el intérprete de binarios AArch64 y copiamos QEMU dentro del sistema:

    sudo update-binfmts --enable qemu-aarch64
    
    test -x /usr/bin/qemu-aarch64-static
    
    sudo install \
      -m 0755 \
      /usr/bin/qemu-aarch64-static \
      "$vRootfs/usr/bin/qemu-aarch64-static"

    Montamos los sistemas virtuales necesarios para configurar correctamente los paquetes:

    sudo mkdir -p \
      "$vRootfs/dev" \
      "$vRootfs/proc" \
      "$vRootfs/sys" \
      "$vRootfs/run"
    
    sudo mount --rbind /dev "$vRootfs/dev"
    sudo mount --make-rslave "$vRootfs/dev"
    
    sudo mount -t proc proc "$vRootfs/proc"
    
    sudo mount --rbind /sys "$vRootfs/sys"
    sudo mount --make-rslave "$vRootfs/sys"
    
    sudo mount --rbind /run "$vRootfs/run"
    sudo mount --make-rslave "$vRootfs/run"
    
    sudo rm -f "$vRootfs/etc/resolv.conf"
    sudo cp --dereference /etc/resolv.conf "$vRootfs/etc/resolv.conf"

    Ejecutamos la segunda etapa:

    sudo chroot \
      "$vRootfs" \
      /debootstrap/debootstrap \
      --second-stage

    Configurar los repositorios de Debian

    Eliminamos la configuración que haya creado debootstrap y definimos los repositorios de Debian Trixie, incluidas las secciones necesarias para instalar firmware:

    sudo rm -f "$vRootfs/etc/apt/sources.list.d/debian.sources"
    
    {
      echo "deb http://deb.debian.org/debian trixie main contrib non-free non-free-firmware"
      echo "deb http://deb.debian.org/debian trixie-updates main contrib non-free non-free-firmware"
      echo "deb http://security.debian.org/debian-security trixie-security main contrib non-free non-free-firmware"
    } | sudo tee "$vRootfs/etc/apt/sources.list" > /dev/null

    Evitamos que los paquetes intenten arrancar servicios dentro del chroot:

    {
      echo "#!/bin/sh"
      echo ""
      echo "exit 101"
    } | sudo tee "$vRootfs/usr/sbin/policy-rc.d" > /dev/null
    
    sudo chmod 0755 "$vRootfs/usr/sbin/policy-rc.d"

    Entramos en el sistema ARM64:

    sudo chroot "$vRootfs" /bin/bash

    A partir de este punto, los siguientes comandos se ejecutan dentro del chroot:

    export DEBIAN_FRONTEND=noninteractive
    
    apt-get update
    
    apt-get install -y \
      ca-certificates \
      cloud-guest-utils \
      ethtool \
      firmware-mediatek \
      gdisk \
      initramfs-tools \
      iproute2 \
      locales \
      nftables \
      openssh-server \
      sudo \
      systemd-resolved \
      systemd-sysv \
      tzdata

    No instalaremos linux-headers-$(uname -r). Dentro del chroot, uname seguiría mostrando el kernel AMD64 que está ejecutando el ordenador de construcción, no el kernel ARM64 que acabamos de compilar.

    Configurar el sistema básico

    Configuramos el nombre del equipo y el archivo hosts:

    echo "bpi-r3" > /etc/hostname
    
    {
      echo "127.0.0.1 localhost"
      echo "127.0.1.1 bpi-r3"
      echo "::1 localhost ip6-localhost ip6-loopback"
    } > /etc/hosts

    Configuramos el idioma y la zona horaria:

    echo "es_ES.UTF-8 UTF-8" > /etc/locale.gen
    
    locale-gen
    update-locale LANG=es_ES.UTF-8
    
    ln -sfn /usr/share/zoneinfo/Europe/Madrid /etc/localtime
    echo "Europe/Madrid" > /etc/timezone
    
    dpkg-reconfigure -f noninteractive tzdata

    Definimos los sistemas de archivos que tendrá la imagen:

    {
      echo "LABEL=BPI-R3-ROOT / ext4 defaults,noatime 0 1"
      echo "LABEL=BPI-R3-BOOT /boot vfat defaults,umask=0077 0 2"
    } > /etc/fstab

    Configurar provisionalmente la red

    Los nombres de las interfaces pueden variar dependiendo de los controladores, del Device Tree y de la versión de systemd. Crearemos una configuración DHCP provisional que coincida con los nombres más habituales. Una vez arrancado el router podremos sustituirla por la configuración definitiva de bridge, VLAN, routing o firewall.

    mkdir -p /etc/systemd/network
    
    {
      echo "[Match]"
      echo "Name=wan lan* eth* end*"
      echo ""
      echo "[Link]"
      echo "RequiredForOnline=no"
      echo ""
      echo "[Network]"
      echo "DHCP=yes"
      echo "IPv6AcceptRA=yes"
    } > /etc/systemd/network/20-dhcp.network

    Habilitamos la red, la resolución DNS, SSH y la consola serie:

    systemctl enable systemd-networkd.service
    systemctl enable systemd-resolved.service
    systemctl enable ssh.service
    systemctl enable serial-getty@ttyS0.service

    Establecemos una contraseña para root y creamos un usuario administrativo. Podemos sustituir administrador por el nombre que prefiramos:

    passwd root
    
    adduser --gecos "" administrador
    usermod -aG sudo administrador

    Eliminamos la política temporal y salimos del chroot:

    rm -f /usr/sbin/policy-rc.d
    
    exit

    Instalar el kernel y los módulos dentro de Debian

    Desde el ordenador de construcción instalamos los módulos del kernel dentro del sistema ARM64:

    cd "$vKernel"
    
    sudo make \
      ARCH=arm64 \
      CROSS_COMPILE=aarch64-linux-gnu- \
      INSTALL_MOD_PATH="$vRootfs" \
      INSTALL_MOD_STRIP=1 \
      modules_install

    Copiamos el kernel, su configuración, el mapa de símbolos y el Device Tree final:

    sudo mkdir -p "$vRootfs/boot"
    sudo mkdir -p "$vRootfs/boot/dtbs/$vVersionKernel/mediatek"
    
    sudo install \
      -m 0644 \
      "$vKernel/arch/arm64/boot/Image" \
      "$vRootfs/boot/vmlinuz-$vVersionKernel"
    
    sudo install \
      -m 0644 \
      "$vKernel/.config" \
      "$vRootfs/boot/config-$vVersionKernel"
    
    sudo install \
      -m 0644 \
      "$vKernel/System.map" \
      "$vRootfs/boot/System.map-$vVersionKernel"
    
    sudo install \
      -m 0644 \
      "$vDtbFinal" \
      "$vRootfs/boot/dtbs/$vVersionKernel/mediatek/mt7986a-bananapi-bpi-r3-sd.dtb"

    Actualizamos las dependencias de los módulos y generamos el initramfs:

    sudo chroot \
      "$vRootfs" \
      depmod \
      -a \
      "$vVersionKernel"
    
    sudo chroot \
      "$vRootfs" \
      update-initramfs \
      -c \
      -k "$vVersionKernel"
    
    test -f "$vRootfs/boot/initrd.img-$vVersionKernel"

    Limpiar el sistema antes de crear la imagen

    Eliminamos los identificadores y claves que deben generarse de forma independiente durante el primer arranque:

    sudo chroot "$vRootfs" apt-get clean
    
    sudo rm -f "$vRootfs/etc/ssh/ssh_host_"*
    sudo truncate -s 0 "$vRootfs/etc/machine-id"
    sudo rm -f "$vRootfs/var/lib/dbus/machine-id"
    
    sudo find \
      "$vRootfs/var/lib/apt/lists" \
      -mindepth 1 \
      -delete
    
    sudo rm -f "$vRootfs/usr/bin/qemu-aarch64-static"
    
    sudo rm -f "$vRootfs/etc/resolv.conf"
    sudo ln -s \
      /run/systemd/resolve/stub-resolv.conf \
      "$vRootfs/etc/resolv.conf"

    Desmontamos los sistemas virtuales del chroot:

    sudo umount -R "$vRootfs/run"
    sudo umount -R "$vRootfs/sys"
    sudo umount -R "$vRootfs/proc"
    sudo umount -R "$vRootfs/dev"

    Crear el archivo de imagen

    Crearemos una imagen inicial de 4 GiB. La primera partición comenzará en el MiB 64, dejando espacio suficiente para BL2, el entorno de U-Boot, FIP y otras áreas reservadas del arranque.

    rm -f "$vImagen"
    
    truncate -s 4G "$vImagen"
    
    vLoop="$(
      sudo losetup \
        --find \
        --show \
        --partscan \
        "$vImagen"
    )"
    
    echo "$vLoop"

    Creamos una tabla GPT, una partición FAT32 para el kernel y una partición ext4 para Debian:

    sudo parted -s "$vLoop" mklabel gpt
    
    sudo parted -s "$vLoop" \
      mkpart BPI-R3-BOOT fat32 64MiB 320MiB
    
    sudo parted -s "$vLoop" \
      mkpart BPI-R3-ROOT ext4 320MiB 100%
    
    sudo partprobe "$vLoop"
    sudo udevadm settle
    
    vParticionBoot="${vLoop}p1"
    vParticionRoot="${vLoop}p2"
    
    test -b "$vParticionBoot"
    test -b "$vParticionRoot"

    La partición FAT32 es únicamente una partición de arranque para U-Boot. No es una partición EFI ni debemos marcarla como ESP.

    Escribir BL2 y FIP en la imagen

    Escribimos BL2 a partir del sector 34 y FIP a partir del sector 13312. El tamaño de bloque utilizado es de 512 bytes. La primera partición de datos comienza en el MiB 64, por lo que estas escrituras no la sobrescribirán.

    sudo dd \
      if="$vBl2" \
      of="$vLoop" \
      bs=512 \
      seek=34 \
      conv=notrunc,fsync \
      status=progress
    
    sudo dd \
      if="$vFip" \
      of="$vLoop" \
      bs=512 \
      seek=13312 \
      conv=notrunc,fsync \
      status=progress

    Este paso sustituye al comando incorrecto del borrador original que escribía u-boot.bin directamente mediante seek=2.

    Formatear y poblar las particiones

    sudo mkfs.vfat \
      -F 32 \
      -n BPI-R3-BOOT \
      "$vParticionBoot"
    
    sudo mkfs.ext4 \
      -F \
      -L BPI-R3-ROOT \
      "$vParticionRoot"

    Montamos la partición raíz y copiamos Debian conservando propietarios, permisos, ACL, atributos extendidos y UID numéricos:

    vMontaje="$vProyecto/mnt"
    
    sudo rm -rf "$vMontaje"
    sudo mkdir -p "$vMontaje"
    
    sudo mount "$vParticionRoot" "$vMontaje"
    
    sudo rsync \
      -aHAX \
      --numeric-ids \
      --exclude="/boot/*" \
      "$vRootfs/" \
      "$vMontaje/"

    Montamos la partición de arranque dentro del directorio boot de Debian:

    sudo mkdir -p "$vMontaje/boot"
    sudo mount "$vParticionBoot" "$vMontaje/boot"
    
    sudo install \
      -m 0644 \
      "$vRootfs/boot/vmlinuz-$vVersionKernel" \
      "$vMontaje/boot/vmlinuz-$vVersionKernel"
    
    sudo install \
      -m 0644 \
      "$vRootfs/boot/initrd.img-$vVersionKernel" \
      "$vMontaje/boot/initrd.img-$vVersionKernel"
    
    sudo mkdir -p \
      "$vMontaje/boot/dtbs/$vVersionKernel/mediatek"
    
    sudo install \
      -m 0644 \
      "$vDtbFinal" \
      "$vMontaje/boot/dtbs/$vVersionKernel/mediatek/mt7986a-bananapi-bpi-r3-sd.dtb"

    Configurar el arranque mediante Extlinux

    U-Boot puede descubrir sistemas arrancables mediante su infraestructura estándar de bootflow y Extlinux. Crearemos una configuración que indique dónde están el kernel, el initramfs, el Device Tree y la partición raíz. La documentación técnica está disponible en el apartado Standard Boot de U-Boot.

    sudo mkdir -p "$vMontaje/boot/extlinux"
    
    {
      echo "DEFAULT debian"
      echo "TIMEOUT 30"
      echo "MENU TITLE Debian GNU/Linux para Banana Pi BPI-R3"
      echo ""
      echo "LABEL debian"
      echo "  MENU LABEL Debian GNU/Linux"
      echo "  LINUX /vmlinuz-$vVersionKernel"
      echo "  INITRD /initrd.img-$vVersionKernel"
      echo "  FDT /dtbs/$vVersionKernel/mediatek/mt7986a-bananapi-bpi-r3-sd.dtb"
      echo "  APPEND root=LABEL=BPI-R3-ROOT rootwait rw console=ttyS0,115200n8 earlycon"
    } | sudo tee \
      "$vMontaje/boot/extlinux/extlinux.conf" \
      > /dev/null

    Añadir un script de arranque alternativo

    Además de Extlinux, añadiremos un archivo boot.scr. Este script nos proporciona una segunda forma de arrancar y puede ejecutarse manualmente desde la consola de U-Boot si el descubrimiento automático no lo encuentra.

    {
      echo "echo Cargando Debian GNU/Linux..."
      echo "setenv bootargs root=LABEL=BPI-R3-ROOT rootwait rw console=ttyS0,115200n8 earlycon"
      echo "load mmc 0:1 \${kernel_addr_r} /vmlinuz-$vVersionKernel"
      echo "load mmc 0:1 \${ramdisk_addr_r} /initrd.img-$vVersionKernel"
      echo "setenv vTamanoInitrd \${filesize}"
      echo "load mmc 0:1 \${fdt_addr_r} /dtbs/$vVersionKernel/mediatek/mt7986a-bananapi-bpi-r3-sd.dtb"
      echo "booti \${kernel_addr_r} \${ramdisk_addr_r}:\${vTamanoInitrd} \${fdt_addr_r}"
    } > "$vProyecto/boot.cmd"
    
    mkimage \
      -A arm64 \
      -O linux \
      -T script \
      -C none \
      -n "Debian BPI-R3" \
      -d "$vProyecto/boot.cmd" \
      "$vProyecto/boot.scr"
    
    sudo install \
      -m 0644 \
      "$vProyecto/boot.scr" \
      "$vMontaje/boot/boot.scr"
    
    sudo install \
      -m 0644 \
      "$vProyecto/boot.cmd" \
      "$vMontaje/boot/boot.cmd"

    Finalizar y comprimir la imagen

    sync
    
    sudo umount "$vMontaje/boot"
    sudo umount "$vMontaje"
    
    sudo losetup -d "$vLoop"
    
    cd "$vProyecto"
    
    rm -f debian-bpi-r3.img.xz
    
    xz \
      -T0 \
      -6 \
      -k \
      debian-bpi-r3.img
    
    sha256sum \
      debian-bpi-r3.img \
      debian-bpi-r3.img.xz \
      > SHA256SUMS

    Al terminar tendremos los siguientes archivos:

    • debian-bpi-r3.img: imagen sin comprimir.
    • debian-bpi-r3.img.xz: imagen comprimida para almacenarla o distribuirla.
    • SHA256SUMS: sumas SHA-256 para verificar la integridad.
    • commit-linux.txt, commit-u-boot.txt y commit-tfa.txt: revisiones exactas utilizadas durante la construcción.

    Grabar la imagen en una tarjeta microSD

    Debemos comprobar con lsblk cuál es el dispositivo correspondiente a la microSD. Escribir sobre el dispositivo equivocado destruirá su tabla de particiones y sus datos.

    lsblk \
      -o NAME,SIZE,MODEL,TRAN,FSTYPE,MOUNTPOINTS

    Definimos el dispositivo completo, no una de sus particiones. En este ejemplo utilizamos /dev/sdX como marcador que debemos sustituir:

    vDispositivo="/dev/sdX"
    
    lsblk \
      -o NAME,SIZE,MODEL,TRAN,FSTYPE,MOUNTPOINTS \
      "$vDispositivo"

    Desmontamos previamente cualquier partición de la tarjeta y grabamos la imagen:

    xz -dc "$vProyecto/debian-bpi-r3.img.xz" \
      | sudo dd \
          of="$vDispositivo" \
          bs=4M \
          status=progress \
          conv=fsync
    
    sync

    No debemos utilizar una partición como /dev/sdX1 en el parámetro of. La imagen debe escribirse sobre el dispositivo completo.

    Realizar el primer arranque

    Colocamos los interruptores físicos de la BPI-R3 en la posición de arranque desde SD indicada en la serigrafía y en la documentación de la placa. Es recomendable realizar el primer arranque mediante la consola UART de 3,3 voltios configurada a 115200 baudios, 8 bits de datos, sin paridad y un bit de parada.

    Desde Debian podemos conectarnos mediante picocom:

    sudo apt install -y picocom
    
    sudo picocom \
      -b 115200 \
      /dev/ttyUSB0

    No debemos conectar la alimentación de 5 voltios del adaptador USB-UART al conector serie del router. Solamente conectaremos tierra, transmisión y recepción, cruzando TX con RX.

    Si U-Boot no encuentra automáticamente Extlinux o boot.scr, interrumpimos la cuenta atrás y ejecutamos:

    mmc list
    part list mmc 0
    fatls mmc 0:1 /
    load mmc 0:1 ${scriptaddr} /boot.scr
    source ${scriptaddr}

    Una vez arrancado Debian comprobamos el modelo, el kernel, los dispositivos de almacenamiento y las interfaces de red:

    cat /proc/device-tree/model
    echo ""
    
    uname -a
    lsblk
    ip -br link
    
    dmesg \
      | grep -Ei "mt7986|mmc|mt753|sfp|firmware|error|fail"

    Ampliar la partición raíz al tamaño completo de la tarjeta

    La imagen inicial mide 4 GiB. Si la grabamos en una tarjeta mayor, el espacio restante quedará sin utilizar hasta que movamos la copia secundaria de la GPT y ampliemos la segunda partición.

    Primero identificamos el dispositivo que contiene la raíz:

    findmnt -no SOURCE /
    lsblk

    Si el sistema raíz es /dev/mmcblk0p2, ampliamos la partición ejecutando:

    sudo sgdisk -e /dev/mmcblk0
    sudo growpart /dev/mmcblk0 2
    sudo resize2fs /dev/mmcblk0p2

    Si la tarjeta aparece como /dev/mmcblk1, debemos sustituir mmcblk0 por mmcblk1 en los tres comandos.

    Diagnosticar problemas de arranque

    No aparece ninguna salida de U-Boot

    Comprobamos la posición de los interruptores de arranque, la alimentación del router, la configuración UART a 115200 baudios y las conexiones TX, RX y GND. Si la consola serie funciona pero no aparece U-Boot, revisamos especialmente la generación de BL2, el parámetro DRAM_USE_DDR4 y las escrituras en los sectores 34 y 13312.

    U-Boot arranca pero no encuentra la partición FAT32

    Comprobamos que la tarjeta sea detectada y que la primera partición pueda leerse:

    mmc rescan
    mmc dev 0
    part list mmc 0
    fatls mmc 0:1 /

    El kernel muestra un error VFS unable to mount root fs

    Este error suele significar que el kernel no puede acceder a la microSD, no reconoce ext4 o no encuentra la etiqueta BPI-R3-ROOT. Debemos comprobar que MMC_MTK, MMC_BLOCK y EXT4_FS estén compilados como parte del kernel, que hayamos aplicado el overlay SD y que la partición tenga la etiqueta correcta.

    Desde otro ordenador podemos comprobar la etiqueta ejecutando:

    sudo blkid /dev/sdX2

    La salida debe incluir:

    LABEL="BPI-R3-ROOT"

    Debian arranca pero no aparecen las interfaces de red

    Revisamos los mensajes del controlador Ethernet, DSA, el switch y el firmware:

    ip -details link
    
    dmesg \
      | grep -Ei "mediatek|mtk|mt753|dsa|phylink|sfp|firmware"

    También comprobamos qué opciones de red quedaron realmente activadas en el kernel:

    zgrep -E \
      "CONFIG_(NET_MEDIATEK_SOC|NET_DSA|NET_DSA_MT7530|SFP|PHYLINK)" \
      /proc/config.gz

    Si /proc/config.gz no existe, consultamos el archivo instalado en boot:

    grep -E \
      "CONFIG_(NET_MEDIATEK_SOC|NET_DSA|NET_DSA_MT7530|SFP|PHYLINK)" \
      "/boot/config-$(uname -r)"

    Para arrancar desde eMMC, NAND o NOR no debemos reutilizar esta imagen sin cambios. Tendremos que seleccionar la configuración correspondiente de U-Boot, cambiar BOOT_DEVICE al compilar Trusted Firmware-A y generar un Device Tree final aplicando el overlay del medio de almacenamiento elegido.

    Deja una respuesta