Pular para o conteúdo

A Linha A20

A linha A20 é a 21ª linha de endereço físico (bit 20, contando a partir de zero) no barramento de endereço da CPU. Em hardware x86 real ela começa mascarada para zero, independentemente do que o bit correspondente num endereço calculado de fato contém: um padrão mantido puramente por compatibilidade retroativa, não por necessidade elétrica alguma. Protected Mode e Emulating & Debugging ambos a mencionam de passagem, o primeiro como a razão pela qual memória acima de 1 MB continua não confiável depois de entrar em protected mode, sem nenhum dos dois explicar o mecanismo em si.

O 8086 original tem apenas vinte linhas de endereço, dando a ele uma faixa endereçável de 1 MB, mas seu esquema de endereçamento segmentado (segmento × 16 + offset) consegue calcular um resultado de 21 bits: um endereço como 0xFFFF:0xFFFF soma para 0x10FFEF, um megabyte e aproximadamente 64 KB além do topo do que os vinte pinos do chip conseguem de fato carregar. No próprio 8086 isso simplesmente dava a volta silenciosamente de volta para o fundo do espaço de endereçamento, já que o 21º bit não tinha para onde ir; quando o 80286 chegou com uma 21ª linha de endereço genuína, software já escrito dependendo desse wraparound (deliberadamente ou não) teria começado a endereçar memória real e diferente perto da marca de 1 MB em vez de dar a volta, quebrando de uma forma que o design do PC/AT da IBM escolheu especificamente prevenir, controlando a 21ª linha através de uma lógica extra que, por padrão, a mantém forçada em zero.

Habilitar a linha A20 significa dizer a essa lógica de controle para parar de mascarar o bit 20, e vários mecanismos em grande parte redundantes existem para fazer isso, uma consequência de o gate historicamente viver atrás do mesmo controlador de teclado usado para um periférico inteiramente não relacionado. O método original do IBM PC/AT reaproveita dois pinos de saída sobressalentes no próprio controlador de teclado 8042, enviando um byte de comando que alterna o gate como efeito colateral de um chip cujo trabalho real é varrer uma matriz de teclado:

; método clássico via controlador de teclado 8042
wait_8042:
in al, 0x64
test al, 2
jnz wait_8042
mov al, 0xD1
out 0x64, al
wait_8042_2:
in al, 0x64
test al, 2
jnz wait_8042_2
mov al, 0xDF ; habilita A20
out 0x60, al

Fast A20, uma alternativa posterior e consideravelmente mais simples, expõe o mesmo gate através de uma única porta de I/O dedicada, 0x92, onde definir o bit 1 habilita a linha diretamente sem handshake de byte de comando algum:

in al, 0x92
or al, 2
out 0x92, al

Uma terceira opção, disponível apenas enquanto ainda rodando sob serviços de modo real da BIOS (e portanto inutilizável uma vez que o kernel já tenha transicionado para longe deles), chama INT 0x15 com AX = 0x2401, delegando o mecanismo real de habilitação para o que quer que o próprio firmware implemente. Nenhum dos três é universalmente garantido presente em todo chipset, motivo pelo qual bootloaders do mundo real comumente tentam mais de um método em sequência em vez de depender de um único ter sucesso incondicionalmente.

Como alguns emuladores e um subconjunto de hardware real habilitam A20 por padrão, ou porque um estágio de bootloader rodando antes do próprio código do kernel pode já tê-la habilitado, testar o estado atual antes de tentar mudá-lo evita trabalho redundante ou conflitante. O teste padrão compara dois endereços físicos exatamente 1 MB separados, um dos quais só dá a volta para fazer alias do outro se A20 ainda estiver desabilitada:

; compara 0000:0500 e FFFF:0510 (1 MB separados, fazem alias sse A20 está desligada)
mov ax, 0xFFFF
mov es, ax
mov word [0x0500], 0x1234
mov word [es:0x0510], 0x5678
cmp word [0x0500], 0x1234 ; se isso agora lê 0x5678, A20 está desabilitada

Se A20 está desabilitada, a escrita através do alias es:0x0510 cai exatamente no mesmo byte físico que 0x0500: os dois endereços, 0x100500 e 0x000500, diferem por exatamente 2^20 (um megabyte), então mascarar o 21º bit os torna indistinguíveis, e o primeiro valor é silenciosamente sobrescrito; se A20 está habilitada, os dois endereços são genuinamente distintos e o valor original sobrevive intocado. Escrever em dois offsets diferentes e imprevisíveis, em vez de um único par fixo, e restaurar o que quer que estivesse originalmente ali depois, evita que o próprio teste corrompa memória que um bootloader ainda possa precisar.

Esquecer de habilitar A20 por completo produz uma das classes mais desorientadoras de bug de boot inicial precisamente porque não falha: uma tabela de página, GDT, ou pedaço de código de kernel colocado logo depois da marca de 1 MB parece carregar e executar normalmente em alguns caminhos de código enquanto silenciosamente lê ou escreve na memória física errada, um megabyte abaixo do pretendido, em outros, dependendo de quais endereços específicos são tocados e se fazem alias de algo já em uso. Esse é também o motivo pelo qual o processo de boot geralmente trata A20 como algo a ser resolvido uma vez, cedo, e definitivamente, antes de qualquer código que presuma que memória acima de 1 MB se comporta normalmente rodar: deixá-la em um estado desconhecido e torcer para que já esteja habilitada é uma fonte comum de falhas intermitentes e dependentes de hardware que não se reproduzem identicamente em toda máquina ou emulador em que um kernel é testado.

  1. ^ IBM, Personal Computer AT Technical Reference: a documentação original do PC/AT descrevendo o gate de A20 do controlador de teclado e a lógica de compatibilidade por trás dele.
  • Protected Mode: por que A20 importa, já que o protected mode expõe memória acima de 1 MB que permanece não confiável sem ela habilitada.
  • The Boot Process: o ponto do boot onde A20 precisa ser resolvida antes que estágios posteriores possam confiar em memória acima de 1 MB.
  • Emulating & Debugging: a emulação incomumente fiel do gate A20 e da segmentação de real mode pelo Bochs.
  • Reiniciando e Desligando: o outro propósito, não relacionado, para o qual o controlador de teclado 8042 pulsado aqui também serve.