Gerando uma Distribuição Linux com o Yocto na placa Rock 4 SE

Introdução

O Yocto Project é uma plataforma de desenvolvimento de software de código aberto que oferece um ambiente altamente configurável para a criação de distribuições de Linux. Ele fornece um conjunto de ferramentas para criação de imagens Linux personalizadas para vários dispositivos.

Embora possua uma curva de aprendizado notoriamente alta, o Yocto é, atualmente, o sistema padrão da indústria para geração de distribuições Linux para sistemas embarcados, devido à sua alta capacidade de customização e robustez.

Neste artigo, faremos uma build de uma Distribuição (Distro) Linux simples, em modo headless (sem componentes gráficos, apenas com o terminal padrão para interface com o usuário). Para tal, usaremos o Single-Board Computer (SBC) Rock 4 SE, da fabricante chinesa Radxa.


Hardware Necessário

A placa Rock 4 SE utiliza um System-on-Chip (SoC) Rockchip RK3399-T, que possui CPU com 2 núcleos ARM Cortex-A72 e mais 4 núcleos ARM-Cortex-A53, além de uma GPU Mali-T860MP4. A placa possui também várias interfaces de conectividade, como USB 3.0, Ethernet 1000 Base-T (Gigabit), Wifi 802.11 ac + BLE 5.0, saída HDMI, e suporta até 4GB de memória RAM do tipo LPDDR4.

Também tem suporte para uso de memória não-volátil do tipo , que será usada neste tutorial, através de um adaptador externo.

Além da placa Rock 4 SE, usaremos uma placa expansora para memória do tipo Embedded Multimedia Card (EMMC), obtida separadamente. Esta será a mídia de boot principal usada neste artigo, ao invés de um cartão SD convencional.

Também usaremos um cabo USB-C para alimentação da placa, um cabo USB tipo A Macho-Macho para gravação na EMMC, e um conversor USB-Serial para o interface com o terminal padrão do kernel.




Fundamentos do Yocto Project e seus principais componentes

Yocto é um sistema utilizado para gerar Distros Linux. Ele permite que desenvolvedores customizem suas imagem de acordo com necessidade específicas de projetos, ao invés de se ater à Distros padrão (como Debian, Arch, Fedora, etc). É composto por diversos componentes:

  • OpenEmbedded: Um conjunto de ferramentas e scripts que usados para a criação de distribuições Linux para sistemas embarcados. Este sistema utiliza o conceito de receitas (recipes) e camadas (layers). Estes componentes definem como compilar os diversos pacotes de software que compõe um sistema Linux, incluindo onde obter o código-fonte, como compilá-lo e quais dependências são necessárias.

  • Bitbake: Executor de tasks usado pelo sistema de build da OpenEmbedded. É responsável por interpretar os meta-dados das receitas, gerar a lista de tarefas a serem executadas na build e executá-las. Pode ser usado também para compilar components da imagem individualmente.

  • Poky: Distro Linux padrão do sistema OpenEmbedded. É usada como referência para gerar as outras distros e imagens do sistema. O processo de adaptação de uma nova placa ao sistema OpenEmbedded geralmente começa com a clonagem do repositório poky.

  • Receitas: Conjuntos de instruções para compilação de pacotes de software. Incluem instruções sobre onde baixar o código-fonte, como compilá-lo, como gerar um pacote a partir do código compilado, etc. São organizadas em arquivos com extensão *.bb.

  • Camadas (meta-layers): Um conjunto de receitas e configurações que são usadas para personalizar as imagens de sistema. Geralmente possuem o prefixo "meta" e são organizadas em "camadas" que podem ser facilmente inseridas ou removidas da build, através de um arquivo de configuração (bblayers.conf).

    Layers são, essencialmente, são conjuntos de receitas, geralmente organizadas em coleções logicamente correlatas. Neste artigo, faremos uso das layers poky, meta-rockchip (que contém definições para o SoC RK3399-T utilizado na Rock 4 SE) e meta-openembedded.


Configuração do ambiente de build

Máquina de Host: Usaremos como ambiente de desenvolvimento (host) um sistema Ubuntu 22.04. Nesta primeira etapa, o ambiente irá utilizar a configuração e ferramentas de build nativas. Em um artigo futuro, este processo será refeito utilizando o Docker, para gerar um container com o ambiente de build virtualizado.

Dependências: É necessário primeiramente realizar a instalação de todas as dependências necessárias na máquina Linux. Como as builds do Yocto podem apresentar variações em alguns pacotes dependendo da especificação de local (locale), este será predefinido aqui também:
╭─guilhermes@guilhermes ~ 
╰─$ sudo apt install gawk wget git diffstat unzip texinfo gcc build-essential chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev python3-subunit mesa-common-dev zstd liblz4-tool file locales && sudo locale-gen en_US.UTF-8

No exemplo, criaremos a pasta "yocto", que será o ponto raíz da build com o Yocto. 
╭─guilhermes@guilhermes ~ 
╰─$ mkdir yocto && cd yocto

Na pasta yocto, clone o repositório da layer poky diretamente do diretório oficial, no branch kirkstone (Yocto 4.0):
╭─guilhermes@guilhermes ~/yocto
╰─$ git clone git://git.yoctoproject.org/poky -b kirkstone

a seguir, clone também a layer oficial da Open Embedded (meta-openembedded), também no branch kirkstone:
╭─guilhermes@guilhermes ~/yocto
╰─$ git clone git://git.openembedded.org/meta-openembedded.git -b kirkstone

finalmente, clone a layer da Rockchip, meta-rockchip, também no branch kirkstone:
╭─guilhermes@guilhermes ~/yocto
╰─$ git clone https://github.com/radxa/meta-rockchip.git -b kirkstone-radxa
Obs.: Apesar de haver uma layer da própria Radxa (meta-radxa, https://github.com/radxa/meta-radxa), esta está, até a data de publicação deste artigo, desatualizada e sem suporte para esta placa. Desta forma, usaremos apenas a meta-rockchip.

Setup de variáveis de ambiente: Execute o script de inicialização padrão da layer poky (oe-init-build-env) para configurar o ambiente de build com as variáveis de ambiente adequadas:
╭─guilhermes@guilhermes ~/yocto 
╰─$ source poky/oe-init-build-env 

### Shell environment set up for builds. ###

You can now run 'bitbake <target>'

Common targets are:
    core-image-minimal
    core-image-full-cmdline
    core-image-sato
    core-image-weston
    meta-toolchain
    meta-ide-support

You can also run generated qemu images with a command like 'runqemu qemux86'

Other commonly useful commands are:
 - 'devtool' and 'recipetool' handle common recipe tasks
 - 'bitbake-layers' handles common layer tasks
 - 'oe-pkgdata-util' handles common target package tasks

Obs.: Se o terminal atual for fechado, será necessário chamar esse script para reiniciar as variáveis de ambiente para a build.

Customizar configurações: No diretório build/conf você encontrará os arquivos conf/local.conf e conf/bblayers.conf, que devem ser editados para customizar a build. Adicione o suporte aos SoC's da Rockchip inserindo as layers meta-rockchip e meta-openembedded/meta-oe ao seu arquivo conf/bblayers.conf, que terá esse formato:
```Python
# POKY_BBLAYERS_CONF_VERSION is increased each time build/conf/bblayers.conf
# changes incompatibly
POKY_BBLAYERS_CONF_VERSION = "2"

BBPATH = "${TOPDIR}"
BBFILES ?= ""

BBLAYERS ?= " \
  ${TOPDIR}/../meta-rockchip \
  ${TOPDIR}/../poky/meta \
  ${TOPDIR}/../poky/meta-poky \
  ${TOPDIR}/../poky/meta-yocto-bsp \
  ${TOPDIR}/../meta-openembedded/meta-oe \
  "
```
Depois, defina o argumento MACHINE (placa-alvo para a qual a imagem será gerada) no arquivo local.conf:
```Python
MACHINE ??= "rockchip-rk3399-rock-4se"
```
A lista de todas as placas-alvo para as quais as builds podem ser geradas se encontra em meta-rockchip/conf/machine. Um dos passos para customizar a build para uma nova placa é justamente a criação de um arquivo *.conf com configurações adequadas ao novo hardware.


Adaptações para gerar uma build sem componentes gráficos

Remover componentes de interface gráfica: Antes de dar início à compilação da imagem, faremos uma adaptação na configuração de build para garantir que nenhum componente do servidor gráfico e de áudio sejam inseridos na imagem final. Como pretendemos gerar um sistema headless, podemos remover todos os pacotes que dizem respeito a componetnes gráficos. Em local.conf:
```Python
DISTRO_FEATURES:remove = "x11 wayland directfb pulseaudio"
IMAGE_FEATURES:remove = "x11"
```

Obs.: Isto se faz necessário devido à uma peculiaridade da layer meta-rockchip: Mesmo ao gerar uma imagem mínima, como será feito a seguir (que, por padrão não deveria possuir nenhum componente gráfico habilitado, e ser apenas capaz de realizar um boot simples na placa), a inclusão desta layer à bblayers.conf faz com que sejam inclusos componentes de uma imagem completa.

Por exemplo, observe este snippet do arquivo rockchip-rk3399-rock-4se.conf, que define o MACHINE da Rock 4 SE:
```Python
# Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd
# Copyright (c) 2023, Radxa Ltd
# Released under the MIT license (see COPYING.MIT for the terms)

#@TYPE: Machine
#@NAME: RK3399 ROCK 4 SE

require conf/machine/include/rk3399.inc

KERNEL_DEVICETREE = "rockchip/rk3399-rock-4se.dtb"
UBOOT_MACHINE = "evb-rk3399_defconfig"
MACHINE_EXTRA_RRECOMMENDS:append = " \
linux-firmware-rk-cdndp \
"

IMAGE_INSTALL:append = " \
kernel-modules \
"
```

Este arquivo de configuração faz com que conf/machine/include/rk3399.inc seja sempre incluso à todas as imagens geradas, e este arquivo, por sua vez, inclui também componentes gráficos. Por isso estes componentes são removidos explicitamente na configuração acima. Com esta alteração, esperamos obter uma imagem Linux menor, e tempo de compilação mais rápido.

Build da imagem


Build mínima: Com as alterações feitas, execute o Bitbake para construir uma imagem mínima. Neste exemplo, usaremos a receita core-image-minimal:
```Python
bitbake core-image-minimal
```
Mesmo se tratando de uma imagem pequena, a build inicial pode levar tempo considerável. Builds subsequentes terão seu tempo reduzido, já que o Yocto irá ser capaz de identificar quais pacotes sofreram alterações, e irá realizar a build de modo incremental.

Resultado: Após a build ser concluída, você encontrará as imagens geradas no diretório build/tmp/deploy/images/rockchip-rk3399-rock-4se. Dentre os arquivos gerados, os mais significativos incluem:

  • update.img: Arquivo crucial para atualização da imagem. Geralmente, contém uma imagem completa que pode ser gravada na placa-alvo, contendo kernel, módulos, e rootfs. Em sistemas que realizam deploys e atualizações Over-the-Air com SoC's da Rockchip, este arquivo é de suma importância, já que é o arquivo que geralmente é enviado para regravar as placas com novas versões de software.

  • loader.bin: Arquivo do bootloader inicial, também conhecido como preloader para SoC's da Rockchip. Antes do bootloader principal (U-boot, neste caso) assumir o controle, este programa roda diretamente após o código de inicialização de fábrica da ROM do SoC, e é responsável por inicializar a memória principal e carregar os demais componentes da cadeia de boot. Este arquivo não precisa ser regravado todas as vezes - geralmente, apenas se for necessária a recuperação de erros fatais na placa.

  • boot.img: Contém a imagem do kernel, necessária para o boot de um sistema Linux.

  • rootfs.img (e demais arquivos com rootfs no nome): Contém o sistema de arquivos raíz (rootfs) da placa. Esta é a partição primária que contém o sistema operacional, suas aplicações e seus serviços.

  • módulos (modules--*.tgz): Arquivo comprimido de módulos do kernel. São drivers que podem ser carregados / descarregados dinamicamente pelo kernel.

  • u-boot.img e u-boot.bin: Imagens do bootloader principal (U-boot).

  • rk3399-rock-4se.dtb: Arquivo do tipo Device Tree Blob (dtb), que contém a device tree compilada a partir dos arquivos *.dts e *.dtsi na pasta do kernel. Se a placa-alvo for customizada, muito provavelmente serão arquivos distintos, com o hardware mapeado para tal.

Terminal serial para logs

Para verificar os logs da placa durante o processo de gravação na EMMC usaremos um conversor USB-serial externo conectado à maquina host. Conecte os pinos de TX (Pino 8), RX (Pino 10) e GND do conversor ao barramento de 40 pinos da lateral da placa. Este procedimento também está descrito em detalhes aqui.



Você pode utilizar um emulador de terminal no host para capturar o log do bootloader e kernel. No tutorial, será utilizado o picocom. Observe que, por padrão, esta interface serial utiliza uma baud não-padronizda de 1500000. Este valor pode ser alterado no arquivo conf/machine/include/rockchip-arm64-common.inc (mostrado abaixo), mas sugere-se seja mantido inalterado neste momento.
```Python
# Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd
# Released under the MIT license (see COPYING.MIT for the terms)

require conf/machine/include/rockchip-common.inc
include conf/machine/include/arm/arch-armv8a.inc

SERIAL_CONSOLES = "1500000;ttyFIQ0"
RK_USE_COMPRESSED_KERNEL = "0"
```

Substitua /dev/ttyUSB0 pela porta serial mapeada pelo kernel da máquina de host. Pode-se encontrar a interface tty mapeada no host com o comando dmesg | grep tty.

Gravação na EMMC

Ferramentas de gravação: Existem duas principais ferramentas para gravação para SoC's da Rockchip: uma solução proprietária, distribuída apenas como binário e sem código aberto, denominada Linux_Upgrade_Tool (https://github.com/vicharak-in/Linux_Upgrade_Tool) e uma solução open-source, rkdeveloptool (https://github.com/rockchip-linux/rkdeveloptool).

Geralmente, a ferramenta Linux_Upgrade_Tool já é incluída como parte de um repositório denominado rkbin (que contém diversas ferramentas adequadas para geração de imagem e gravação), com o nome upgrade_tool. Este repositório será usado neste artigo.

Clone o repositório https://github.com/rockchip-linux/rkbin.git para utilizar a upgrade_tool para realizar a gravação na EMMC da placa. Instale as dependências necessárias para utilizá-la:
╭─guilhermes@guilhermes ~/yocto
╰─$ sudo apt-get install libudev-dev libusb-1.0-0-dev
 
Modifique as permissões para que o binário possa ser executado:
╭─guilhermes@guilhermes ~/yocto
╰─$ git clone https://github.com/rockchip-linux/rkbin.git
cd rkbin/tools/
sudo chmod a+x upgrade_tool

Verificar modo Maskrom: Se ainda não estiver inserido, insira o módulo EMMC na Rock 4 SE, na parte de baixo. A partir desse ponto, admite-se que ainda não há uma imagem gravada no módulo EMMC.

Por padrão, se não houver nenhuma mídia de boot (ou imagem de um sistema operacional capaz de realizar boot), a Rock 4 SE será inicializada automaticamente em modo Maskrom. Neste modo, o bootloader em ROM do SoC irá se comunicar com a ferramenta upgrade_tool para que a gravação de uma nova imagem (composta pelo bootloader + imagem do kernel + rootfs) seja feita na mídia de armazenamento disponível.

Para tal, será usado cabo USB tipo A Macho-Macho, ligado entre a porta USB OTG 3.0 no canto superior esquerdo da placa e o PC host, como na figura abaixo.




Alimentação da placa: A placa é alimentada através de um cabo USB Tipo C (lateral), conforme figura abaixo.

Após ligá-la, o PC host deverá mapear (através do Cabo USB Tipo A macho-macho ligado à USB da placa) este dispositivo com o id 2207:330c. Verifique com o comando lsusb: A placa deverá ser mapeada com identificador "Fuzhou Rockchip Electronics".
╭─guilhermes@guilhermes ~/yocto
╰─$ lsusb
Bus 002 Device 003: ID 0bda:0411 Realtek Semiconductor Corp. Hub
Bus 002 Device 002: ID 0bda:0411 Realtek Semiconductor Corp. Hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 0b05:1939 ASUSTek Computer, Inc. AURA LED Controller
Bus 001 Device 005: ID 046d:c083 Logitech, Inc. G403 Prodigy Gaming Mouse
Bus 001 Device 007: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC

Utilize a ferramenta upgrade_tool para conferir se a placa é detectada corretamente pela máquina host. Por exemplo, partindo da pasta raíz ~/yocto:
╭─guilhermes@guilhermes ~/yocto 
╰─$ cd rkbin/tools/
╭─guilhermes@guilhermes ~/yocto/rkbin/tools ‹master●› 
╰─$ sudo ./upgrade_tool
Program Data in /root/.config/upgrade_tool
List of rockusb connected
DevNo=1 Vid=0x2207,Pid=0x330c,LocationID=1112   Mode=Maskrom
Found 1 rockusb,Select input DevNo,Rescan press <R>,Quit press <Q>:


Gravação de loader.bin: Assegurando-se que a placa está de fato em modo Maskrom, inicie a gravação de loader.bin com a ferramenta upgrade_tool. Por exemplo, partindo da pasta raíz ~/yocto:
╭─guilhermes@guilhermes ~/yocto/rkbin/tools ‹master●› 
╰─$ cd ~/yocto/rkbin/tools
╭─guilhermes@guilhermes ~/yocto/rkbin/tools ‹master●› 
╰─$ sudo ./upgrade_tool db ../../build/tmp/deploy/images/rockchip-rk3399-rock-4se/loader.bin
Program Data in /root/.config/upgrade_tool
Download boot ok.


Após gravar loader.bin com sucesso, você poderá ver o log de boot no terminal serial da placa:

Boot1 Release Time: Jun  2 2020 15:02:17, version: 1.26
CPUId = 0x0
SdmmcInit=2 0
BootCapSize=100000
UserCapSize=29820MB
FwPartOffset=2000 , 100000
UsbBoot ...72661
powerOn 84942

Gravação de update.img: Reinicie a placa (há um push-button de reset logo abaixo do conector USB-C da alimentação). A seguir, grave a imagem completa (update.img) com o comando abaixo:
╭─guilhermes@guilhermes ~/yocto 
╰─$ sudo ./upgrade_tool uf ../../build/tmp/deploy/images/rockchip-rk3399-rock-4se/core-image-minimal-rockchip-rk3399-rock-4se.update.img

Teste de boot: A placa irá reiniciar após a gravação e iniciar o boot do sistema. Após subir o bootloader e inicializar o kernel, a tela de login deve estar acessível pelo terminal serial:
udhcpc: broadcasting discover
udhcpc: broadcasting discover
[ 2943.951641] rk_gmac-dwmac fe300000.ethernet eth0: Link is Up - 1Gbps/Full - flow control rx/tx
udhcpc: broadcasting discover
udhcpc: broadcasting select for 192.168.0.103, server 192.168.0.1
udhcpc: lease of 192.168.0.103 obtained from 192.168.0.1, lease time 7200
/etc/udhcpc.d/50default: Adding DNS 8.8.8.8
/etc/udhcpc.d/50default: Adding DNS 4.4.4.4
done.
Starting syslogd/klogd: done

Poky (Yocto Project Reference Distro) 4.0.12 rockchip-rk3399-rock-4se ttyFIQ0

rockchip-rk3399-rock-4se login: 

No terminal da placa, entre com o usuário root. Não há senha definida por padrão.

Se os passos forem executados corretamente até aqui, você terá uma Distro simples funcional gerada com o Yocto e rodando na Rock 4 SE. Para verificar seu funcionamento, poderá acessar o terminal da placa em userspace e executar comandos. Por exemplo, para verificar a versão padrão do kernel da Rockchip usada nesta build:

root@rockchip-rk3399-rock-4se:~# uname -a
Linux rockchip-rk3399-rock-4se 5.10.160-rockchip-standard #1 SMP Fri Aug 4 02:10:04 UTC 2023 aarch64 GNU/Linux

Como removemos os componentes do servidor gráfico, a placa não deve ter interface gráfica e deve exibir apenas emular um terminal padrão na saída de vídeo HDMI, como visto na figura abaixo:




Obs.: Apesar de termos removido os componentes do servidor gráfico, a device tree permanece inalterada - logo, espera-se que os nós responsáveis pelo controle do módulo HDMI permaneçam funcionais, como demonstrado aqui. Se for necessário remover esta interface, o mais correto é desativar / remover estes nós das device trees que o kernel usa para esta placa.

Re-gravando e apagando a EMMC

Atualizando a imagem: Se a placa já tiver um bootloader gravado, não será necessário regravar loader.bin a cada atualização - basta interromper a sequência de boot no terminal do U-boot com Ctrl +C, digitar "download", e executar o comando acima para grravar update.img novamente. O timeout padrão para boot automático é de 3 segundos, então a sequência de escape Ctrl + C deve ser inserida rapidamente.

PartType: EFI
No misc partition
boot mode: None
CLK: (uboot. arml: enter 400000 KHz, init 816000 KHz, kernel 0N/A)
CLK: (uboot. armb: enter 24000 KHz, init 816000 KHz, kernel 0N/A)
  aplll 816000 KHz
  apllb 816000 KHz
  dpll 666000 KHz
  cpll 24000 KHz
  gpll 800000 KHz
  npll 600000 KHz
  vpll 24000 KHz
  aclk_perihp 133333 KHz
  hclk_perihp 66666 KHz
  pclk_perihp 33333 KHz
  aclk_perilp0 266666 KHz
  hclk_perilp0 88888 KHz
  pclk_perilp0 44444 KHz
  hclk_perilp1 100000 KHz
  pclk_perilp1 50000 KHz
Net:   eth0: ethernet@fe300000
Hit key to stop autoboot('CTRL+C'):  0 
=> download 
RKUSB: LUN 0, dev 0, hwpart 0, sector 0x0, count 0x3a3e000

Apagar conteúdo da EMMC: Para apagar totalmente a EMMC, deve-se recolocar a placa em modo Maskrom. Para tal, é necessário desenergizar a placa, colocar um curto no no pad de SW1500 (não-montado por padrão) com uma pinça/chave/etc, e então energizar a placa novamente. Se o processo for feito com sucesso, a placa será novamnte detectada com o comando lsusb, conforme descrito anteriormente. Isto faz com que o clock da EMMC seja desativado, e a placa não reconhece uma mídia de boot ao iniciar, fazendo com que entre no modo Maskrom.



Para facilitar este processo, pode-se também soldar um push-button em SW1, mostrado na foto abaixo. Deste modo, apenas segure SW1 e, sem soltá-lo, resete a placa com o push-button abaixo do conector USB-C da alimentação.



Se a placa entrar corretamente em modo Maskrom novamente, não enviará nada em seu terminal serial, e poderá ter a EMMC apagada com o comando:
╭─guilhermes@guilhermes ~/yocto/rkbin/tools ‹master●› 
╰─$ sudo ./upgrade_tool ef ../../build/tmp/deploy/images/rockchip-rk3399-rock-


Inserindo pacotes à imagem

A melhor prática para inserir pacotes em uma Distro gerada com o Yocto é utilizar um arquivo de receita de imagem próprio para cada imagem. No entanto, por simplicidade, também podemos testar a build adicionando pacotes diretamente à local.conf.

Neste exemplo, adicionaremos o pacote util-linux à imagem para utilizar o programa lsblk. Ao tentar executá-lo no terminal da placa-alvo, observamos que não está inserido na imagem:
root@rockchip-rk3399-rock-4se:~# lsblk
-sh: lsblk: not found
root@rockchip-rk3399-rock-4se:~#


Adicione este pacote à local.conf:
```Python

DISTRO_FEATURES:remove = "x11 wayland directfb pulseaudio"
IMAGE_FEATURES:remove = "x11"
IMAGE_INSTALL:append = " util-linux"
```

e recompile a imagem com o comando bitbake core-image-minimal. Observe que, como a build é incremental, o tempo de recompilação é consideravelmente menor, uma vez que há apenas a necessidade de realizar o fetch / compilação de util-linux e re-gerar o rootfs (com a task core-image-minimal-1.0-r0 do_rootfs).

╭─guilhermes@guilhermes ~/yocto/build 
╰─$ bitbake core-image-minimal
Loading cache: 100% |########################################################################################################################| Time: 0:00:00
Loaded 2820 entries from dependency cache.
NOTE: Resolving any missing task queue dependencies

Build Configuration:
BB_VERSION           = "2.0.0"
BUILD_SYS            = "x86_64-linux"
NATIVELSBSTRING      = "universal"
TARGET_SYS           = "aarch64-poky-linux"
MACHINE              = "rockchip-rk3399-rock-4se"
DISTRO               = "poky"
DISTRO_VERSION       = "4.0.12"
TUNE_FEATURES        = "aarch64 armv8a crc"
TARGET_FPU           = ""
meta-rockchip        = "kirkstone-radxa:ef027f4455f705da7b4c24a63570ad9f0c512ff9"
meta                 
meta-poky            
meta-yocto-bsp       = "kirkstone:5d822b31316663c838c5864ab68b28fb3ca41351"
meta-oe              = "kirkstone:a88cb922f91fda95e8a584cee3092083d5ad3e98"

Initialising tasks: 100% |###################################################################################################################| Time: 0:00:01
Sstate summary: Wanted 8 Local 6 Mirrors 0 Missed 2 Current 1125 (75% match, 99% complete)
Removing 2 stale sstate objects for arch rockchip_rk3399_rock_4se: 100% |####################################################################| Time: 0:00:00
NOTE: Executing Tasks
NOTE: Tasks Summary: Attempted 2984 tasks of which 2971 didn't need to be rerun and all succeeded.

Reinicie a placa, interrompa a sequência do U-boot com a sequência de escape Ctrl + C e digite novamente "download" no terminal do U-boot. Grave update.img com as instruções acima. Observe que agora este pacote estará presente na imagem:

root@rockchip-rk3399-rock-4se:~# lsblk
NAME         MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
mmcblk0      179:0    0 29.1G  0 disk 
|-mmcblk0p1  179:1    0    4M  0 part 
|-mmcblk0p2  179:2    0    4M  0 part 
|-mmcblk0p3  179:3    0 29.9M  0 part 
`-mmcblk0p4  179:4    0 29.1G  0 part /
mmcblk0boot0 179:32   0    4M  1 disk 
mmcblk0boot1 179:64   0    4M  1 disk 
zram0        254:0    0    0B  0 disk 


Modificando o kernel com o devtool

A ferramenta devtool (https://docs.yoctoproject.org/ref-manual/devtool-reference.html) é parte da suite de ferramentas do Yocto Project. Seu propósito é auxiliar no desenvolvimento e depuração de receitas e aplicações. Esta ferramenta facilita o processo de modificação e versionamento ds receitas de uma imagem - dentre elas, a receita do próprio kernel (que, para SoC's da Rockchip, é denominada linux-rockchip).

Detalhes mais aprofundados sobre o desenvolvimento do kernel e do uso do devtool fogem do escopo deste artigo. No entanto, podemos demonstrar uma forma de realizar alterações pontuais no kernel com este utilitário.

Este comando cria uma layer denominada workspace, para a qual extrai o código-fonte das receitas. Isto permite que este código seja modificado em um ambiente isolado da pasta de build, e facilita o processo de criação de patches, mantendo o ambiente de build organizado.

O comando devtool modify linux-rockchip pode ser usado para extrair os arquivos-fonte do kernel na pasta workspace, como visto a seguir. A partir da pasta ~/yocto/build:

╭─guilhermes@guilhermes ~/yocto/build 
╰─$ devtool modify linux-rockchip                                                                                                              
NOTE: Starting bitbake server...
NOTE: Reconnecting to bitbake server...
NOTE: Retrying server connection (#1)...
Loading cache: 100% |########################################################################################################################| Time: 0:00:00
Loaded 2820 entries from dependency cache.
NOTE: Resolving any missing task queue dependencies

Build Configuration:
BB_VERSION           = "2.0.0"
BUILD_SYS            = "x86_64-linux"
NATIVELSBSTRING      = "universal"
TARGET_SYS           = "aarch64-poky-linux"
MACHINE              = "rockchip-rk3399-rock-4se"
DISTRO               = "poky"
DISTRO_VERSION       = "4.0.12"
TUNE_FEATURES        = "aarch64 armv8a crc"
TARGET_FPU           = ""
meta-rockchip        = "kirkstone-radxa:ef027f4455f705da7b4c24a63570ad9f0c512ff9"
meta                 
meta-poky            
meta-yocto-bsp       = "kirkstone:5d822b31316663c838c5864ab68b28fb3ca41351"
meta-oe              = "kirkstone:a88cb922f91fda95e8a584cee3092083d5ad3e98"
workspace            = "<unknown>:<unknown>"

Initialising tasks: 100% |###################################################################################################################| Time: 0:00:00
Sstate summary: Wanted 1 Local 0 Mirrors 0 Missed 1 Current 105 (0% match, 99% complete)
NOTE: Executing Tasks
NOTE: Tasks Summary: Attempted 475 tasks of which 464 didn't need to be rerun and all succeeded.
INFO: Copying kernel config to srctree
INFO: Source tree extracted to /home/guilhermes/yocto/build/workspace/sources/linux-rockchip
INFO: Recipe linux-rockchip now set up to build from /home/guilhermes/yocto/build/workspace/sources/linux-rockchip

Receitas extraídas com o devtool estão localizadas na pasta sources:

╭─guilhermes@guilhermes ~/yocto/build/workspace 
╰─$ cd sources                                                                                                                          
╭─guilhermes@guilhermes ~/yocto/build/workspace/sources 
╰─$ ls
linux-rockchip

Como exemplo, faremos uma pequena modificação na device tree da placa, e testaremos na placa-alvo. Observe o conteúdo do campo "Machine Model" no log do kernel da placa:

root@rockchip-rk3399-rock-4se:~# dmesg | grep model
[    2.853312] Machine model: Radxa ROCK 4SE
[    5.125233] rockchip-dmc dmc: could not find power_model node
[    5.357372] midgard ff9a0000.gpu: Using configured power model mali-simple-power-model, and fallback mali-simple-power-model
[    5.357563] I : [File] : drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c; [Line] : 417; [Func] : midgard_kbase_devfreq_init(); success initing power_model_simple.

Iremos alterar esta string modificando o campo "Model" do arquivo linux-rockchip/arch/arm64/boot/dts/rockchip/rk3399-rock-4se.dts. No exemplo, modificamos de "Radxa ROCK 4SE" para "Custom dts Yocto demo MP":
```C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2019 Akash Gajjar <Akash_Gajjar@mentor.com>
 * Copyright (c) 2019 Pragnesh Patel <Pragnesh_Patel@mentor.com>
 */

/dts-v1/;
#include "rk3399-rock-pi-4.dtsi"
#include "rk3399-t-opp.dtsi"

/ {
model = "Custom dts Yocto demo MP";
compatible = "radxa,rock-4se", "rockchip,rk3399";
};
```
De volta à pasta de build, recompile o kernel com  o comando devtool build linux-rockchip. Recompile agora a imagem completa com o comando bitbake core-image-minimal. Grave novamente o arquivo re-gerado update.img na placa.

Após o boot, rode novamente o comando dmesg | grep model na Rock 4 SE - como esperado, o campo "Machine Model" foi alterado de acordo com a modificação feita em  rk3399-rock-4se.dts, comprovando que a device tree foi alterada corretamente.

root@rockchip-rk3399-rock-4se:~# dmesg | grep model
[    2.870466] Machine model: Custom dts Yocto demo MP
[    5.161135] rockchip-dmc dmc: could not find power_model node
[    5.385885] midgard ff9a0000.gpu: Using configured power model mali-simple-power-model, and fallback mali-simple-power-model
[    5.386079] I : [File] : drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c; [Line] : 417; [Func] : midgard_kbase_devfreq_init(); success initing power_model_simple.

Conclusão

Neste artigo apresentamos uma forma de gerar uma distribuição Linux headless simples com o Yocto Project para a placa Rock 4 SE. Para tal, foi feito uso de um conjunto de layers padronizadas da distro padrão poky e da layer meta-rockchip.

O Yocto Project possui uma alta complexidade, mas, em contrapartida, também possui enorme flexibilidade e robustez para gerar imagens Linux customizadas, tornando-se o padrão da indústria para este propósito.

Postar um comentário

Deixe seu comentário ou sua sugestão!

Postagem Anterior Próxima Postagem

Formulário de contato