Construção de um Sistema de Recuperação para Rockchip RK3566 com Buildroot e U-Boot

Configuração do Ambiente e Hardware

Plataforma alvo: Orange Pi 3B (SoC Rockchip RK3566)

  • Armazenamento: eMMC de 32GB
  • Memória: 2GB LPDDR4
  • Bootloader: U-Boot v2017.09
  • Kernel: Linux 5.10
  • Distribuição base: Ubuntu 22.04 LTS (Jammy)
  1. Geração da Imagem de Recuperação (recovery.img)

O sistema de recuperação é empacotado como uma imagem FIT (Flattened Image Tree) contendo o kernel, a árvore de dispositivos (DTB) e um sistema de arquivos raiz em memória (ramdisk).

1.1 Configuração e Compilação do Kernel

Para utilizar um ramdisk, o kernel precisa ter o suporte a dispositivos de bloco em RAM habilitado.

cd /caminho/para/o/kernel
make menuconfig

Navegue até as seguintes opções:

General setup  --->
    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support

Device Drivers  --->
     [*] Block devices  --->
          <*>   RAM block device support
          (1)     Default number of RAM disks
        (131072) Default RAM disk size (kbytes)

Isso gera as seguintes flags no .config:

CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=1
CONFIG_BLK_DEV_RAM_SIZE=131072

Após salvar, compile o kernel para obter o Image.gz e copie os binários da árvore de dispositivos (.dtb) correspondentes às versões do hardware (v1.1 e v2.1).

1.2 Construção do Rootfs com Buildroot

Utilizaremos o Buildroot para gerar um sistema de arquivos mínimo.

wget https://buildroot.org/downloads/buildroot-2024.02.4.tar.gz
tar -xvf buildroot-2024.02.4.tar.gz
cd buildroot-2024.02.4

Defconfig Personalizado

Crie um arquivo de configuração base (rk356x_recovery_defconfig):

BR2_aarch64=y
BR2_cortex_a55=y
BR2_TOOLCHAIN_BUILDROOT_CXX=y
BR2_TARGET_GENERIC_HOSTNAME="rk356x-rec"
BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y
BR2_ROOTFS_MERGED_USR=y
BR2_PACKAGE_E2FSPROGS=y
BR2_PACKAGE_OPENSSH=y
BR2_TARGET_ROOTFS_CPIO=y
BR2_TARGET_ROOTFS_CPIO_GZIP=y
BR2_TARGET_ROOTFS_EXT2=y
BR2_TARGET_ROOTFS_EXT2_4=y
BR2_TARGET_ROOTFS_EXT2_SIZE="96M"

Overlay e Scripts de Pós-Build

Adicione um diretório de overlay para customizar o sistema. No script de pós-construção (post-build.sh), inclua a lógica para copiar os módulos de rede (como o driver do PHY YT8531C) e configurar a montagem das partições oem e userdata.

# Exemplo de fragmento do post-build.sh
KERNEL_VER="5.10.160-rockchip-rk356x"
if [ -d "${SDK_DIR}/lib/modules/$KERNEL_VER" ]; then
    mkdir -p $TARGET_DIR/lib/modules/$KERNEL_VER
    cp -a ${SDK_DIR}/lib/modules/$KERNEL_VER/*.ko $TARGET_DIR/lib/modules/$KERNEL_VER/
fi

# Configuração de montagem via inittab para evitar problemas com PARTLABEL
echo "::sysinit:/bin/mount -t ext4 -r /dev/block/by-name/image1 /oem" >> $TARGET_DIR/etc/inittab
echo "::sysinit:/bin/mount -t ext4 /dev/block/by-name/image2 /userdata" >> $TARGET_DIR/etc/inittab

Compile o Buildroot:

make rk356x_recovery_defconfig
make -j$(nproc)

O resultado será um arquivo rootfs.ext2 que deve ser comprimido com gzip para servir como ramdisk.

1.3 Empacotamento da Imagem FIT

Crie um arquivo de origem ITS (recovery.its) para combinar o kernel, DTBs e ramdisk.

/dts-v1/;
/ {
    description = "RK3566 Recovery FIT Image";
    images {
        kernel {
            description = "Linux Kernel";
            data = /incbin/("Image.gz");
            type = "kernel";
            arch = "arm64";
            os = "linux";
            compression = "gzip";
            load = <0x00280000>;
            entry = <0x00280000>;
            hash { algo = "sha256"; };
        };
        fdt_v1 {
            description = "DTB v1";
            data = /incbin/("rk3566-orangepi-3b.dtb");
            type = "flat_dt";
            arch = "arm64";
            compression = "none";
            load = <0x08300000>;
            hash { algo = "sha256"; };
        };
        fdt_v2 {
            description = "DTB v2";
            data = /incbin/("rk3566-orangepi-3b-v2.dtb");
            type = "flat_dt";
            arch = "arm64";
            compression = "none";
            load = <0x08300000>;
            hash { algo = "sha256"; };
        };
        ramdisk {
            description = "Buildroot Ramdisk";
            data = /incbin/("ramdisk.gz");
            type = "ramdisk";
            arch = "arm64";
            os = "linux";
            compression = "gzip";
            hash { algo = "sha256"; };
        };
    };
    configurations {
        default = "conf_v1";
        conf_v1 {
            description = "Boot with DTB v1";
            kernel = "kernel";
            fdt = "fdt_v1";
            ramdisk = "ramdisk";
        };
        conf_v2 {
            description = "Boot with DTB v2";
            kernel = "kernel";
            fdt = "fdt_v2";
            ramdisk = "ramdisk";
        };
    };
};

Use a ferramenta mkimage do U-Boot para gerar o binário final:

mkimage -f recovery.its -E -p 0x800 recovery.img

  1. Adaptação do U-Boot para Boot Condicional

Para permitir que o sistema entre em modo de recuperação através de um pino GPIO ou variável de ambiente, modificaremos o código-fonte do U-Boot.

2.1 Detecção de GPIO no Boot Mode

Edite o arquivo arch/arm/mach-rockchip/boot_mode.c para ler o estado do pino GPIO3_D3. Se estiver em nível alto, o modo de boot é forçado para recuperação.

#include <asm/gpio.h>

#define GPIO3_BASE_ADDR      0xfe760000
#define PIN_OFFSET_D3        27

int rockchip_get_boot_mode(void)
{
    struct rockchip_gpio_regs *gpio_regs = (void *)GPIO3_BASE_ADDR;
    int pin_state;

    // Configurar pino como entrada
    clrbits_le32(&gpio_regs->swport_ddr, (1UL << PIN_OFFSET_D3));
    
    // Ler estado do pino
    pin_state = (readl(&gpio_regs->ext_port) >> PIN_OFFSET_D3) & 0x1;

    if (pin_state) {
        env_set("update_mode", "recovery");
        env_save();
    }

    char *mode = env_get("update_mode");
    if (mode && (strcmp(mode, "recovery") == 0 || strcmp(mode, "update") == 0)) {
        printf("Boot mode: recovery\n");
        return BOOT_MODE_RECOVERY;
    }

    return BOOT_MODE_NORMAL;
}

2.2 Ajuste no Comando de Boot e FIT

Em include/configs/rockchip-common.h, priorize o comando boot_fit antes do distro_bootcmd:

#define RKIMG_BOOTCOMMAND \
    "boot_fit;" \
    "run distro_bootcmd;" \
    "boot_android ${devtype} ${devnum};"

No arquivo cmd/bootfit.c, ajuste a função do_boot_fit para selecionar a configuração correta do FIT com base na variável fdtfile:

char *current_fdt = env_get("fdtfile");
if (current_fdt && strstr(current_fdt, "v2.dtb")) {
    strcat(fit_addr_str, "#conf_v2");
} else {
    strcat(fit_addr_str, "#conf_v1");
}

2.3 Desabilitando OP-TEE

Para evitar conflitos de API com o OP-TEE durante o boot via FIT, desative as opções relacionadas no defconfig do U-Boot (configs/orangepi-3b-rk3566_defconfig):

# CONFIG_OPTEE_CLIENT is not set
# CONFIG_OPTEE_V2 is not set

Recompile o U-Boot e grave as imagens idbloader.img e u-boot.itb no dispositivo.

  1. Layout de Partições e Integração na Imagem Linux

A imagem final do sistema operacional precisa conter partições dedicadas para a recuperação e para imagens de fábrica/atualização.

3.1 Modificação do Script de Criação de Imagem

No script debootstrap.sh (ou equiavlente do SDK), adicione as novas partições à tabela GPT:

  • recovery: 128MB (para o recovery.img)
  • image1: 4GB (imagem de fábrica)
  • image2: 4GB (imagem de atualização OTA)
# Exemplo de adição no script de particionamento
RECOVERY_PART=$((NEXT_PART++))
RECOVERY_SIZE=128

IMAGE1_PART=$((NEXT_PART++))
IMAGE1_SIZE=4096

IMAGE2_PART=$((NEXT_PART++))
IMAGE2_SIZE=4096

# Lógica de gravação do recovery.img
if [ -f "$OUTPUT_DIR/recovery.img" ]; then
    dd if="$OUTPUT_DIR/recovery.img" of="${LOOP_DEV}p${RECOVERY_PART}" bs=1M status=none
fi

  1. Validação e Testes no Sistema

4.1 Expansão do Rootfs em Tempo de Execução

Como a imagem base tem tamanho fixo, ao bootar no sistema normal, expanda a partição rootfs para ocupar o espaço restante do eMMC/SD:

fdisk /dev/mmcblk1
# Delete a partição rootfs e recrie-a com o tamanho máximo
partprobe /dev/mmcblk1
resize2fs /dev/mmcblk1p5

4.2 Testando o Acesso via Sysfs no Recovery

Dentro do ambiente de recuperação (Buildroot), o acesso aos pinos GPIO pode ser feito via sysfs. O pino GPIO3_D3 corresponde ao índice 123 (base 96 + 27).

echo 123 > /sys/class/gpio/export
echo in > /sys/class/gpio/gpio123/direction
cat /sys/class/gpio/gpio123/value

  1. Manipulação do Ambiente do U-Boot pelo Linux

Para ler e alterar as variáveis do U-Boot diretamente pelo Linux (útil para scripts de atualização OTA), compile a ferramenta fw_printenv fornecida no código-fonte do U-Boot.

5.1 Compilação e Configuração

cd /caminho/para/uboot
make envtools CROSS_COMPILE=aarch64-linux-gnu-

Copie o binário fw_printenv para o overlay do Buildroot e crie um link simbólico para fw_setenv.

Configure o arquivo /etc/fw_env.config apontando para o offset correto no dispositivo de bloco:

# Dispositivo       Offset      Tamanho
/dev/mmcblk1        0x3f8000    0x8000

5.2 Uso em Scripts de Atualização

Agora é possível alternar o modo de boot programaticamente:

# Forçar boot no recovery na próxima reinicialização
fw_setenv update_mode recovery
reboot

# Limpar a flag após a atualização
fw_setenv update_mode normal

Tags: Rockchip RK3566 U-Boot Buildroot Linux

Publicado em 7-5 06:49