Microcódigo do 80386 foi desmontado
(reenigne.org)- A ROM de microcódigo do 80386 tem 94.720 bits, muito maior que os 10.752 bits do 8086, o que tornou a conversão e a verificação de imagens bem mais difíceis
- A partir de imagens de alta resolução do die, foi extraído e validado em poucos dias um blob binário combinando processamento de imagem, redes neurais e automação assistida por humanos
- No processo de desmontagem, foram sendo revelados gradualmente o arranjo de μ-ops, os campos de bits, os padrões de término de instrução, o decodificador de instruções e a estrutura da PLA de testes de proteção
- O 80386 possui microcódigo para todas as instruções, e muitas rotinas servem mais para configurar o hardware de multiplicação/divisão e o barrel shifter do que para implementar algoritmos
- Foi encontrado um possível defeito no tratamento do bitmap de permissão de E/S no modo protegido, no qual acessos de porta de 4 bytes aparentemente verificam apenas os 3 primeiros endereços, mas isso ainda não foi confirmado
Extração e desmontagem do microcódigo do 80386
- Após o microcódigo do 8086 ser desmontado, Ken Shirriff forneceu imagens em alta resolução da ROM de microcódigo do 80386, mas a ROM do 80386, com 94.720 bits, é muito maior que a do 8086, com 10.752 bits, tornando conversão e verificação bem mais difíceis
- O 8086 tinha uma patente com a estrutura geral e alguns trechos de código, o que dava pistas para a busca, mas o 80386 era praticamente uma caixa-preta completa, dificultando encontrar estrutura dentro de um grande bloco binário
- No Discord, GloriousCow, Smartest Blob e outros continuaram o trabalho de extrair o microcódigo a partir de imagens em alta resolução do die do 80386, e o principal obstáculo era transformar imagem → binário → microcódigo compreensível
- Combinando processamento de imagem, redes neurais e automação assistida por humanos, eles extraíram da imagem um blob binário e o validaram por verificação cruzada em poucos dias
Estruturas reveladas durante a desmontagem
- Mesmo após a extração do binário, a desmontagem não foi fácil, e ao ajustar vários padrões foi possível descobrir uma reorganização em que um eixo continha os μ-ops e o outro os bits dos μ-ops
- Havia um bloco de μ-ops não utilizado em uma das extremidades, o que serviu de pista para entender a ordem de leitura dos μ-ops
- Ao dividir os bits dos μ-ops em vários campos, a experiência anterior com o microcódigo do 8086 ajudou a identificar campos de registradores de origem e destino
- Como o 80386 pode executar operações da ALU em 2 ciclos, era necessário um campo que especificasse a segunda entrada da ALU para carregar os dois operandos no primeiro ciclo e enviar o resultado ao destino no segundo
- Padrões que apareciam repetidamente foram considerados marcadores de fim de instrução e depois se confirmou que isso estava correto
- Ken contribuiu rastreando vários fios e bits lógicos no die do 80386 para entender as conexões internas, e cada nova estrutura revelada ajudava a interpretar outros trechos de microcódigo que usavam os mesmos componentes
- Junto com o microcódigo, também foram decifrados o decodificador de instruções, composto por várias pequenas PLAs, e a PLA de testes de proteção, o que permitiu ligar instruções do 386 aos trechos correspondentes de microcódigo
Como a execução do 80386 difere da do 8086
- O 80386 é muito mais rápido por ciclo do que o 8086 na maioria das instruções, e para isso usa mais transistores
- Vários algoritmos que eram implementados em microcódigo no 8086 passaram, no 80386, a ser tratados por verdadeiros aceleradores de hardware
- Grande parte do microcódigo do 80386 serve mais para configurar aceleradores como o hardware de multiplicação/divisão, o barrel shifter e a unidade de testes de proteção do que para executar o algoritmo em si
- Uma parte importante do trabalho de desmontagem foi entender como essas interfaces dos aceleradores se conectam ao microcódigo
Pontos de entrada do microcódigo e processamento de instruções
- Há 215 pontos de entrada de microcódigo acessados a partir da ROM de decodificação, um aumento grande em relação aos 60 do 8086
- Esse aumento não se deve apenas a novas instruções, mas também ao fato de que a mesma instrução pode ser tratada por rotinas diferentes dependendo de o operando estar em registrador ou memória, de a CPU estar em modo real ou protegido e de um prefixo REP estar em uso
- A lista completa de entradas está no arquivo
fields.txt, junto com sub-rotinas e código compartilhado - Muitas rotinas de microcódigo de nível superior fazem pouco trabalho antes de saltar para rotinas compartilhadas com outros pontos de entrada, então o tamanho da rotina superior por si só não basta para entender seu significado
- Como o decodificador de instruções não usa apenas o opcode para escolher a rotina, também é difícil explicar a estrutura apenas pelo número de opcodes tratados por cada ponto de entrada
Todas as instruções são tratadas por microcódigo
- Diferentemente do 8086 ou de CPUs modernas, o 80386 sempre executa μ-ops, e existe microcódigo correspondente para todas as instruções
- Foi confirmado que não há instruções tratadas sem microcódigo
Código não utilizado e vestígios de tratamento de exceções
- As rotinas de
0x849a0x856aparecem marcadas comounused?na desmontagem do microcódigo e parecem não ter pontos de entrada conectados - O funcionamento exato dessas rotinas não é totalmente certo, mas elas têm muitas semelhanças com as rotinas
#PF(PAGE_FAULT) de0x8e9a0x8f5 - Em ambos os casos, o último código de erro da unidade de paginação é configurado e depois o fluxo segue para a interrupção 0x0e
- A diferença é que essas rotinas parecem gravar em
CR2um valor desconhecido vindo da unidade de paginação, em vez do fault linear address - O restante do microcódigo parece ter sido projetado para implementar o comportamento documentado da CPU, enquanto rotinas que interagem com hardware ICE (In-Circuit Emulator) de depuração de baixo nível correspondem a comportamentos não documentados
Recursos ocultos e possível falha no bitmap de permissão de E/S
- Ainda não foi possível confirmar em uma máquina 386 real, mas pode haver uma falha no tratamento do bitmap de permissão de E/S, usado por alguns sistemas operacionais em modo protegido para permitir acesso limitado de processos em modo usuário a portas de E/S
- Quando ocorre um acesso de porta de 4 bytes, o microcódigo parece verificar apenas os bits de permissão dos 3 primeiros endereços
- Se um processo fizer esse acesso na fronteira de uma área de portas de E/S para a qual tem permissão, o acesso ao último byte pode acabar sendo aceito indevidamente, permitindo tocar em um registrador de hardware ao qual o SO não pretendia dar acesso
- Esse bug é muito específico e poderia ter passado despercebido sem a desmontagem do microcódigo, mas ainda assim é incomum que uma falha de segurança em hardware tão amplamente usado por mais de 40 anos não tenha sido descoberta
- Esse comportamento pode ter existido apenas em algumas versões da CPU, ou a interpretação da rotina pode estar errada e o funcionamento real ser o correto
- Este microcódigo não parece ser de uma versão inicial do 80386, e as instruções
XBTSeIBTSnão deixam vestígios além do decodificador
Material de estudo e onde baixar
- O texto sobre a estrutura interna do 80386 de nand2mario é um bom ponto de partida para entender a desmontagem
- 80386 Multiplication and Division
- 80386 Barrel shifter
- 80386 Protection
- 80386 Memory Pipeline
- O resultado da desmontagem pode ser baixado no repositório x86 microcode no GitHub
parts.txtexplica o papel dos outros arquivos, emicrocode_10.txté o arquivo que leva diretamente à própria desmontagem do microcódigo
1 comentários
Comentários no Hacker News
Fiquei curioso sobre como é possível restaurar o microcódigo a partir de imagens de die em alta resolução
Queria entender se o processo consiste em identificar cada transistor para modelar o circuito, e se o resultado fica em algo como Verilog
Depois classificamos 0 e 1; o 1 era distinguido visualmente pela presença de transistor e pela lacuna no polissilício
Pelas características do microcódigo da Intel, dava para assumir que havia muito mais 0 do que 1, então, se havia transistor, considerávamos 1
Existem ferramentas que fazem isso automaticamente por limiar de cor, mas algumas partes do mosaico estavam borradas e havia poeira, o que gerava muitos bits 1 falsos, então não funcionou bem
Em vez disso, treinamos uma rede neural convolucional para classificar as regiões de bits extraídas como 0/1 e conferimos o resultado cobrindo o mosaico original com retângulos brancos/pretos com 50% de transparência
Depois passamos vários dias revisando erros de forma tediosa e, por fim, obtivemos a matriz bruta bidimensional de bits; a próxima etapa é extrair dessa matriz as palavras de microcódigo
https://youtu.be/HwEdqAb2l50?si=VFLed64PZvpCHfy1
“A foto acima mostra uma parte da ROM de microcódigo. Ao microscópio, dá para ver o conteúdo da ROM de microcódigo, e é possível ler os bits dependendo de haver ou não transistor em cada posição”
https://www.righto.com/2020/06/a-look-at-die-of-8086-process...
Thread relacionada em andamento: z386: An Open-Source 80386 Built Around Original Microcode - https://news.ycombinator.com/item?id=48248014 - maio de 2026, 22 comentários
Quando vi o blog do reenigne alguns dias atrás, pensei “hmm, não tem post desde 2020”, então foi bom vê-lo voltar
Também é especialmente interessante que o blog tenha histórico de 33 anos
Um bom livro que explica microprogramação desde a base: https://www.amazon.com/Computation-Structures-Optical-Electr...
Também é fácil encontrar um PDF gratuito
O esforço necessário para fazer a engenharia reversa desse microcódigo é impressionante, e é um excelente texto que mergulha fundo na arquitetura do 386
Ver uma implementação real de microcódigo torna menos misterioso como processadores antigos lidavam com operações complexas
O 386 teve muitas pequenas mudanças ao longo de seus 22 anos de produção, então é importante saber de qual revisão do 386 esse código veio
9B5 BIST1 -> TMPD 0x0303 PASS29B6 SIGMA -> EDX9B7 BIST2 -> TMPE TMPD XOR9B8 SIGMA 0x3ddc0c2c XOR9B9 SIGMA -> EAX BOOTUP_JUMP JFPUOK0x303significa família 3, modelo 0, stepping ID 3A análise de caixa-preta necessária para decifrar isso é absurdamente difícil, mas, se der certo, parece muito divertida e recompensadora
É satisfatório ter cursado disciplinas difíceis na faculdade para conseguir entender textos como esse, e também foi legal que o HN naquela época, em 2015, estimulasse esse tipo de pensamento
Hoje eu já não uso tanto conhecimento de programação de baixo nível, mas toda vez que leio algo assim sinto que minha consciência fica mais rica, o que é muito bacana
Para quem tem pouco acesso à universidade, recomendo nand2tetris.org
Estudar projetos antigos e simples, como RISC ou Transputer, também ajuda, e o 80386 está no extremo oposto desse espectro
Isso porque ele ficou desnecessariamente complexo ao tentar manter compatibilidade retroativa com projetos antigos ruins
Não é obrigatório passar por uma universidade para aprender projeto de chips; assistir a algumas palestras do Alan Kay ou folhear materiais de projeto de computadores no Bitsavers também é um bom começo
Criei o Morphle Logic para simular projeto em nível de gate de um jeito mais fácil que FPGA e converter isso em transistores em chip por menos de 200 dólares em valores de 2026
No fim, isso pode evoluir para integração em supercomputadores em escala de wafer cada vez maiores, mais rápidos e mais baratos
https://github.com/fiberhood/MorphleLogic/blob/main/README_M...
https://www.youtube.com/watch?v=vbqKClBwFwI
https://www.youtube.com/watch?v=f1605Zmwek8
http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/alto/...
Essa simplicidade em si é uma grande lição e me inspirou bastante, mas as aulas de engenharia elétrica que fiz na faculdade nos anos 1990 eram parecidas com nand2tetris, só que também mostravam como CPUs do tipo 8086 eram construídas, incluindo a explicação de como o microcódigo funcionava
Havia um contador de programa interno que percorria uma tabela de palavras de controle, e cada bit coordenava diretamente uma parte controlável da CPU
Cada aluno implementava uma instrução em seu simulador; eu fiquei com DEC, isto é, a instrução de decremento
Em certo sentido, as instruções do nand2tetris também podem ser vistas como microcódigo
Os bits da instrução controlam o hardware diretamente, e o primeiro bit escolhe entre dois tipos de instrução, então cada instrução tem apenas 1 etapa de código
Já no microcódigo, uma instrução pode ter um número arbitrário de etapas de microcódigo
Nos vídeos da CPU breadboard de 8 bits do Ben Eater, a palavra de controle é determinada indexando a ROM com o opcode de 4 bits da instrução e o contador de etapas
Essa ROM substitui uma parte que também poderia ser feita com portas lógicas suficientemente complexas e, como exige mexer diretamente com eletrônica e resolver problemas, é um bom próximo passo no lado do hardware
Só é uma pena que a RAM tenha apenas 16 bytes, o que dificulta construir camadas de abstração mais altas como no nand2tetris
Nesse ponto, dá para refazer com um projeto melhor, passar para PCB ou ir para um projeto com 6502 para pensar CPU, ROM, RAM, E/S, UART e temporizadores como um sistema integrado, antes de então migrar para microcontroladores onde tudo isso já vem junto
Se quiser ler sobre como construir uma CPU com portas lógicas, Code, de Charles Petzold, explica devagar e foi revisado recentemente, enquanto The Pattern on the Stone, de Danny Hillis, avança mais rápido
A 2ª edição de Code usa um contador de ciclo de 4 bits e lógica hardwired para definir a ação de cada ciclo, além de usar arranjos de diodos em parte da lógica; fico curioso se isso também deveria ser considerado microcódigo