Experimentações com a TV box MXQ Pro 5G 4K (RK322x)

Peguei uma das TV boxes para experimentar; detalho aqui meus resultados, tentativas e tribulações:

Especificações

  • Nome de mercado: MXQ Pro 5G 4K
  • SoC: Rockchip RK322x (provavelmente RK3229)
  • RAM: 1GB[1]
  • Flash: 8GB, NAND Micron controlada via software[2]

Objetivos

  • Executar uma distribuição Linux de forma aceitável[3]
    • A partir de um cartão SD
    • A partir do flash interno
    • A partir da rede (i.e. PXE)
    • Com o kernel da Rockchip
    • Com um kernel mainline
      • Com aceleração 3D de hardware (via Lima)
      • Bonus: Com acesso ao flash interno
        • Com a raiz no flash interno

UART

A placa possui pads expostos conectados a uma interface UART, rotulados R, T e G e configurados para 1500000 baud durante a inicialização da placa (embora alguns pacotes, notavelmente o multitool, reconfigurem a interface para o mais comum 115200 baud durante o processo de boot). Headers foram soldados a um dos exemplares para simplificar o acesso a interface:


Software existente

Armbian

“Meta-distribuição” Linux oferecendo imagens baseadas em Debian e Ubuntu para diversas placas ARM, juntamente a um sistema de build altamente flexível. O projeto fornece imagens pré-compiladas para placas RK322x utilizadas em TV boxes. As imagens, tanto pré-compiladas quanto geradas a partir do sistema de build do projeto aparentam não possuir bootloader instalado da forma esperada pela placa (ver Processo de boot).

Multitool

Utilitário para manutenção e instalação de imagens desenvolvido pelo autor do port do Armbian para a placa, comunmente referenciado em tutoriais durante o processo de instalação. Tanto a imagem disponibilizada no tópico oficial do port quanto a compilada pelo autor do artigo aparentam funcionar conforme esperado no equipamento em questão. O script de build fornece um exemplo prático de como se empacotar uma imagem de sistema operacional para a placa, utilizando o idbloader proprietário da Rockchip.

Processo de boot

O conteudo a seguir ainda não é completamente entendido pelo autor, e pode não representar de fato o que ocorre durante o processo. Caveat emptor.

O processo de boot para os SoCs da Rockchip é documentado em https://opensource.rock-chips.com/wiki_Boot_option.

Existem dois métodos de inicialização oficialmente suportados para placas baseadas no RK3229, um utilizando o idbloader/miniloader proprietário da Rockchip, e outro utilizando o TPL/SPL do projeto U-Boot, completamente(?) software livre. Em ambos os casos o processo culmina na execução do U-Boot, comum em placas ARM de natureza similar, que fica responsável por carregar o kernel e passar o controle para o sistema operacional conforme apropriado.

Para ser selecionado[4] como dispositivo de boot, nosso cartão SD precisa que alguns componentes do metodo de inicialização selecionado estejam presentes em endereços pré determinados.

TODO: Escrever sobre os componentes do processo de boot, seus endereços e função durante a inicialização; exemplos de como preparar uma imagem para boot com ambos os métodos, assim que entender melhor como as coisas se encaixam.

Recursos, materiais e referências

  • CSC Armbian for RK322x TV box boards - Armbian Community Forums: Tópico oficial do port do Armbian para TV boxes utilizando SoCs RK322x; inclui informações relevantes sobre o estado do Linux e do Armbian nessas placas, peculiaridades de hardware notáveis e informações para instalação e depuração da mesma, juntamente informações sobre o multitool, desenvolvido pelo autor do tópico.

Status atual, e coisas a se fazer

Por agora, estou tentando:

  • Entender o processo de boot
  • Coagir alguma distribuição Linux a inicializar pelo cartão SD
    • Utilizando o idbloader
    • Utilizando o TPL/SPL do U-Boot

Para reempacotar a imagem do Armbian com o bootloader do minitool, tentei a seguinte sequencia de comandos:

Comandos utilizados
  # I'm assuming
  # - an armbian image at `armbian-latest.img'
  # - a checkout of multitool's repository at `multitool/'
  # - you are running as root (prefix sudo/doas/whatever as appropriate)


  # create our target image
  truncate -s 1280M repack.img

  # and a few temporary directories
  mkdir -p tmp mnt/boot

  # set up loopback devices
  # - for the source image
  source=$(losetup -Pf --show armbian-latest.img)
  # - for our target
  target=$(losetup -Pf --show repack.img)


  # set up the partition table. we'll be creating
  # - a FAT32 partition to hold u-boot's Extboot bootmeth configuration,
  #   sized 64M and starting at fdisk's default 2048th sector (enough to
  #   hold idbloader et al. behind it)
  # - a Linux partition spanning the rest of the image
  fdisk $target <<-EOF
  o
  n



  +64M
  t
  0b
  a
  n




  w
  EOF

  boot=${target}p1
  root=${target}p2

  source_root=${source}p1

  # copy over the rootfs
  pv $source_root | dd of=$root bs=64k


  # set up the u-boot configuration
  # for more information, see https://docs.u-boot.org/en/latest/develop/bootstd/overview.html

  mkfs.vfat $boot
  mount $boot mnt/boot

  boot_uuid=$(blkid -o value -s PARTUUID $boot)
  root_uuid=$(blkid -o value -s PARTUUID $root)

  mkdir mnt/boot/extlinux
  cat <<-EOF | tee mnt/boot/extlinux/extlinux.conf
  LABEL Armbian
    LINUX /kernel.img
    FDT /rk322x-box.dtb
    APPEND boot=UUID=$boot_uuid root=PARTUUID=$root_uuid rootwait console=ttyS2,115200 verbose=1 consoleblank=0
  EOF

  # copy over the kernel and device tree from multitool
  # TODO test with Armbian's kernel instead
  board=multitool/sources/rk322x
  cp $board/{kernel.img,rk322x-box.dtb} mnt/boot

  umount mnt/boot


  # install rockchip's idbloader
  tools=multitool/tools

  # pack uboot
  $tools/loaderimage \
      --pack --uboot $board/u-boot-dtb.bin \
      tmp/uboot.img \
      0x61000000
  # TODO ^ where does this address come from?

  # pack trustos
  $tools/loaderimage \
      --pack --trustos $board/rk3228_tee_ta-51.1.0-333-gc9d95d1.bin \
      tmp/trustos.img \
      0x68400000
  # TODO ^ ditto

  # install (see https://opensource.rock-chips.com/wiki_Boot_option for more details):
  # - uboot at 0x4000
  dd if=tmp/uboot.img of=$target seek=$((0x4000))
  # - tee at 0x6000
  dd if=tmp/trustos.img of=$target seek=$((0x6000))
  # - idbloader at 0x40
  dd if=$board/idbloader_ddr_1.11.img of=$target seek=$((0x40))


  # clean up after ourselves
  losetup -d $source $target
  rm -rf tmp
  rmdir mnt/* mnt

O resultado carrega a imagem do U-Boot empacotada com sucesso, mas falha ao carregar o kernel, proseguindo para a tentativa de boot via rede.

Os passos foram derivados do script de compilação do multitool, simplesmente por imaginar que seria mais simples seguir um exemplo funcional. Alguns dos componentes presentes aparentam provenir do repositório de binários da Rockchip, outros (como a imagem do U-Boot utilizada, ou a device tree) são de provenância desconhecida. Não esperava um boot bem-sucedido a partir dessa tentativa, embora eu esperasse que pelo menos o kernel fosse carregado com sucesso.

Não quero seguir o procedimento de instalação para NAND pelo multitool por agora pois não tenho certeza do processo de recuperação, e quero eventualmente utilizar o kernel mainline, de qualquer forma.


  1. É interessante notar que o dispositivo vem juntamente a uma etiqueta indicando que o mesmo haveria 3GB de RAM, e 4GB de flash interno, o que seria improvável, pois, assim como apontado pela datasheet do SoC, o mesmo só conseguiria endereçar até 2GB de memória. ↩︎

  2. Os chips NAND não possuem controlador, sendo conectados direto no SoC. O fork do kernel da Rockchip possui um módulo específico para se comunicar com os chips. Até a data atual, o kernel mainline não possui driver para acessar o armazenamento interno da placa. ↩︎

  3. Com “aceitável” sendo definido de forma parcialmente subjetiva :); ter as placas funcionando aceitavelmente como servidores seria conveniente; ter as placas funcionando aceitavelmente como terminais seria particularmente interessante. ↩︎

  4. Por quem? A BootRom? O bootloader instalado na NAND? ↩︎

1 curtida

Antes tarde do que nunca.

Errata: instalação do bootloader

O procedimento para instalação do bootloader não estava incorreto, mas em algum momento confundi setores com bytes, e acabei instalando o bootloader por cima da partição FAT. Mover a partição FAT mais para frente resolve o problema; o particionamento fica, então:

sfdisk $target <<-EOF
label: dos
1: start=32768, size=64M, type="W95 FAT32", bootable
2: start=163840, type=Linux
EOF

Utilizando o mesmo metodo, podemos instalar outras distribuições Linux além do Armbian na segunda partição, desde que seja possível popular a partição com uma raiz contendo binários ARMv7 compatíveis com o kernel utilizado. Pode-se, por exemplo, instalar o Debian “baunilha” através do debootstrap, ou o Void Linux, utilizando uma das tarballs de raiz disponíbilizados no site do projeto[1].



Compilando o kernel da Rockchip, problemas com HDMI e Wi-Fi

Até então estávamos utilizando a build do kernel que está no repositório do multitool. Essa build não inclui outros artefatos, como drivers compilados como módulos, e é de procedência incerta[2]. A Rockchip disponibiliza o código-fonte do seu fork do kernel no GitHub. Infelizmente, a versão disponibilizada é antiga, 4.4, e não compila com versões mais novas do GCC. Sabe-se que o GCC 13.2.0 não funciona para compilar o kernel fornecido, e que o GCC 6.3.0 funciona.

Podemos fazer uso da distribuição do GCC 6.3.0 fornecida pelo Debian nos repositórios do stretch, que pode ser facilmente instalado em um chroot utilizando debootstrap (prefixe sudo/doas/etc conforme apropriado):

release=https://cdimage.debian.org/mirror/cdimage/archive/9.13.0/amd64/jigdo-bd/debian-9.13.0-amd64-BD-1.jigdo
repo="$(wget -q -O - "$release" | gunzip | awk -F= '/snapshot.debian.org/ {print $2}')"
chroot="$PWD/stretch-chroot"

debootstrap --arch amd64 stretch "$chroot" "$repo"

Configuramos então o chroot e instalamos as ferramentas necessárias:

cd "$chroot"
cp -L /etc/resolv.conf etc
for fs in proc sys dev; do mount -R /$fs $fs; done
chroot . /bin/bash
# we're inside the chroot
# consider creating a new user and dropping privileges, maybe bind /home into the chroot too
apt install build-essential crossbuild-essential-armhf lzop bc python git

Por fim, adquirimos e compilamos o código-fonte do kernel para nossa placa:

git clone --depth=1 https://github.com/rockchip-linux/kernel
cd kernel
make -j$(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- rockchip_defconfig all

Copiamos então arch/arm/boot/zImage para a partição de boot, substituindo o kernel.img do multitool, e instalamos os módulos de Kernel para a raiz de nosso sistema, utilizando make modules_install com INSTALL_MOD_PATH apontado para a raiz do nosso sistema. Assumindo $boot_fs e $root_fs configurados conforme apropriado, temos:

[ -z "$boot_fs" ] || cp arch/arm/boot/zImage "$boot_fs"/kernel.img
[ -z "$root_fs" ] || make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH="$root_fs" modules_install

Infelizmente, o kernel compilado aparenta não inicializar a saída HDMI, mas funciona normalmente através da porta serial. Além disso, nenhum dos dois kernels parece oferecer suporte ao chip de Wi-Fi presente na placa, um SSV6256P. O driver disponibilizado na árvore da Rockchip aparenta ser compatível apenas com o SSV6051. Um driver compatível com o chip utilizado pode ser encontrado aqui, que foi confirmado a compilar juntamente a árvore do kernel utilizada, mas ainda não foi testado.


  1. Em particular, void-armv7l-ROOTFS-20250202.tar.xz foi testado e confirmado a funcionar. ↩︎

  2. Provavelmente vem dos pacotes do Armbian, mas de qualquer forma. ↩︎

1 curtida

Se o WiFi funcionar com esse módulo que você compillou funcionar, pelo menos para a captura de imagens não vai ser tanto problema não ter HDMI funcionando, então beleza.

Valeu pelo corre de testar com um compilador mais velho!

1 curtida

Consegui testar o módulo na placa, ele carrega o firmware e o arquivo de configuração normalmente, mas não detecta dispositivo nenhum. Teoricamente o chip é conectado via SDIO, mas /sys/bus/mmc/devices só mostra a entrada do cartão SD. Não sei se talvez tenha algo faltando na device tree, ou se eu tenho que fazer alguma coisa pra inicializar o chip antes. Talvez depois dê pra dar uma fuçada na ROM de Android que vem com elas pra ver se nos ajuda a descobrir alguma coisa.