Referência técnica
Kernel, Mesa, U-Boot, DTS, pipeline de build
Pipeline de build
make docker-RK3326 executa todo o sistema de stages no estilo LibreELEC dentro do Docker. O build atual instala 543 pacotes na imagem.
toolchain cross-gcc / glibc / binutils (stage do host)
linux kernel 6.12 LTS + DTS do RK3326 + patches do ArchR
mali-bifrost mali_kbase out-of-tree + patch de PM unbalance do ArchR
mesa Panfrost (GLES 3.1, +speed +lto)
zlib (-ng) zlib-ng 2.2.4 em modo --zlib-compat (libz.so.1)
retroarch 1.x + conjunto de cores libretro
emulators Flycast, PPSSPP, melonDS, DraStic, Yabasanshiro, Mupen64Plus standalone
es EmulationStation (archr-linux/emulationstation-next)
image FAT32 BOOT (256 MB) + ext4 ROOT (4608 MB) + ext4 STORAGEVersões atuais: gcc 14.2.0, Linux 6.12.94, Mesa 26.1.3, systemd 255.8, U-Boot v2025.10, pacman 7.0.0.
Saída em target/<Ver of="imageBase" />-{original,clone}.img.gz.
Kernel
Source Linux 6.12 LTS (kernel.org, patches do ArchR e da Rockchip)
Toolchain gcc do stage de toolchain do ArchR
Config projects/ArchR/devices/RK3326/linux/linux.aarch64.conf
HZ 300
PREEMPT CONFIG_PREEMPT=y (A/B com PREEMPT_VOLUNTARY pendente de bench)
DEBUG todos os sanitizers / KASAN / KMEMLEAK / FTRACE desligados no release
DEBUG_FS y (intencional, necessário para debugfs do CMA e introspecção de PSI)
CMA 96 MB (subiu de 64 MB após auditoria; veja /docs/features)
ZRAM lzo-rle por padrão (zstd opcional via memory-manager)
ZSWAP desabilitado (zswap.enabled=0 na cmdline)DTS
Origem: rk3326-gameconsole-r36s.dts (placa) + rk3326-gameconsole-r3xs.dtsi (família de placas) + px30.dtsi (SoC) + 000-rk3326-dts.patch (overrides do ArchR).
| Nó | O que mudamos | Por quê |
|---|---|---|
cpu0_opp_table | Adicionada OPP turbo de 1512 MHz @ 1.4 V; mantida a escada completa 408–1416 | Acessível via "Enable CPU Overclock" |
Regulador vdd_arm | regulator-max-microvolt 1.35 V → 1.45 V | Caso contrário o kernel rejeita a OPP de 1512 silenciosamente |
gpu_opp_table | Restauradas as OPPs inferiores de 200/300/400 MHz; adicionadas as superiores de 600/650 MHz | OPPs baixas são necessárias para o boot; 650 MHz é o turbo diário a 1.150 V |
dmc / dfi | Definidos explicitamente para que o devfreq possa ser controlado | O px30.dtsi upstream os mantém desabilitados |
gpu | Adicionados clock-names="bus", resets, power_policy="coarse_demand", power_models | Necessário para panfrost/mali_kbase modernos |
Alias ethernet0 | Adicionado | Nomeação futura para hot-plug de USB-Ethernet |
| Power model | mali-simple-power-model + mali-g31-power-model | DVFS com consciência térmica |
Patches de kernel
Patches de kernel específicos do ArchR mais relevantes (projects/ArchR/packages/linux-drivers/mali-bifrost/patches/6.12-LTS/ e projects/ArchR/packages/linux/patches/6.12-LTS/):
mali-bifrost/002-fix-unbalanced-regulator-clk-pm.patch: elimina os avisosunbalanced disables for vdd_logicquandovdd_logicé compartilhado com rockchip-pm-domain. Adiciona um flaggpu_power_heldque acompanha o estado de enable do próprio driver.linux/0005-drm-panfrost-Add-SYSTEM_TIMESTAMP*.patch: requerido pelo Mesa atual para queriesGL_TIMESTAMPeVK_KHR_calibrated_timestamps.linux/0006-drm-panfrost-Add-cycle-counter-job-requirement.patch: encanamento paraVK_KHR_shader_clock.linux/0007-0010-drm-panfrost-Add-BO-labelling*.patch: auxílio de debug; o Mesa chama o novo IOCTL ainda que os labels sejam apenas para debug.
Mesa
Compilado com +speed +lto em packages/graphics/mesa/package.mk. Flags do configure:
-Dgallium-drivers=panfrost -Dvulkan-drivers=
-Dgles1=disabled -Dgles2=enabled
-Dglvnd=disabled -Dplatforms=wl
-Dglx=disabled -Dshader-cache=enabled
-Dopengl=true -Dgbm=enabled
-Degl=enabled -Dlibunwind=disabled
-Dbuild-tests=false -Ddraw-use-llvm=false-Dgallium-drivers=panfrost apenas, sem fallback de software no release (o overhead do LLVMpipe seria maior que o hardware no G31). -Dvulkan-drivers= vazio porque o PanVK não é conformante em Bifrost v9.
Variáveis de runtime
Exportadas globalmente em /etc/profile.d/041-panfrost:
PAN_MESA_DEBUG=forcepack # dica de format-pack específica do G31
MESA_NO_ERROR=1
MESA_SHADER_CACHE_DIR=/storage/.cache/mesa_shader_cache
MESA_SHADER_CACHE_MAX_SIZE=128MBWhitelist MESA_GLTHREAD=true por emulador, apenas Flycast e PPSSPP standalone (veja GPU driver e o docs/mesa-glthread-matrix.md no repo).
libmali (alternativo)
mali_kbase out-of-tree r52p0-00eac0 com o patch de PM do regulador/clk. Trocado no boot via /usr/bin/gpudriver --start, que lê /storage/.config/system/configs/system.cfg → gpu.driver. Fallback automático se algum dos drivers falhar em se ligar ao nó da GPU.
U-Boot
| Original | Clone | |
|---|---|---|
| Pacote | u-boot-legacy | u-boot |
| Origem | árvore BSP da Hardkernel (archr-linux/hardkernel-uboot) | U-Boot mainline v2025.10, mais um patch para o clone K36 com eMMC |
| Defconfig | archr_rk3326_defconfig | rk3326-handheld_defconfig |
| Seleção de placa | o hwrev no canal 0 do SARADC escolhe o DTB dentro de /dtbs/ | igual, através do boot.scr |
As duas variantes trazem boot.scr e extlinux/extlinux.conf, e as duas carregam o overlay de painel a partir de FDTOVERLAYS /overlays/mipi-panel.dtbo. Os dois loaders não são intercambiáveis: um clone não dá boot com o build do BSP. O ArchR entrega os dois e o build escolhe pela variante (make docker-RK3326 produz as duas imagens).
Layout da imagem
MBR (msdos), 3 partições primárias, a primeira começa no setor 32768
├── 1 FAT32 ARCHR 256 MB clusters de 16 KB, kernel, DTBs, overlays/, U-Boot
├── 2 ext4 ARCHR_ROOT 4608 MB sistema, montado read-only em runtime
└── 3 ext4 STORAGE resto gravável: configs, ROMs, save states, screenshotsOs tamanhos das partições vêm de distributions/ArchR/options (BOOT_SIZE, ROOT_SIZE, SYSTEM_PART_START) e os rótulos de DISTRO_BOOTLABEL, DISTRO_ROOTLABEL e DISTRO_DISKLABEL. A terceira partição sai da fábrica com 32 MB e cresce para o resto do cartão no primeiro boot.
O mkimage formata a partição de boot com mkfs.vfat -F 32 -S 512 -s 32, ou seja, clusters de 16 KB, e depois confere com fsck.fat -n, só leitura. Nada no build nem no dispositivo repara esse filesystem automaticamente.
Fluxo de boot
0.0 POR / SPL (na ROM)
0.x U-Boot lê SARADC ch0 → ID da placa
0.5 carrega DTB da placa + overlay do painel a partir de /flash/
0.7 splash do initramfs (BMP via xxd, /init estático em aarch64)
~7 entrega ao kernel
~9 target do systemd
~12 archr-autostart (quirks + governor + display + bluetooth + checagem de ssh)
~17 EmulationStation
~19 pronto para jogarO trace é escrito em /storage/.boot_last_step antes de cada script de autostart rodar, e apagado em caso de sucesso. Se o boot anterior travou, o arquivo /storage/.boot_last_hang nomeia o script que travou. Inclua isso em relatos de bug.
Painéis de display
O ArchR não distribui overlays de painel prontos. São combinações demais de placa e painel para enumerar, então o overlay é montado por aparelho a partir do DTB do cartão original do próprio console.
A imagem carrega um diretório overlays/ vazio na partição de boot, com um README em seis idiomas apontando para o gerador (projects/ArchR/devices/RK3326/packages/u-boot/config/overlays/README.txt). O extlinux.conf carrega exatamente um arquivo de lá:
FDTOVERLAYS /overlays/mipi-panel.dtboO gerador existe em dois lugares, os dois rodando o mesmo archr_dtbo.py:
- a versão no navegador em /overlay-generator, que roda o script sobre um runtime Python em WebAssembly
- o
archr-dtbo-offline.zip, o mesmo script como ferramenta de terminal, com a bibliotecafdte os DTBs base junto
O aparelho também carrega uma cópia em /usr/libexec/generic-dsi/archr_dtbo.py, instalada pelo pacote generic-dsi.
A option string codifica o console e as flags da placa. Por exemplo: SDORIG para um R36S original, SDCLONE para um clone e SDORIG-NAm para uma placa Soysauce.
Armadilhas
Casos extremos que apareceram durante o desenvolvimento. Nenhum se aplica a uma gravação normal de release. Só importam se você está mexendo no build.
Contagens de cabeçalho de hunk em patches DTS importam. Uma contagem @@ -X,Y +A,B @@ errada faz o patch abortar silenciosamente o resto dos hunks, deixando "lixo" recuperado. Valide com patch --dry-run --verbose sobre uma fonte limpa após cada edição de DTS.
OPPs de turbo-mode se escondem atrás de cpufreq/boost. Setar scaling_max_freq=1512000 diretamente não faz nada se boost=0. A frequência não aparece em scaling_available_frequencies até você escrever 1 em cpufreq/boost. O set_cpu_max_freq highest do ArchR lê tanto scaling_available_frequencies quanto scaling_boost_frequencies.
O regulator-max do vdd_arm controla a disponibilidade de OPPs silenciosamente. Se uma OPP solicita uma tensão acima de regulator-max-microvolt, o kernel descarta a OPP sem nada no dmesg, e policy_has_boost_freq() retorna false → o sysfs cpufreq/boost nunca é criado.
Os avisos de FAT depois de um resize são esperados. O fs-resize roda fatlabel -i -r para dar um ID de volume novo à partição de boot, e o fatlabel reclama duas vezes no processo: a contagem de clusters está abaixo do mínimo do FAT32 e o setor de boot primário difere do backup. Os dois são de propósito. A partição de boot de 256 MB usa a mesma geometria FAT32 fora da spec que o resto do ecossistema RK3326, porque os U-Boots dos fabricantes só são testados contra essa geometria, e a diferença no setor de boot é a flag de dirty do vfat do kernel, escrita só no setor primário enquanto /flash está montado para escrita. O fs-resize escreve essa mesma nota em /flash/fs-resize.log.
Não "conserte" o filesystem de boot com fsck.fat -a -w. Reescrever a geometria para algo dentro da spec é justamente o que quebra o boot nos loaders dos fabricantes. Tanto o mkimage quanto o lado do dispositivo conferem o filesystem só em leitura, com fsck.fat -n.
O diretório raiz do shader cache do Mesa precisa existir. O Mesa não vai fazer mkdir em MESA_SHADER_CACHE_DIR por conta própria; apenas cai silenciosamente para modo sem cache. O runemu.sh cria /storage/.cache/mesa_shader_cache se estiver faltando.
panfrost_devfreq precisa de OPPs baixas de GPU para inicializar. Remover as OPPs de 200/300/400 MHz (para forçar um piso mais alto) bloqueia a inicialização da GPU no boot. O driver espera que a menor OPP publicada seja alcançável. Mantenha a escada; force um piso alto em runtime com set_gpu_min_freq highest.
PREEMPT vs PREEMPT_VOLUNTARY ainda não foi bencheado. A configuração do kernel define CONFIG_PREEMPT=y. Se o PREEMPT_VOLUNTARY se sairia melhor no A35 é uma questão em aberto, à espera de um A/B com o bench no dispositivo.
O unbalance do regulador no mali_kbase é um patch de kernel, não um quirk. Sem o 002-fix-unbalanced-regulator-clk-pm.patch, cada transição de PM emite unbalanced disables for vdd_logic e Enabling unprepared clk_gpu, audível como estalo no Mario 64.