Introdução à Portabilidade do Bootloader
A configuração do ambiente de inicialização é fundamental para o funcionamento do sistema embarcado. Após estabelecer a rotina de arranque via bootloader, o próximo passo envolve ajustar este software para suportar uma versão específica do núcleo, neste caso o Linux 5.2.8. A portabilidade de kernel consiste na modificação dos trechos de código que são específicos da arquitetura de hardware, visando compatibilizar o sistema operativo com as características físicas da placa.
No ecossistema Linux, os códigos sensíveis ao hardware residem principalmente nas pastas arch (onde estão definidos os arquiteturas e configurações de placas específicas) e drivers (contendo os controladores de dispositivos). Dependendo da posição na cadeia de produção, as responsabilidades variam:
- Fabricantes de Núcleos (IP Core): Responsáveis por fornecer as instruções base e os blocos lógicos do processador, desenvolvendo as rotinas assembly iniciais de inicialização.
- Fabricantes de SOC: Ao integrar núcleos de CPU com periféricos, estes fabricantes criam placas de desenvolvimento genéricas. O trabalho deles envolve fornecer drivers de periféricos integrados (on-board) e códigos de suporte de placa (BSP).
- Fabricantes de Periféricos: Fornecem os drivers necessários para sensores ou controladores externos (como conversores ADC), facilitando a integração pelo cliente final.
- Fabricantes de Produtos Finais: Geralmente utilizam o código de suporte fornecido pelos fabricantes de SOC, adaptando apenas a definição de dispositivo (device tree) para seus circuitos impressos personalizados.
Configuração Avançada do U-Boot
Para garantir a compatibilidade com a nova versão do kernel, copia-se o diretório do bootlaoder antigo para um novo ambiente de trabalho. Alterações estruturais devem ser aplicadas nos arquivos de cabeçalho de configuração e lógica de controle.
Definição de Parâmetros de Inicialização
O arquivo smdk2440.h, locailzado em include/configs/, deve conter os argumentos de inicialização (bootargs). Estes parâmetros informam ao kernel onde encontrar o sistema de arquivos raiz e qual processo iniciar:
#define CONFIG_BOOTARGS "root=/dev/mtdblock3 console=ttySAC0,115200 init=/linuxrc"
Nesta definição:
- root: Define o bloco de memória correspondente ao sistema de arquivos raiz (NAND partição 3).
- init: Especifica o binário de iniciação.
- console: Redireciona a saída padrão para a porta serial 0 (UART).
Habilitação do Suporte a Sistemas YAFFS2
Versões antigas do U-Boot frequentemente incluíam suporte nativo a sistemas de arquivos YAFFS, mas versões mais recentes podem necessitar de implementação manual no comando de gravação da NAND. Dentro da rotina do_nand no arquivo cmd/nand.c, adiciona-se tratamento específico para sufixos ".yaffs2":
#ifdef CONFIG_CMD_NAND_YAFFS
} else if (!strcmp(s, ".yaffs2")) {
if (read) {
printf("Comando 'nand' desconhecido: %s\n", s);
return 1;
}
// Lógica de escrita preservando a área OOB (Out-of-Band)
ret = nand_write_skip_bad(nand, off, &rwsize, NULL, maxsize,
(u_char *)addr, WITH_YAFFS_OOB);
#endif
Além disso, registros auxiliares na array nand_help_text devem ser atualizados para documentar o novo comando nand write.yaffs2. No driver principal de utilitários NAND (drivers/mtd/nand/nand_util.c), a função nand_write_skip_bad precisa identificar se o flag WITH_YAFFS_OOB está ativo para calcular corretamente o tamanho do bloco baseado na página e dados fora da página (OOB):
if (flags & WITH_YAFFS_OOB) {
if (flags & ~WITH_YAFFS_OOB) return -EINVAL;
int pages = nand->erasesize / nand->writesize;
blocksize = (pages * nand->oobsize) + nand->erasesize;
// Validação de alinhamento de página
if (*length % (nand->writesize + nand->oobsize)) {
printf("Erro: Tentativa de escrever página incompleta em modo YAFFS\n");
return -EINVAL;
}
}
// ... restante do fluxo de escrita ...
Por fim, o macro WITH_YAFFS_OOB deve ser declarado em include/nand.h e a opção CONFIG_CMD_NAND_YAFFS ativada no arquivo de configuração da placa.
Definição da Máquina (Machine ID)
Para que o kernel identifique corretamente a placa durante a carga de imagens, o U-Boot deve passar o número de identificação correto através do registro r1. No código de inicialização da placa (board_init em board/samsung/smdk2440/smdk2440.c), alteramos o valor para o identificador correto da SMDK2440, garantindo consistência entre o bootloader e o kernel.
Solução de Problemas na Leitura NAND
Erros como "Failed -74" durante a leitura indicam falhas de校验 (verificação de integridade) ECD (Error Correction Code). Este erro decorre frequentemente de uma incompatibilidade entre a configuração de ECC do hardware e o driver utilizado pelo bootloader.
Análise do Erro ECC
O retorno negativo -74 corresponde à constante -EBADMSG, indicando que a correção de erros falhou repetidamente após tentativas de recuperação (retry). A causa raiz está na função nand_do_read_ops dentro de drivers/mtd/nand/nand_base.c, onde a lógica verifica falhas de ECC acumuladas. Se o contador exceder o limite permitido, a flag ecc_fail é setada true.
Desabilitação de ECC para Desenvolvimento
Em casos onde a qualidade da mídia não permite correção robusta, ou durante fases de teste, pode-se desabilitar a verificação de ECC no driver da placa. Isso envolve modificar a estrutura de inicialização da NAND (board_nand_init) para usar o modo NAND_ECC_NONE:
// Comentar linha habilitadora de ECC Soft
// nand->ecc.mode = NAND_ECC_SOFT;
// A configuração herdará o valor padrão NONE
Com o modo definido como NONE, a função nand_read_page_raw será chamada, ignorando cálculos de checksum e permitindo leitura direta dos dados brutos, eliminando o erro de boot pendular (stuck) causados por falsos positivos de bad block.
Preparação e Compilação do Kernel
O processo de obtenção dos fontes do núcleo Linux envolve download de repositórios confiáveis. É crucial verificar versões antigas (como 5.2.x) pois versões muito recentes podem ter removido suporte a plataformas ARM legadas.
Estrutura de Projetos
Diretórios principais incluem:
arch/arm: Código específico da arquitetura ARM.drivers/mtd: Controladores de armazenamento (MTD).init: Roteiros de inicialização do sistema (start_kernel).
Configuração do Makefile
No nível raiz, o arquivo Makefile define o alvo da arquitetura compilada. Deve-se configurar explicitamente o compilador de cruzamento:
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-gnueabi-
Geração de Configuração (Defconfig)
A ferramenta make menuconfig facilita a seleção de recursos. Para esta plataforma, utiliza-se uma configuração base próxima, como a s3c2410_defconfig, já que a arquitetura é semelhante. Opções críticas incluem habilitar suporte ao filesystem de memória e definir a máquina correta.
Patch no Código do Kernel
São necessárias edições manuais para sincronizar perfeitamente com o hardware específico da placa Mini2440:
- Cronometrista (Clock): Definir a frequência correta do cristal (ex: 12MHz) no arquivo
mach-smdk2440.c. - Partições NAND: Em
common-smdk.c, redifinir a tabela de partições (smdk_default_nand_part) para refletir o uso real da memória Flash (U-Boot, Parametros, Kernel, RootFS). - Mach-ID: O identificador da máquina no arquivo
mach-typesdeve corresponder ao valor informado pelo U-Boot. Verificar o valor hexadecimal no terminal U-Boot usandobdinfo.
Instalação do Ambiente de Compilação
Compiladores modernos do GNU Toolchain podem apresentar incompatibilidades com kernels antigos se não configurados corretamente. A escolha da versão da ferramenta (arm-none-linux-gnueabi-gcc) depende da arquitetura alvo (EABI vs ABI clássico).
O caminho ideal é instalar ferramentas de compilação como u-boot-tools para gerar a imagem do kernel (mkimage) e configurar linkes simbólicos para manter a compatibilidade com nomes antigos de comandos de compilação (ex: gcc, ld).
Testes de Boot e Transferência
Após a compilação gerada a imagem uImage, o processo de carregamento pode ocorrer via rede (TFTP) ou programação direta da Flash.
O U-Boot deve receber os argumentos de inicialização e apontar para o endereço de memória correto onde a imagem reside. Exemplo de sequência de comandos via TFTP:
tftp 30000000 uImage
nand erase.part kernel
nand write 30000000 kernel
bootm 30000000
Análise de Logs de Boot
Em caso de falha ao iniciar o kernel, observam-se mensagens claras sobre IDs de máquina divergentes ou erros de E/S no disco. Um erro comum é "unrecognized machine ID", o que indica que o U-Boot passou um valor numérico diferente daquele esperado pelo Kernel.
Sistemas de arquivos montados como "yaffs" ou "jffs2" confirmam sucesso parcial na inicialização. O término ideal seria a mensagem "Starting kernel..." seguida pelas linhas de inicialização de drivers. Erros no sistema de arquivos raiz (VFS) geralmente indicam que o particionamento ou a própria imagem do rootfs não corresponde aos argumentos bootargs passados.
Limpando Bad Blocks Virtuais
Fenômenos onde muitos blocos aparecem como defeituosos logo após a limpeza podem indicar marcas de firmware residual. O comando nand scrub.chip realiza uma limpeza profunda, removendo essas marcações falsas.
Otimização do Kernel
Imagens de kernel podem ser excessivamente grandes dependendo das configurações padrão. Utilizando o make menuconfig novamente, é possível remover drivers de periféricos não utilizados e sistemas de arquivos obsoletos no projeto específico (ex: remover suporte a EXT3 ou ISO9660 se não houver CD-ROM no hardware). Essa redução diminui o consumo de RAM e acelera o tempo de boot.