2

Meu Game Stick GD10 veio com o cartão corrompido, e reinstalar o sistema me ensinou mais sobre boot Amlogic do que eu gostaria

Comprei na Shopee um Game Stick GD10 daqueles genéricos, com dois controles sem fio e a promessa de "40 mil jogos", para dar de presente. O anúncio dizia Amlogic S905X com 2 GB de RAM.

Funcionou exatamente um dia. No dia seguinte a TV mostrou uma tela de erro de sistema de arquivos e o aparelho não subiu mais. Presente estreado e quebrado antes de completar 48 horas.

Game Stick GD10

A conclusão óbvia era que o cartão SD que veio de fábrica tinha morrido. Comprei um Kodak de 32 GB e achei que seria coisa de vinte minutos: baixar uma imagem do EmuELEC, gravar, plugar e jogar. Foram várias horas, três problemas que eu não fazia ideia que existiam, e uma boa dose de erros meus. Este artigo é o registro disso, com todos os links e comandos, principalmente para quando eu (ou você) comprar outro desses.

Primeiro, um banho de realidade no hardware

Antes de baixar qualquer coisa, resolvi espiar o cartão original no notebook. Ele ainda montava parcialmente, e a partição de boot tinha os arquivos de sempre de um sistema Amlogic:

aml_autoscript
dtb.img
kernel.img
SYSTEM

O dtb.img é o device tree, o arquivo que descreve o hardware para o kernel. Rodei strings nele e a primeira linha entregou tudo:

gxl_p281_1g

p281 é a placa de referência do S905W. E 1g é a quantidade de RAM: 1 GB. Ou seja, o anúncio mentiu duas vezes. Não é S905X e não são 2 GB.

Isso não é detalhe cosmético. Se eu tivesse confiado no anúncio e usado o device tree de um S905X com 2 GB, o aparelho simplesmente não daria vídeo, e eu passaria horas procurando o problema no lugar errado. A lição aqui é direta: não confie na descrição do anúncio, leia o dtb.img do cartão que veio no aparelho.

O cartão de fábrica não estava só corrompido, era falsificado

Olhando o tamanho do cartão original, apareceu algo estranho:

sda  48,83 GiB  (52,4 GB)

Não existe cartão SD de 52,4 GB. Um 64 GB real reporta cerca de 59,5 GiB. Aquele número quebrado é a assinatura clássica de um cartão adulterado, daqueles que têm o controlador modificado para declarar mais capacidade do que a memória física realmente tem. Tudo que é gravado além do limite real vira lixo silenciosamente.

Isso explicava a corrupção, e explicava por que a partição de jogos dizia ter 46 GB ocupados. Boa parte daquilo nunca existiu de fato. Não consigo provar qual gravação específica derrubou o sistema, mas num cartão assim é questão de tempo: qualquer escrita que caia numa região inexistente se perde em silêncio, e um dia ela cai numa estrutura de que o sistema depende.

O Kodak de 32 GB que comprei reportou 29,12 GiB (31,3 GB), que é exatamente o esperado de um cartão honesto. Se quiser ter certeza antes de confiar num cartão, a ferramenta certa é o f3 (Fight Flash Fraud), que grava e relê o cartão inteiro para detectar capacidade adulterada. A documentação dele explica os modos de uso; no Fedora está no repositório oficial como f3.

Escolhendo a versão: a mais nova não serve

Aqui está a primeira armadilha real. O EmuELEC tem duas linhas de build para Amlogic:

  • Amlogic-ng, com kernel 4.9, para S905X2, X3, X4 e S922X
  • Amlogic (legacy), com kernel 3.14, para S905, S905X, S905W e S912

O S905W do Game Stick está na linha legacy. E consultando os releases do projeto, a 4.3 é a última versão que ainda publicou build legacy. Da 4.4 em diante só existe Amlogic-ng, que não roda nesse chip de jeito nenhum.

Ou seja, baixar a versão mais recente é garantia de tela preta. É preciso descer até a release v4.3 e pegar o EmuELEC-Amlogic.aarch64-4.3-Generic.img.gz. Repare no nome: se tiver -ng, é a imagem errada.

curl -LO https://github.com/EmuELEC/EmuELEC/releases/download/v4.3/EmuELEC-Amlogic.aarch64-4.3-Generic.img.gz
curl -LO https://github.com/EmuELEC/EmuELEC/releases/download/v4.3/EmuELEC-Amlogic.aarch64-4.3-Generic.img.gz.sha256

sha256sum -c EmuELEC-Amlogic.aarch64-4.3-Generic.img.gz.sha256
gunzip EmuELEC-Amlogic.aarch64-4.3-Generic.img.gz

Gravar é o dd de sempre, conferindo antes com lsblk qual é o dispositivo do cartão:

sudo dd if=EmuELEC-Amlogic.aarch64-4.3-Generic.img of=/dev/sdX bs=4M conv=fsync status=progress
sync

Dentro da imagem existe uma pasta device_trees com dezenas de arquivos. Peguei o gxl_p281_1g.dtb e comparei com o dtb.img do cartão de fábrica:

29787d812a3d7276456f34531ef083be  gxl_p281_1g.dtb
29787d812a3d7276456f34531ef083be  dtb-original.img

MD5 idêntico. Isso me deu certeza absoluta de que era o device tree certo, sem tentativa e erro.

Com a imagem já gravada, a troca é montar a primeira partição do cartão e sobrescrever o dtb.img. Note que a imagem traz um dtb.img genérico de 344 KB, que é um contêiner com vários device trees; ele é substituído por um único arquivo de cerca de 41 KB:

sudo mount /dev/sdX1 /mnt
sudo cp /caminho/para/gxl_p281_1g.dtb /mnt/dtb.img
sync && sudo umount /mnt

Aproveitei para expandir a segunda partição, que vem com apenas 32 MB e ocuparia o cartão inteiro sozinha no primeiro boot. Fazer isso manualmente dá mais controle:

sudo parted -s /dev/sdX resizepart 2 100%
sudo e2fsck -f -y /dev/sdX2
sudo resize2fs /dev/sdX2

Se você expandir por conta própria, remova depois o arquivo /storage/.please_resize_me da segunda partição, senão o sistema tenta redimensionar de novo no primeiro boot. Volto a esse detalhe adiante.

Tela preta: o problema que quase me fez desistir

Gravei a imagem, troquei o device tree, expandi a partição de dados, coloquei no aparelho. Tela preta. Nem logo, nem splash, absolutamente nada.

O cartão antigo, mesmo quebrado, ainda mostrava a tela de erro. Então o hardware estava vivo e o problema era o cartão novo. Comecei a eliminar suspeitos.

Os arquivos de boot estavam íntegros, o MD5 do kernel.img e do SYSTEM batiam com os arquivos .md5 da própria imagem, e o device tree estava correto. Sobrou o aml_autoscript, o script que o u-boot executa para dar boot pelo cartão. Comparei os dois e o MD5 era diferente. Achei que tinha encontrado a causa.

Mas era pista falsa. O aml_autoscript é um arquivo com 64 bytes de cabeçalho u-boot seguido do payload. Descontando o cabeçalho, os 731 bytes de conteúdo eram byte a byte idênticos. O que diferia era só o timestamp de compilação e, por consequência, o CRC. Descartar essa pista foi o que me levou ao lugar certo.

O lugar certo é a área reservada antes da primeira partição. Chips Amlogic têm uma BootROM que procura o bootloader no setor 1 do cartão. Medi quantos bytes não-zero existiam ali nos dois cartões:

cartão de fábrica : 733.938 bytes não-zero (de 1.048.064)
cartão novo       :       0 bytes não-zero

Zero. A imagem oficial do EmuELEC não traz bootloader na área reservada. Ela pressupõe que o aparelho tenha um u-boot interno capaz de encadear o aml_autoscript, o que vale para a maioria das TV Boxes, mas não vale para esses Game Sticks. A BootROM procurava o bootloader, encontrava zeros e desistia antes de qualquer coisa chegar à tela. Daí a tela preta absoluta.

A correção é copiar essa área de um cartão que funcione. No meu caso, a primeira partição do cartão de fábrica começa no setor 2048 e a do cartão novo no setor 8192, então copiar os setores 1 a 2047 é seguro, sem risco de encostar nos dados:

# sdX = cartão de fábrica (origem), sdY = cartão novo (destino)
# preserva o setor 0 (tabela de partições) e para bem antes da primeira partição
sudo dd if=/dev/sdX of=/dev/sdY bs=512 skip=1 seek=1 count=2047 conv=fsync

Confira os setores iniciais das suas partições antes de rodar isso. Dá para ler sem root:

cat /sys/block/sdX/sdX1/start

Depois dessa cópia, deu imagem na TV. Esse é, na minha opinião, o ponto mais importante deste artigo: guarde o cartão original mesmo depois de aposentado, porque aqueles 717 KB podem ser insubstituíveis.

"We can't find any systems": esse aqui foi culpa minha

O EmulationStation subiu, mas com uma mensagem dizendo que não encontrou nenhum sistema. Os jogos estavam todos no cartão, nos diretórios certos.

Tela do EmulationStation com a mensagem WE CAN'T FIND ANY SYSTEMS

O problema é que eu tinha tentado ser esperto. Antes do primeiro boot, criei pastas dentro de /storage para já deixar idioma e fuso configurados. Parecia inofensivo. Não é. Olhe o que o emuelec_autostart.sh faz:

CONFIG_DIR="/storage/.emulationstation"
CONFIG_DIR2="/storage/.config/emulationstation"

if [ ! -L "$CONFIG_DIR" ]; then
ln -sf $CONFIG_DIR2 $CONFIG_DIR
fi

O teste é "se não for um symlink". Como eu tinha criado um diretório de verdade ali, a condição deu verdadeira e o ln -sf rodou. Só que ln -sf com um diretório existente como destino não substitui a pasta: ele cria o link dentro dela. O resultado foi um .emulationstation/emulationstation inútil, e o EmulationStation acabou lendo o es_systems.cfg de exemplo que sobrou na pasta.

Dá para ver o estrago pelo tamanho do arquivo:

.emulationstation/es_systems.cfg          1.791 bytes  (exemplo genérico, 1 sistema)
.config/emulationstation/es_systems.cfg  75.131 bytes  (o real, 101 sistemas)

O arquivo de exemplo declara só um sistema nes aceitando extensões .nes e .NES. Como as ROMs são .zip, nenhum jogo casou, o sistema foi escondido e veio a mensagem de erro.

A correção foi trocar o diretório pelo symlink que deveria estar lá:

rm -rf /storage/.emulationstation
ln -sfn /storage/.config/emulationstation /storage/.emulationstation

Se estiver fazendo isso com o cartão montado no PC, atenção: o alvo do link tem que ser o caminho como o aparelho enxerga (/storage/...), não o ponto de montagem do seu computador. No PC ele vai parecer quebrado, e está certo assim.

A lição vale para qualquer coisa: grave a imagem limpa, deixe o aparelho bootar uma vez e só então ajuste as configurações. O mesmo erro também tinha disparado um reboot com mensagem de erro na tela, porque o fs-resize usa a existência dessas pastas como sinal de que o sistema já foi inicializado.

As configurações que se apagam sozinhas

Configurei o idioma para português e o fuso para São Paulo editando o es_settings.cfg e o .cache/timezone. No boot seguinte, voltou tudo para inglês e México.

O motivo está no emustation-config:

TZ=$(get_ee_setting system.timezone)
echo "TIMEZONE=$TZ" > /storage/.cache/timezone

O .cache/timezone é um arquivo derivado, regerado a cada boot. Editar ele não adianta. O mesmo vale para o idioma no es_settings.cfg. As chaves de verdade ficam no emuelec.conf, e vêm assim de fábrica:

system.language=en_US
system.timezone=America/Mexico_City

Para português do Brasil e horário de São Paulo, o que persiste é trocar por:

system.language=pt_BR
system.timezone=America/Sao_Paulo

Esse arquivo fica em /storage/.config/emuelec/configs/emuelec.conf, e o modelo original está no repositório com todas as chaves comentadas — vale a leitura, tem bastante coisa útil ali além dessas três.

Trocar ali persiste. E sim, o padrão do EmuELEC é México, então isso não vem do fabricante do stick. A regra geral é: configuração de sistema mora no emuelec.conf; o resto é cache.

Bônus: copiar arquivos de um cartão com FAT corrompido

Para migrar os jogos do cartão antigo, esbarrei em outro detalhe. Rodei um du para medir as pastas e o processo travou por vários minutos. Investigando:

setores lidos do cartão em 6s : 0
erros de FAT nos últimos 2min : 27.574
estado do processo            : R (queimando CPU)

Zero leitura, 27 mil erros de sistema de arquivos, e o processo consumindo CPU em vez de esperar I/O. O du tinha entrado em loop infinito percorrendo uma entrada de diretório corrompida. Não ia terminar nunca.

A mesma corrupção também inflava tamanhos. Uma pasta de Famicom Disk System, cujos jogos têm cerca de 128 KB cada, aparecia com 36 GB.

A solução foi envolver toda operação em timeout, tanto a medição quanto a cópia, e aceitar resultado parcial:

TAM=$(timeout 120 du -sm "$PASTA" | cut -f1)
timeout 3600 rsync -a --ignore-errors "$ORIGEM" "$DESTINO"

Assim uma pasta doente é abandonada e registrada, em vez de derrubar o processo inteiro. Vale também checar o espaço livre antes de cada pasta, senão um diretório com tamanho fantasma tenta copiar dezenas de gigabytes inexistentes.

Um último detalhe que quase passou batido: os arquivos de BIOS estavam com nomes em caixa mista, tipo scph5501.BIN. No cartão antigo, formatado em FAT, isso não importa porque o sistema de arquivos ignora maiúsculas. A partição nova é ext4, que diferencia. Os emuladores de PlayStation procuram o nome em minúsculo e não encontrariam o arquivo, sem nenhuma mensagem de erro clara. Basta criar as cópias em minúsculo.

Conclusão

No fim, o aparelho está rodando EmuELEC 4.3, com NES, SNES, Mega Drive, PlayStation, Neo Geo, arcade e com uma folga confortável de espaço. O N64 eu acabei removendo porque 1 GB de RAM não dá conta de forma decente.

EmuELEC 4.3 rodando no Game Stick, com a lista de sistemas no EmulationStation

Se você for fazer o mesmo caminho, o resumo é este:

  1. Leia o dtb.img do cartão de fábrica para descobrir o hardware real, ignorando o anúncio
  2. Confira se o cartão original não é de capacidade falsificada, olhando se o tamanho é um número plausível
  3. Use EmuELEC 4.3, build Amlogic sem o sufixo -ng
  4. Substitua o dtb.img pelo device tree correto e confirme pelo MD5
  5. Copie os setores 1 a 2047 do cartão original para o novo, senão não dá boot
  6. Deixe bootar uma vez antes de configurar qualquer coisa
  7. Configure idioma e fuso no emuelec.conf, não nos arquivos derivados

O que mais me surpreendeu foi que o problema decisivo, aquele que impedia qualquer imagem na tela, não estava em nenhum arquivo visível: estava em 717 KB de bootloader escondidos antes da primeira partição, que nenhuma imagem oficial distribui. É o tipo de coisa que só aparece comparando um cartão que funciona com um que não funciona.

Alguém mais aqui já reaproveitou um desses Game Sticks? Fiquei curioso para saber se os modelos com RK3566 sofrem do mesmo problema de bootloader, porque a arquitetura de boot é bem diferente da Amlogic.

EmuELEC

Código-fonte citado no artigo

Ferramentas

  • f3 — detecta cartões de capacidade falsificada (documentação)
  • dtc (device-tree-compiler) para inspecionar o dtb.img, embora um strings já resolva
  • dd, parted, resize2fs e rsync, todos presentes em qualquer distro

Os comandos deste artigo foram executados no Fedora, mas nada aqui é específico de distribuição.

Carregando publicação patrocinada...