Initramfs: uma longa história e um teclado bluetooth

Posted by Aline Freitas on 2 de August de 2012 | Subscribe
in Linux
as

 

Esta imagem representa o esquema de inicialização de um sistema Linux num típico computador PC. Retirei a imagem deste excelente artigo, disponível no site da IBM, M. Tim Jones, Inside the Linux boot process.

 

Em linhas gerais, quando pressionamos o botão “power” do nosso sistema, os dados armazenados no BIOS ocupam um endereço de memória. O BIOS precisa determinar quais os dispositivos capazes de inicializar e delegar as tarefas seguintes a um destes de acordo com suas configurações.  O dispositivo candidato a execução e habilitado para inicializar toma o controle do processo pelo seu boot loader, presente nos primeiros 512 bytes. Este é o famoso MBR (Master Boot Record). O seu trabalho é acionar o segundo boot loader, o programa capaz de executar algum sistema operacional. Ele pode executar apenas um sistema, como o winload.exe ou pode oferecer um menu de opções como os populares gerenciadores de inicialização Grub e Lilo.

 

A primeira peça do sistema operacional a ser acionada é o kernel. Tradicionalmente o gerenciador de inicialização o executa, que assumindo o controle, checa o hardware para então conectar os dispositivos físicos nas interfaces disponíveis pelo kernel (como discos, teclados, monitor), monta o sistema de arquivos root e procede com a incialização dos daemons responsáveis por tarefas como rede, sistema de impressão, comunicação interna (dbus), gerenciamento de permissões (polkit) e outros de acordo com a configuração e as necessidades do ambiente até o login prompt, que pode ser via console (modo texto) ou gráfico (GDM, KDM, XDM, SLIM).

 

Um kernel linux (tal qual os kerneis dos BSDs) pode ser compilado com seus drivers built-in (diretamente no kernel) ou como módulos independentes do kernel, armazenados no sistema de arquivos, tradicionamente em /lib/modules. Como o kernel vai ocupar um endereço de memória durante toda a execução do sistema operacional, quanto maior for este kernel, mais endereços de memória RAM ficarão ociosos, além do que, muitos drivers não precisam residir na memória RAM a todo o momento porque só são utilizados em alguns momentos. Podemos compilar todos como módulos? Sim. Mas ainda temos um problema. Se os módulos ficam armazenados em /lib/modules isso quer dizer que eles só estarão disponíveis após o sistema de arquivos principal (rootfs) estiver montado. Isso quer dizer então que drivers da controladora de discos e do sistema de arquivos não podem ser compilados como módulos porque devem estar disponíveis antes que o “rootfs” esteja montado? Não necessariamente. Existem algumas alternativas que possibilitam que mesmo este tipo de driver fundamental sejam compilados como módulo.

 

A primeira surgiu em meados do ano 2000, cujo documento existe até hoje na árvore do kernel Linux: initrd.

 

O initrd (ou Inicial RAM disk) é um pequeno sistema de arquivos armazenado num container e acionado pelo gerenciador de inicialização juntamente com o kernel. O kernel decomprime e disponibiliza o conteúdo do initrd num dispositivo de bloco (como /dev/ram0)  que por sua vez será montado numa estrutura de sistema de arquivos. O initrd pode armazenar módulos e executar programas que necessitem que estejam disponíveis antes da montagem do “rootfs”. Entretanto ele necessita que o módulo do sistema de arquivos do próprio initrd seja compilado built-in no kernel. O initrd causou uma verdadeira revolução nos sistemas linux. Permitiu que as distribuições Linux disponibilizassem um kernel padrão com módulos para todos os possíveis tipos de hardwares existente pré-compilados. Até o final da década de 90 incentivava-se que o usuário compilasse seu próprio kernel para ganhar performance, com o initrd tal recomendação deixou de fazer sentido. Mas uma revolução mais importante ainda estaria por vir na primeira década dos anos 2000.

 

Há alguns anos atrás, Linus Torvalds teve uma idéia: e se o Linux cache pudesse ser montado como um sistema de arquivos? Mantenha os arquivos em cache e nunca se livre deles até que sejam removidos ou que o sistema seja reiniciado? Linus desenhou um pequeno círculo ao redor do cache chamando de ‘ramfs’, e outros desenvolvedores do kernel criaram uma versão melhorada chamada ‘tmpfs’ (que pode escrever dados na swap, e limitar o tamanho de um determinado ponto de montagem tal que seja preenchido antes de consumir toda a memória disponível).”  Rob Landley, Introducing initramfs, a new model for initial RAM disks

 

O tmpfs é um dispositivo de bloco referenciado na memória RAM mas disponível no kernel a todo momento. Isso quer dizer que podemos montar um sistema de arquivos neste espaço sem precisar de um módulo de sistema de arquivos como ext2. A forma mais ordinária de uso de tmpfs é na montagem do diretório /tmp. Já a forma mais extraordinária de uso da tmpfs é na armazenagem temporária de um outro pequeno e poderoso sistema de arquivos chamado initramfs.

 

O initramfs guarda muitas semelhanças com o initrd. É um pequeno sistema de arquivos capaz de armazenar módulos e programas binários para ser executados antes da montagem do “rootfs”. Mas enquanto o initrd vai ser disponibilizado como um dispositivo de bloco o initramfs vai ser montado diretamente no tmpfs, sem a necessidade de um drive de sistema de arquivos. O kernel não sabe que se trata de um sistema temporário. Delega o PID 1 (init, o processo principal) ao initramfs e é o initramfs que vai (ou não) tratar de executar suas tarefas, subir os módulos, montar os sistemas de arquivos dos dispositivos de discos do hardware físico e delegar o restante das tarefas ao init do “rootfs”.

 

Um caso típico onde o initramfs é fundamental é na criptografia do sistema de arquivos. Há muitas formas de criptografar um disco rígido, uma das mais comuns e com maior nível de segurança propõe criptografar todas as partições exceto uma pequena partição /boot, que armazene pelo menos o kernel e o initramfs. Para descriptografar o disco eu preciso dos módulos dos algoritmos de criptografia, dos executáveis responsáveis pela descriptografia. Ainda na initramfs o sistema deve oferecer um prompt ao usuário para que o mesmo insira sua senha. Assim podemos precisar também de módulos de USB para entrada de dados (USB input). E se eu tiver um teclado bluetooth? Esta foi a pergunta que eu me fiz há poucos dias e me fez mergulhar num desafio.

 

A principal interface bluetooth para Linux é o pacote Bluez. O Bluez para entrar em ação depende do Dbus, responsável pela comunicação entre interface-sistema (userland/system) e entre aplicação-aplicação. Num processo de inicialização comum o sistema inicializa primeiro o dbus, que disponibiliza um TCP/socket (um dispositivo de comunicação entre aplicações), o Bluez inicializa acessando o socket do dbus e solicitando a partir deste comunicação com o sistema. Uma vez estabelecida a comunicação os eventuais periféricos que estejam previamente pareados estarão prontos para ativação (teclados ou mouses, por exemplo).

 

Os dispositivos bluetooth são identificados pelo Bluez a partir dos seus endereços MAC, os mesmos que usamos em interfaces de rede ethernet, por exemplo. Na primeira conexão ligamos o periférico desejado, no nosso caso um teclado, o mesmo emite um sinal de conexão e o Bluez recebe este sinal (por meio do daemon hidd). Para estabelecer uma conexão precisamos “parear” o dispositivo. O pareamento entre dispositivos móveis como celulares é bem intuitivo: um dispositivo envia um sinal ao outro, o outro pede que confirme um código PIN, e basta comparar o código entre ambos os aparelhos e digitar ou simplesmente aceitar de acordo. No caso de um teclado o Bluez disponibiliza um código PIN que deverá ser digitado no teclado que solicita a conexão. Uma vez estabelecida podemos acrescentar nosso teclado como um dispositivo “trusted”. O bluez gera e armazena um hash deste processo de comunicação, permitindo que nas próximas conexões não seja preciso fazer o pareamento novamente. E não será preciso a menos que o mesmo teclado seja pareado em outro computador ou que o resetemos, por exemplo ao trocar as pilhas. Neste caso será preciso fazer um novo pareamento e gerar um novo hash. O bluez armazena todas estas configurações num diretório, por padrão /var/lib/bluetooth. Como fazer isso funcionar num initramfs?

 

O Dbus, como herdeiro do DCOP, o antigo protocolo de comunicação do KDE, é um backend cuja existência se deu incialmente em ambientes desktop completos como Gnome e KDE. Ele surgiu para facilitar o desenvolvimento de aplicações desktop ao fornecer um framework pronto de comunicação com o sistema (kernel-land). Por isso desconfiei da possibilidade de executar um daemon de sistema do dbus no initramfs (chamada pelo Arch  Linux de initcpio). É quase executar o sistema operacional inteiro no próprio initramfs. Nem tanto…

 

A implementação no Arch Linux é bem simples.

  1. Faço o carregamento dos módulos
  2. Copio os binários dbus-daemon, bluetoothd e hidd. O framework do initcpio do Arch se encarrega de copiar as respectivas bibliotecas do qual tais daemons dependem.
  3. Copio as bibliotecas libnss_files e libnsl. O dbus precisa de um ambiente com usuário e grupo configurados. O initramfs é um sistema mínimo, por padrão não fornece essa infra estrutura. Assim precisamos disponibilizar passwd e group com pelo menos o usuário root. Mas o dbus não busca as informações de usuário diretamente via passwd e group, mas via glib que por sua vez precisa destas duas bibliotecas para finalmente buscar as informações de usuário e grupo pelos dois arquivos UNIX padrão.
  4. Acrescento arquivos de configuração para o dbus específicos para este sistema, delegando permissões ao bluetooth para o usuário root (sim, isso parece um absurdo, mas o root nem sempre é deus). Por fim acrescento os arquivos de configuração do Bluez e o diretório completo que contem os dispositivos pareados e “trusteds” (confiáveis).

Uma vez pronto nosso initramfs, o script hook vai executar 3 daemons:

run_hook() {
    msg "Activating dbus..."
    mkdir -m755 /run/dbus
    dbus-daemon --system
    sleep 1

    msg "Activating bluetoothd..."
    mkdir -m755 /run/sdp
    bluetoothd
    sleep 1

    msg "Activating hidd..."
    . /etc/conf.d/bluetooth
    hidd ${HIDD_OPTIONS}
}

O inconveniente é que o reconhecimento e ativação do teclado demora alguns segundos.

 

No próximo eu gostaria de escrever sobre o “init” e o polêmico “systemd”.

 

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>