Introdução
Depois de configurar o ambiente de testes com QEMU/libvirt e compilar meu primeiro kernel para ARM, os próximos dois tutoriais do FLUSP — "Introduction to Linux kernel build configuration and modules" e "Introduction to Linux kernel Character Device Drivers" — foram realizados. O primeiro ensina os fundamentos dos módulos do kernel, e o segundo aplica esses conceitos para criar um driver de dispositivo de caractere funcional.
Parte 1: Módulos do Kernel
O que são Módulos?
O tutorial começa com uma explicação simplesl: módulos do kernel são pedaços de código que podem ser carregados e removidos do kernel em tempo de execução, sem a necessidade de reiniciar o sistema. Isso contrasta com os kernels monolíticos, onde toda nova funcionalidade precisa ser compilada diretamente no kernel, exigindo uma reinicialização.
Meu Primeiro Módulo
Seguindo o tutorial, criei meu primeiro módulo: o simple_mod. O código é mínimo. Ele define uma função de inicialização (simple_mod_init) que imprime "Hello world" no log do kernel, e uma função de saída (simple_mod_exit) que imprime "Goodbye world". As macros module_init e module_exit registram essas funções para serem chamadas quando o módulo for carregado e removido, respectivamente
Para que o sistema de build do kernel soubesse da existência desse módulo, precisei criar um símbolo de configuração no arquivo Kconfig e adicionar a opção de compilação no Makefile.
Configurando o Build
O próximo passo foi usar o make menuconfig para ativar a compilação do meu módulo. Dentro do menu "Device Drivers" -> "Misc devices", habilitei a opção "Simple example module" que eu havia criado.
Depois, compilei o kernel e os módulos com make -j$(nproc) Image.gz modules. Para instalar os módulos na VM ARM64, usei make modules_install. Tudo isso executado remotamente na keter via SSH.
Carregando e Testando o Módulo
Com a VM rodando, copiei o arquivo simple_mod.ko para dentro dela e usei o comando insmod para carregá-lo. Para ver a mensagem "Hello world", usei dmesg | tail. Depois, removi o módulo com rmmod e conferi a mensagem "Goodbye world"
Parte 2: Character Device Drivers
O segundo tutorial é uma continuação do primeiro. Ele pega os conceitos de módulos e os aplica para construir um driver de dispositivo de caractere funcional que são aqueles que trocam dados sequencialmente com o sistema, como portas seriais, teclados e mouses.
Major, Minor e file_operations
- Cada dispositivo de caractere é identificado por um par de números. O major identifica o driver associado, e o minor é usado pelo driver para diferenciar entre múltiplos dispositivos do mesmo tipo.
- file_operations é uma estrutura que contém ponteiros para as funções que implementam as operações que podem ser realizadas no dispositivo, como open, read, write e release.
- O kernel oferece funções como alloc_chrdev_region() para alocar dinamicamente um número major e um intervalo de minors para o seu driver.
Exemplo simple_char
Seguindo o tutorial, criei um novo módulo chamado simple_char. Ele registra um driver de caractere com um número major alocado dinamicamente e implementa as operações básicas de open, release, read e write.
A função simple_char_read permite que um processo no espaço de usuário leia dados do dispositivo, e a simple_char_write permite que ele escreva. O dispositivo armazena os dados em um buffer interno.