Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

04 - Parâmetros de Boot do Kernel (console, root, quiet, splash)

video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

A linha de comando do kernel (Kernel Command Line ou cmdline) é o mecanismo arquitetural definitivo de injeção de estado estático no núcleo do Linux. Como o kernel (operando em Ring 0) precisa inicializar o hardware, mapear a memória e orquestrar a transição para o espaço do usuário antes de ter acesso a qualquer sistema de arquivos ou arquivo de configuração (/etc), ele depende inteiramente de uma string ASCII passada a ele pelo bootloader durante o milissegundo do hand-off de execução.

Esta string não é meramente um conjunto de variáveis globais; ela é a fundação que dita a topologia de inicialização, mitigação de falhas de CPU, comportamento do sistema de log interrupções de hardware (APIC) e os limites do gerenciador de memória (MMU).


1. A Estrutura de Passagem de Parâmetros e o Zero Page

O bootloader (como o GRUB2, systemd-boot ou EFISTUB) não escreve os parâmetros de boot em um arquivo. Na arquitetura x86/x86_64, ele obedece ao "Linux Boot Protocol", que define uma região de memória específica conhecida como Zero Page (historicamente na estrutura boot_params).

O bootloader carrega a string ASCII bruta (limitada a 2048 ou 4096 bytes, dependendo da compilação, definida por COMMAND_LINE_SIZE) em um endereço de memória física alocado e passa o ponteiro para esse endereço no cabeçalho do bzImage (especificamente no campo cmd_line_ptr no offset 0x228).
Quando a função C primária do kernel, start_kernel() (em init/main.c), inicia sua execução, ela copia essa string para estruturas internas do kernel. Essa string imutável é posteriormente exposta ao espaço do usuário através do pseudo-arquivo /proc/cmdline.


2. A Arquitetura de Parsing de Dupla Passagem

A inicialização do kernel apresenta um paradoxo: para alocar memória, o kernel precisa saber se o usuário passou limites de memória (mem=512M), mas para fazer o parse de strings complexas, ele precisa alocar memória. Para resolver isso, o parsing da linha de comando é feito em duas passagens estritas.

Passagem 1: Parâmetros Iniciais (early_param)

Antes do gerenciador de memória Buddy Allocator ou mesmo do console estarem ativos, a função parse_early_param() é chamada.
No código-fonte, certos parâmetros vitais são registrados usando a macro C early_param("nome", funcao_handler). Esses parâmetros configuram a fundação do hardware. Exemplos incluem:

  • mem=: Restringe artificialmente a quantidade de RAM vista pelo kernel.
  • numa=: Controla a topologia de memória não-uniforme.
  • earlycon: Inicializa uma UART primitiva (console serial) diretamente via portas de I/O, antes do subsistema TTY completo ser carregado, vital para depurar falhas que ocorrem nos primeiros milissegundos de vida do kernel.

Passagem 2: Parâmetros Padrões (__setup e core_param)

Após a memória e a arquitetura básica (CPU, interrupções) estarem inicializadas, o kernel invoca parse_args() para iterar sobre o restante da string.
Subsistemas registram handlers usando a macro __setup("parametro=", funcao). Se o kernel encontrar uma string correspondente, ele chama o ponteiro de função associado, passando o resto da string como argumento. Se uma opção da linha de comando contiver um ponto (module.param), ela é delegada para ser passada ao módulo específico quando (e se) ele for carregado dinamicamente mais tarde.


3. Ancoragem do VFS: Parâmetros de Raiz (root, ro, rw)

O propósito fundamental do kernel é orquestrar recursos, mas ele não tem utilidade sem montar o sistema de arquivos raiz (/). A injeção dessa localização ocorre através da diretiva root=.

  • root=: Especifica o nó do dispositivo primário. Devido à não-determinação da enumeração assíncrona de barramentos (um disco SCSI pode ser sda ou sdb dependendo de qual controladora responder primeiro na inicialização), usar root=/dev/sda1 é considerado uma falha de engenharia arquitetural moderna. O kernel, através da função name_to_dev_t(), e auxiliado pelo initramfs, aceita UUIDs e PARTUUIDs (root=UUID=1234-abcd).
  • ro (Read-Only) e rw (Read-Write): O kernel inicializa o VFS (Virtual File System) montando a raiz especificada em modo restrito. Passar ro é o padrão da indústria. Ele obriga o kernel a montar a raiz física inicial sem permissões de escrita, garantindo que as ferramentas de espaço de usuário do initramfs (como o fsck) possam escanear os superblocos em busca de inconsistências sem risco de concorrência ou corrupção de journal. O sistema de init real (systemd) se encarrega de remontar o VFS como rw posteriormente no ciclo de vida de boot.
  • **rootfstype= e rootflags=**: Informam preventivamente ao handler de montagem qual driver usar (ex: ext4, xfs) e diretivas específicas do módulo (ex: rootflags=subvol=@ para BTRFS), economizando ciclos de CPU que seriam gastos em probing heurístico (adivinhação do sistema de arquivos através de assinaturas de superbloco).

4. O Subsistema de Telemetria e Multiplexação TTY (console=)

O parâmetro console= instrui o subsistema de log do kernel (gerenciado primariamente pela infraestrutura do printk) sobre para onde deve enviar as saídas (outputs) vitais e, bidirecionalmente, de onde aceitar a entrada (stdin em Ring 0). Este é um parâmetro especial porque pode ser declarado múltiplas vezes, multiplexando a saída de emergência.

  • console=tty0: Direciona o console para o Terminal Virtual (VT) atual do monitor de vídeo local via framebuffer ou driver de vídeo legível.
  • console=ttyS0,115200n8: Direciona os logs para a primeira porta serial UART a uma taxa de 115200 bauds, com 8 bits de dados, sem paridade.

Se um administrador declarar console=tty1 console=ttyS0,115200, o kernel fará o broadcast de seus logs internos (panic, oops, mensagens de boot) tanto para o monitor ligado ao servidor quanto para a conexão de cabo serial no datacenter. Arquiteturalmente, o sistema operacional entregará o /dev/console (e, subsequentemente, o PID 1 e os prompts de login de recuperação) para a última declaração de console encontrada na linha de comando.


5. Controle de Verbosidade: quiet, splash, debug e loglevel

O kernel Linux possui um sistema de limiares (thresholds) para a emissão de mensagens. Cada linha gerada pelo código do kernel (usando a função C printk) é prefixada com um nível de log, variando de 0 (emergência/pânico) a 7 (debug). O comportamento destes logs é modulado por flags na cmdline.

  • loglevel=N: Um parâmetro numérico (ex: loglevel=3) que manipula diretamente a variável interna de limiar do kernel (console_loglevel). Qualquer mensagem com prioridade mais alta (número menor) que N será enviada ao console.
  • quiet: Este parâmetro é um mero apelido (alias) arquitetural. Quando passado, ele simplesmente força a execução de console_loglevel = CONSOLE_LOGLEVEL_QUIET (tipicamente o nível 4). Isso silencia as mensagens informacionais triviais (descobertas de USB ACPI de baixo nível), mas falhas severas ainda irão perfurar o bloqueio.
  • debug: O antônimo de quiet. Ele define o console_loglevel = 10, habilitando até mesmo as mensagens de depuração microscópica, resultando em uma avalanche de logs na tela. Utilizado quase exclusivamente para engenharia reversa de falhas de drivers.
  • splash: Ao contrário do senso comum, o parâmetro splash é solenemente ignorado pelo kernel em Ring 0. Não há função no código-fonte do kernel mapeada para ele via __setup. Ele é lido do /proc/cmdline por daemons de espaço de usuário (como o Plymouth) para desenhar telas gráficas e ocultar o texto durante o processo de inicialização.

6. Delegação de Controle: O Parâmetro init=

A responsabilidade primária final da função start_kernel() e de suas threads filhas (kernel_init) é invocar o primeiro processo em espaço de usuário (PID 1). Por padrão estrutural, o kernel tenta executar arquivos em uma ordem codificada fisicamente: /sbin/init, /etc/init, /bin/init, /bin/sh.

A passagem do parâmetro init= sobrescreve totalmente esta heurística de array, instruindo o kernel a usar um binário explícito através de uma transição sys_execve.

  • init=/lib/systemd/systemd: Padrão adotado em distribuições modernas, forçando o uso do systemd independentemente de links simbólicos em /sbin.
  • init=/bin/bash: A técnica definitiva de invasão e recuperação física (Single-User Mode Override). Ao passar isso, o kernel monta a raiz, mas não inicia o sistema de init. Ele despeja um shell bash com privilégios de root completos. Como nenhum daemon foi carregado, a máquina não tem rede, os daemons não operam e não há syslog, configurando uma ponte de depuração forense inestimável.

7. Segurança, Mitigações e O "Lockdown Mode"

Devido ao poder absoluto garantido pela linha de comando, a integridade desta string é o foco central de modelos de ameaça e segurança física.

  • Parâmetros de Mitigação (mitigations=, spectre_v2=): Permitem que o administrador habilite ou desabilite correções de hardware para falhas de execução especulativa da CPU em tempo real, ponderando entre segurança absoluta e maximização de I/O.
  • **Ameaça do init=/bin/sh**: Qualquer pessoa com acesso físico ao teclado durante a tela do GRUB pode apertar e, injetar init=/bin/sh, e comprometer todo o sistema de arquivos (contornando travas PAM, passwords do /etc/shadow e autenticações LDAP).
  • Integração com Secure Boot e Kernel Lockdown: Em ecossistemas empresariais modernos, se a flag EFI UEFI Secure Boot estiver ativa, o kernel Linux automaticamente transita para o modo "Lockdown" (mesmo se não explicitly solicitado). Nesse estado, o kernel proíbe estritamente o bootloader de modificar a cmdline de maneira que burle a restrição (como desabilitar validações de assinaturas ou forçar init=/bin/sh). Para evitar isso em topologias extremas, as distribuições usam o UKI (Unified Kernel Image), onde o binário do kernel, o initramfs e a cmdline ASCII são compilados juntos em um único arquivo .efi e assinados criptograficamente de forma unitária, impedindo que a linha de comando seja manipulada independentemente sob nenhuma circunstância.