Configuração de boot¶
- Autor:
Masami Hiramatsu <mhiramat@kernel.org>
Visão geral¶
A configuração de boot expande a linha de comando atual do kernel para suportar dados adicionais de chave-valor ao inicializar o kernel de forma eficiente. Isso permite que os administradores passem um arquivo de configuração estruturado por chaves.
Sintaxe do arquivo de configuração¶
A sintaxe de configuração de boot é uma estrutura simples de chave-valor. Cada
chave consiste em palavras conectadas por pontos, e a chave e o valor são
conectados por =. A string de valor deve ser terminada pelos seguintes
delimitadores descritos abaixo.
Cada palavra-chave deve conter apenas letras do alfabeto, números, traço (-)
ou sublinhado (_). E cada valor contém apenas caracteres imprimíveis ou espaços,
exceto delimitadores como ponto e vírgula (;), quebra de linha (\n),
vírgula (,), cerquilha (#) e chave de fechamento (}).
Se o = for seguido por espaços em branco até um desses delimitadores, a chave
será atribuída a um valor vazio.
Para arrays, os valores do array são separados por vírgula (,), e comentários
e quebras de linha com nova linha (\n) são permitidos entre os valores do array
para facilitar a legibilidade. Assim, a primeira entrada do array deve estar na
mesma linha da chave.:
CHAVE[.PALAVRA[...]] = VALOR[, VALOR2[...]][;]
Diferente da sintaxe de linha de comando do kernel, espaços em branco (incluindo
tabs) são ignorados ao redor da vírgula e do =.
Se você quiser usar esses delimitadores em um valor, pode usar aspas duplas
("VALOR") ou aspas simples ('VALOR') para colocá-lo entre aspas. Observe
que você não pode escapar essas aspas.
Pode haver uma chave que não tenha valor ou tenha um valor vazio. Essas chaves são usadas para verificar se a chave existe ou não (como um booleano).
Sintaxe de chave-valor¶
A sintaxe do arquivo de configuração de boot permite que o usuário mescle chaves com palavras parcialmente iguais usando chaves (braces). Por exemplo:
foo.bar.baz = value1
foo.bar.qux.quux = value2
Elas também podem ser escritas assim:
foo.bar {
baz = value1
qux.quux = value2
}
Ou de forma mais concisa, escrita da seguinte maneira:
foo.bar { baz = value1; qux.quux = value2 }
Em ambos os estilos, as mesmas palavras-chave são mescladas automaticamente ao serem analisadas no momento da inicialização. Assim, você pode anexar árvores ou pares de chave-valor semelhantes.
Valores com a mesma chave¶
É proibido que dois ou mais valores ou arrays compartilhem a mesma chave. Por exemplo:
foo = bar, baz
foo = qux # !ERRO! não podemos redefinir a mesma chave
Se você quiser atualizar o valor, deve usar o operador de substituição (override)
:= explicitamente. Por exemplo:
foo = bar, baz
foo := qux
então, qux é atribuído à chave foo. Isso é útil para sobrescrever o
valor padrão adicionando bootconfigs personalizados (parciais) sem precisar
analisar o bootconfig padrão.
Se você quiser anexar o valor a uma chave existente como membro de um array,
pode usar o operador +=. Por exemplo:
foo = bar, baz
foo += qux
Neste caso, a chave foo conterá bar, baz e qux.
Além disso, subchaves e um valor podem coexistir sob uma chave pai. Por exemplo, a seguinte configuração é permitida:
foo = value1
foo.bar = value2
foo := value3 # Isto atualizará o valor de foo.
Observe que, como não há sintaxe para colocar um valor puro diretamente sob uma chave estruturada, você deve defini-lo fora das chaves. Por exemplo:
foo {
bar = value1
bar {
baz = value2
qux = value3
}
}
Além disso, a ordem do nó de valor sob uma chave é fixa. Se houver um valor e subchaves, o valor será sempre o primeiro nó filho da chave. Portanto, se o usuário especificar subchaves primeiro, por exemplo:
foo.bar = value1
foo = value2
No programa (e em /proc/bootconfig), ele será exibido conforme abaixo:
foo = value2
foo.bar = value1
Comentários¶
A sintaxe de configuração aceita comentários no estilo shell-script. Os comentários iniciados com cerquilha (“#”) até a quebra de linha (”n”) serão ignorados.
# linha de comentário
foo = value # valor atribuído a foo.
bar = 1, # 1º elemento
2, # 2º elemento
3 # 3º elemento
Isto é analisado como abaixo:
foo = value
bar = 1, 2, 3
Observe que você NÃO pode colocar um comentário ou uma quebra de linha entre o
valor e o delimitador (, ou ;). Isso significa que a seguinte configuração
possui um erro de sintaxe:
key = 1 # comentário
,2
/proc/bootconfig¶
/proc/bootconfig é uma interface de espaço de usuário da configuração de boot. Diferente de /proc/cmdline, este arquivo exibe a lista no estilo chave-valor. Cada par chave-valor é mostrado em cada linha no seguinte formato:
CHAVE[.PALAVRAS...] = "[VALOR]"[,"VALOR2"...]
Inicializando o kernel com um Boot Config¶
Existem duas opções para inicializar o kernel com bootconfig: anexar o bootconfig à imagem initrd ou embuti-lo no próprio kernel.
Anexando um Boot Config ao Initrd¶
Como o arquivo de configuração de boot é carregado com o initrd por padrão, ele será adicionado ao final do arquivo de imagem do initrd (initramfs) com preenchimento (padding), tamanho, checksum e palavra mágica de 12 bytes conforme abaixo.
[initrd][bootconfig][padding][size(le32)][checksum(le32)][#BOOTCONFIGn]
Os campos de tamanho (size) e checksum são valores de 32 bits sem sinal em little endian.
Quando a configuração de boot é adicionada à imagem do initrd, o tamanho total
do arquivo é alinhado a 4 bytes. Para preencher a lacuna, caracteres nulos
(\0) serão adicionados. Assim, size é o comprimento do arquivo bootconfig
+ bytes de preenchimento.
O kernel Linux decodifica a última parte da imagem do initrd na memória para obter os dados de configuração de boot. Por causa desse método “piggyback”, não há necessidade de alterar ou atualizar o carregador de inicialização (boot loader) e a própria imagem do kernel, desde que o boot loader passe o tamanho correto do arquivo initrd. Se, por qualquer motivo, o boot loader passar um tamanho maior, o kernel não conseguirá encontrar os dados do bootconfig.
Para realizar esta operação, o kernel Linux fornece o comando bootconfig sob
tools/bootconfig, que permite ao administrador aplicar ou excluir o arquivo de
configuração na/da imagem do initrd. Você pode compilá-lo com o seguinte comando:
# make -C tools/bootconfig
Para adicionar seu arquivo de configuração de boot à imagem do initrd, execute o bootconfig conforme abaixo (dados antigos são removidos automaticamente se existirem):
# tools/bootconfig/bootconfig -a your-config /boot/initrd.img-X.Y.Z
Para remover a configuração da imagem, você pode usar a opção -d como abaixo:
# tools/bootconfig/bootconfig -d /boot/initrd.img-X.Y.Z
Em seguida, adicione “bootconfig” na linha de comando normal do kernel para instruir
o kernel a procurar pelo bootconfig no final do arquivo initrd.
Como alternativa, compile seu kernel com a opção Kconfig CONFIG_BOOT_CONFIG_FORCE
selecionada.
Embutindo um Boot Config no kernel¶
Se você não puder usar initrd, também poderá embutir o arquivo bootconfig no kernel através de opções do Kconfig. Nesse caso, você precisa recompilar o kernel com as seguintes configurações:
CONFIG_BOOT_CONFIG_EMBED=y
CONFIG_BOOT_CONFIG_EMBED_FILE="/CAMINHO/PARA/ARQUIVO/BOOTCONFIG"
CONFIG_BOOT_CONFIG_EMBED_FILE requer um caminho absoluto ou um caminho
relativo para o arquivo bootconfig a partir da árvore de código-fonte ou da
árvore de objetos. O kernel o embutirá como o bootconfig padrão.
Assim como ao anexar o bootconfig ao initrd, você precisa da opção bootconfig
na linha de comando do kernel para habilitar o bootconfig embutido ou,
alternativamente, compilar seu kernel com a opção Kconfig
CONFIG_BOOT_CONFIG_FORCE selecionada.
Observe que, mesmo se você definir esta opção, poderá sobrescrever o bootconfig embutido por outro bootconfig anexado ao initrd.
Renderizando chaves kernel.* embutidas em tempo de compilação¶
Por padrão, o bootconfig embutido (CONFIG_BOOT_CONFIG_EMBED=y) é analisado
em tempo de execução, após parse_early_param() já ter sido executado. Os
tratadores de parâmetros iniciais (early parameters) (mem=, earlycon=,
loglevel=, ...) não conseguem, portanto, ver valores fornecidos através da
subárvore kernel embutida.
A opção CONFIG_CMDLINE_FROM_BOOTCONFIG resolve isso renderizando a subárvore
kernel de CONFIG_BOOT_CONFIG_EMBED_FILE em uma string de cmdline plana em
tempo de compilação do kernel (via tools/bootconfig -C) e prefixando-a a
boot_command_line durante a configuração arquitetural inicial, de modo que
as chaves fiquem visíveis para parse_early_param().
A opção requer CONFIG_BOOT_CONFIG_EMBED=y, um CONFIG_BOOT_CONFIG_EMBED_FILE
não vazio, CONFIG_CMDLINE vazio e uma arquitetura que selecione
CONFIG_ARCH_SUPPORTS_CMDLINE_FROM_BOOTCONFIG. Atualmente apenas x86 a seleciona;
em outras arquiteturas, o bootconfig embutido ainda funciona, mas apenas por meio
do analisador tardio em tempo de execução.
A mesma ativação explícita de bootconfig se aplica como em outros lugares:
as chaves renderizadas são prefixadas apenas quando bootconfig (em qualquer
forma) aparece na linha de comando do kernel, ou quando CONFIG_BOOT_CONFIG_FORCE
está definido, cujo padrão é y quando CONFIG_BOOT_CONFIG_EMBED está definido.
Por exemplo, dado:
kernel {
loglevel = 7
mem = 4G
}
o kernel inicializa como se loglevel=7 mem=4G tivesse sido prefixado à linha
de comando do carregador de inicialização, com os valores visíveis para os
tratadores analisados precocemente. Valores separados por vírgula ainda são
expandidos em múltiplas entradas de linha de comando de acordo com a convenção
de array do bootconfig -- o kernel.earlycon = "uart8250,io,0x3f8" embutido
deve estar entre aspas para resultar em uma única entrada earlycon=, exatamente
como no analisador em tempo de execução.
Se a string renderizada não couber em COMMAND_LINE_SIZE juntamente com a
linha de comando existente, o prefixo será ignorado e um erro será registrado,
de modo que um bootconfig embutido superdimensionado não possa travar a inicialização.
Interação com outras fontes de linha de comando e bootconfig¶
Com CONFIG_CMDLINE_FROM_BOOTCONFIG=y, a subárvore kernel renderizada
comporta-se como uma linha de comando em tempo de compilação (semelhante a
CONFIG_CMDLINE), e não como uma fonte de bootconfig. Ela é prefixada a
boot_command_line em setup_arch(), antes de parse_early_param() e
muito antes de o analisador em tempo de execução inspecionar um initrd. As opções
podem chegar ao kernel a partir de até quatro locais:
Linha de comando do carregador de inicialização (bootloader): os argumentos que o carregador de inicialização passa. A linha de comando embutida é prefixada na frente deles, portanto, para parâmetros onde o último vence (last-one-wins), uma opção do bootloader ainda sobrescreve o valor embutido. Visível em /proc/cmdline.
Linha de comando embutida (esta opção): a subárvore
kernelrenderizada, prefixada precocemente para ser vista porparse_early_param(). Visível em /proc/cmdline.Bootconfig do initrd: analisado tardiamente em
setup_boot_config(); suas chaveskernelsão colocadas antes deboot_command_line, ou seja, antes da linha de comando embutida, portanto, a regra do último vencedor favorece os valores embutidos. Como uma fonte de bootconfig, um bootconfig do initrd ainda substitui o bootconfig embutido. Visível em /proc/cmdline e /proc/bootconfig.Bootconfig embutido (tempo de execução): analisado tardiamente, apenas quando nenhum bootconfig do initrd estiver presente. Visível em /proc/cmdline e /proc/bootconfig.
Assim, com esta opção, os valores kernel.* embutidos têm precedência sobre os
valores kernel.* de um bootconfig do initrd: para parâmetros iniciais, o initrd
ainda não foi analisado e, para parâmetros comuns, as chaves embutidas chegam mais
tarde na linha de comando. Se você precisa que um bootconfig do initrd sobrescreva
as chaves kernel.* embutidas, deixe esta opção desativada e confie no analisador
em tempo de execução.
A string renderizada faz parte da linha de comando, portanto aparece em /proc/cmdline. Ela deliberadamente não é mostrada em /proc/bootconfig: esse arquivo continua relatando a árvore de bootconfig analisada -- o bootconfig do initrd se presente, caso contrário o bootconfig embutido -- independentemente de a renderização da linha de comando em tempo de compilação estar ativada.
Parâmetros do kernel via Boot Config¶
Além da linha de comando do kernel, a configuração de boot pode ser usada para
passar os parâmetros do kernel. Todos os pares chave-valor sob a chave kernel
serão passados diretamente para a linha de comando do kernel. Além disso, os pares
chave-valor sob init serão passados para o processo init via linha de comando.
Os parâmetros são concatenados com a string de linha de comando do kernel fornecida
pelo usuário na seguinte ordem, de modo que o parâmetro de linha de comando possa
sobrescrever os parâmetros de bootconfig (isso depende de como o subsistema lida com
parâmetros, mas em geral, o parâmetro anterior será sobrescrito pelo posterior):
[bootconfig params][cmdline params] -- [bootconfig init params][cmdline init params]
Aqui está um exemplo de arquivo bootconfig para parâmetros de kernel/init:
kernel {
root = 01234567-89ab-cdef-0123-456789abcd
}
init {
splash
}
Isto será copiado na string de linha de comando do kernel da seguinte forma:
root="01234567-89ab-cdef-0123-456789abcd" -- splash
Se o usuário fornecer alguma outra linha de comando como:
ro bootconfig -- quiet
A linha de comando final do kernel será a seguinte:
root="01234567-89ab-cdef-0123-456789abcd" ro bootconfig -- splash quiet
Limitações do arquivo de configuração¶
Atualmente, o tamanho máximo da configuração é de 32 KB e o total de palavras-chave (não entradas de chave-valor) deve ser inferior a 1024 nós. Nota: este não é o número de entradas, mas de nós; uma entrada deve consumir mais de 2 nós (uma palavra-chave e um valor). Portanto, teoricamente, haverá até 512 pares chave-valor. Se as chaves contiverem 3 palavras em média, poderá conter 256 pares chave-valor. Na maioria dos casos, o número de itens de configuração estará abaixo de 100 entradas e será menor que 8 KB, o que seria suficiente. Se o número de nós exceder 1024, o analisador retornará um erro mesmo se o tamanho do arquivo for menor que 32 KB. (Observe que esse tamanho máximo não inclui os caracteres nulos de preenchimento.) De qualquer forma, como o comando bootconfig verifica isso ao anexar uma configuração de boot à imagem initrd, o usuário pode perceber antes da inicialização.
APIs do Bootconfig¶
O usuário pode consultar ou iterar sobre pares chave-valor; também é possível encontrar um nó de chave raiz (prefixo) e encontrar chaves-valores sob esse nó.
Se você tiver uma string de chave, poderá consultar o valor diretamente com a chave
usando xbc_find_value(). Se quiser saber quais chaves existem no bootconfig, pode
usar xbc_for_each_key_value() para iterar sobre os pares chave-valor.
Observe que você precisa usar xbc_array_for_each_value() para acessar o valor de
cada array, por exemplo:
vnode = NULL;
xbc_find_value("key.word", &vnode);
if (vnode && xbc_node_is_array(vnode))
xbc_array_for_each_value(vnode, value) {
printk("%s ", value);
}
Se você quiser focar em chaves que tenham uma string de prefixo, pode usar
xbc_find_node() para encontrar um nó pela string de prefixo e iterar pelas chaves
sob o nó de prefixo com xbc_node_for_each_key_value().
Mas o uso mais típico é obter o valor nomeado sob o prefixo ou obter o array nomeado sob o prefixo, como abaixo:
root = xbc_find_node("key.prefix");
value = xbc_node_find_value(root, "option", &vnode);
...
xbc_node_for_each_array_value(root, "array-option", value, anode) {
...
}
Isso acessa um valor de “key.prefix.option” e um array de “key.prefix.array-option”.
O bloqueio (locking) não é necessário, pois após a inicialização, a configuração torna-se somente leitura. Todos os dados e chaves devem ser copiados se você precisar modificá-los.
Funções e estruturas¶
Para a documentação kernel-doc das definições, consulte:
include/linux/bootconfig.h
lib/bootconfig.c