paint-brush
O mundo oculto do firmware: explorando o processo de inicialização do seu computadorpor@tristejoursoir
3,062 leituras
3,062 leituras

O mundo oculto do firmware: explorando o processo de inicialização do seu computador

por Aleksandr Goncharov15m2023/04/21
Read on Terminal Reader

Muito longo; Para ler

Neste artigo, apresentamos uma visão geral do processo de inicialização, incluindo seus vários estágios, os principais componentes envolvidos e os desafios enfrentados durante o processo. Embora nosso foco principal seja a **arquitetura x86** (a mais amplamente usada), outras arquiteturas teriam muitas semelhanças em seu processo de inicialização.
featured image - O mundo oculto do firmware: explorando o processo de inicialização do seu computador
Aleksandr Goncharov HackerNoon profile picture


Muitas pessoas estão interessadas em saber como o computador é inicializado. É aqui que a mágica começa e continua enquanto o dispositivo estiver ligado. Neste artigo, daremos uma visão geral do processo de inicialização , incluindo seus vários estágios, os principais componentes envolvidos e os desafios enfrentados durante o processo.


Embora nosso foco principal seja a arquitetura x86 (a mais usada), outras arquiteturas teriam muitas semelhanças em seu processo de inicialização. Espero que este artigo seja um recurso valioso para quem deseja aprofundar seus conhecimentos neste campo. Aqui vamos nós!

Índice:

  • ROM DE BOOT
    • Memória não volátil
      • Programável uma vez
      • Campo programável
    • Executar no local (XIP)
      • Cache como RAM (CAR)
    • Layout e mapeamento de memória
      • Modo não descritor
      • Intel Flash Descriptor/Modo descritor
      • Tabela de interface de firmware Intel (FIT)
      • Estrutura de Firmware Incorporado AMD
  • Inicialização de Silício
    • Pacote de suporte de firmware Intel (FSP)
    • AMD Generic Encapsulated Software Architecture (AGESA)
  • Subsistemas autônomos
    • Intel ME
    • AMD PSP
  • Sequências de energia de hardware

ROM DE BOOT

Um circuito integrado (chip) que fica localizado na placa-mãe e armazena o código do firmware responsável pela inicialização do computador é chamado de BOOT ROM . Este nome não é padronizado, então outros desenvolvedores costumam chamá-lo de FLASH ROM , BIOS FLASH , BOOT FLASH , SPI FLASH , etc (esses nomes são dados a eles por causa da tecnologia, interface e nomes de finalidade). Não se preocupe, esses termos são intercambiáveis. O código do firmware no BOOT ROM é executado primeiro quando o computador é ligado. Ele executa testes básicos, inicializa o hardware e, em seguida, carrega o carregador do sistema operacional de um dispositivo inicializável, como um disco rígido ou uma unidade USB, na memória. Este chip é feito de memória não volátil (NVM) .

Memória não volátil

A memória não volátil é um tipo de memória de computador que retém seu conteúdo mesmo quando a energia é desligada . Isso torna esse tipo de memória ideal para armazenar dados importantes que precisam ser retidos mesmo quando o computador está desligado. Além disso, a discussão será focada apenas na memória que contém o código do firmware. Não falaremos sobre armazenamento como unidades de disco rígido (HDD), unidades de estado sólido (SSD), disquetes e assim por diante.


Basicamente, podemos categorizar esse tipo de memória nos seguintes grupos.

Programável uma vez

  • Masked ROM: O conteúdo é determinado no momento da fabricação e não pode ser alterado depois disso.
  • ROM programável (PROM): Ao contrário da ROM mascarada, esse tipo de memória pode ser programada após a fabricação. Mas ainda apenas uma vez.

Campo programável

  • ROM programável apagável (EPROM): pode ser programado várias vezes, mas seu conteúdo pode ser apagado e reprogramado usando luz ultravioleta .


  • Programável eletricamente apagável (EEPROM): Pode ser reprogramado várias vezes usando sinais elétricos .


    • Memória Flash NOR : Arquitetonicamente arranjada em blocos onde os dados são apagados no nível do bloco e podem ser lidos ou escritos no nível do byte . A memória NOR é diretamente acessível usando uma interface padrão, como byte parallel, I2C ou SPI.


      Na indústria, existe uma convenção para reservar o termo EEPROM para memórias apagáveis por byte em comparação com memórias flash apagáveis por bloco.


A memória programável vem com uma regra de apagar antes de gravar . Nessa memória, escrever novos dados é mais complicado porque os dados são armazenados como uma carga em uma porta flutuante (a razão pela qual a maior parte está apenas na física das células de memória). A quantidade de carga no portão determina se a célula armazena "0" ou "1".


Quando você apaga um chip de memória flash, define todos os bits de dados armazenados nele para um estado conhecido (padrão), normalmente um "1" lógico. Isso permite que você comece do zero, por assim dizer, e programe novos dados no chip sem ter nenhum resquício dos dados antigos ainda armazenados nele. Quando novos dados são gravados no chip, o estado dos bits individuais é alterado de "1" para "0" para representar os novos dados.


Se você simplesmente gravar novos dados no chip sem primeiro apagá-los, os novos dados serão combinados com os dados antigos, resultando em resultados imprevisíveis. Por exemplo, considere um chip de memória flash que possui 8 bits de memória armazenando o valor "0110 0010". Se você escrever novos dados "1100 1001" no chip sem primeiro apagá-los, o estado resultante do chip será "0100 0000", o que pode não ser o que você pretendia.


A principal confusão está relacionada à palavra ROM , que significa Read Only Memory . O termo "memória somente leitura" tem sido historicamente usado para se referir à memória que é permanente e não pode ser alterada pelo usuário. No entanto, com o avanço da tecnologia, a definição de ROM mudou e agora é frequentemente usada para se referir à memória que é pré-programada na fábrica e não pode ser facilmente alterada pelo usuário final. Mas se o usuário tiver habilidades desejadas e equipamento especializado (por exemplo, um programador), a pessoa pode reprogramar o chip. O nome ROM permaneceu, embora a definição tenha mudado, como uma referência histórica ao propósito original da memória.


Ao aplicar a proteção contra gravação, alguns tipos de ROMs reprogramáveis podem temporariamente se tornar memória somente leitura.


Esses NÃO são TODOS os tipos existentes de memória não volátil, mas a maioria dos populares que você pode ouvir falar apenas por acaso. Hoje em dia, na maioria das placas-mãe, esses chips são feitos com a tecnologia NOR Flash .

Executar no local (XIP)

Execute in Place (XIP) é um método que permite ao processador executar o código diretamente da memória flash sem copiá-lo primeiro para a memória volátil (como a RAM ). Isso é obtido mapeando a memória flash no espaço de endereço do processador, de modo que a execução do código possa ser executada diretamente do flash. Assim, o sistema pode começar a executar o código o mais rápido possível, sem ter que esperar que a RAM seja inicializada primeiro.


Espere... a CPU pode se comunicar com o BOOT ROM via protocolo SPI/Paralelo/etc? Claro que não, é apenas buscar instruções da memória do sistema, as requisições para esta região da memória são redirecionadas para a Intel Direct Media Interface (DMI ) ou AMD Infinity Fabric (IF) /Unified Media Interface (UMI) (predecessora). É o link entre a CPU e o chipset na placa-mãe. Nesse ponto, a decodificação do endereço é realizada por meio de decodificadores localizados no chipset, e os dados do chip são devolvidos ao processador.


Quando o chip é feito de memória flash NOR, que suporta leituras de acesso aleatório, mas não gravações de acesso aleatório, surge um problema. Na medida em que a memória gravável não estiver disponível, todos os cálculos devem ser executados nos registradores do processador. Neste ponto, o código pode ser escrito apenas em linguagem assembly e tende a configurar o ambiente para linguagem de alto nível (tipicamente, para linguagem C ). A razão para isso é que a inicialização da memória se tornou tão complexa que seria difícil escrever puramente em assembly. Como essas linguagens requerem pelo menos um heap e uma pilha, precisamos de memória gravável. Alguns processadores possuem SRAM embutida no próprio chip, mas uma abordagem mais moderna é usar a memória Cache onboard como RAM (CAR) .


Cache como RAM (CAR)

O cache da CPU é uma memória de alta velocidade que armazena uma cópia dos dados e instruções usados com frequência da memória principal. Um cache está localizado mais próximo do processador e organizado em vários níveis (L1, L2, L3, ...), sendo cada nível maior e mais lento que o anterior.


Se os dados estiverem no cache, a CPU pode recuperar os dados solicitados do cache (chamado cache hit ). Quando o cache da CPU não consegue encontrar os dados necessários, isso resulta em uma falta de cache . Isso pode ocorrer porque os dados nunca foram armazenados no cache ou porque os dados foram armazenados anteriormente, mas foram removidos do cache. De qualquer forma, o processador precisa ir até a memória principal para acessar os dados e copiá-los para o cache.


A remoção de cache é o processo de remoção de dados do cache para liberar espaço para novos dados. A remoção de dados pode ser iniciada pelo sistema de cache (normalmente quando um cache está cheio e novos dados precisam ser armazenados, ou quando a política de tempo de vida dos dados expirou) ou por solicitação explícita.


No entanto, se quisermos usar o cache da CPU como RAM , precisamos configurar o cache para operar no modo Non-Eviction , também chamado de modo No-Fill . Essa técnica evita o despejo devido a uma falta de cache . Em vez disso, o cache é tratado como uma SRAM normal e todos os acessos (leitura/gravação) atingirão o cache e não a memória principal. O modo pode ser ativado usando instruções de CPU específicas do fornecedor.

Layout e mapeamento de memória

Na realidade, o BOOT ROM contém vários tipos de firmware. Uma vez que um monte de firmware é armazenado no BOOT ROM, ele precisa ser organizado de alguma forma para distinguir entre eles. Vamos descobrir como isso é feito.

Modo não descritor

Originalmente, o chipset faz um mapeamento direto de todo o conteúdo do BOOT ROM para a memória (de 4 GB para 4 GB - 16 MB). Normalmente, se o BOOT ROM for menor que 16 MB, o conteúdo será mapeado repetidamente. A CPU e o firmware podem ler/gravar no flash sem nenhuma restrição.





O modo não descritor não é mais suportado em novos chipsets.

Intel Flash Descriptor/Modo descritor

Eventualmente, no ICH8, a Intel apresenta um layout especial para BOOT ROM. O flash é dividido nas seguintes regiões:


  • Flash Descriptor (FD) - esta estrutura de dados deve estar localizada no início do dispositivo com deslocamento 0x10 . É composto por onze seções, conforme mostrado na figura abaixo:



O Descritor MAP tem ponteiros para as outras regiões e o tamanho de cada uma também.


A seção Componente contém informações sobre o(s) flash(s) no sistema (número de componentes, a densidade de cada um, instruções inválidas e assim por diante).


A seção Mestres define as permissões de leitura/gravação para as regiões. No que diz respeito à leitura/gravação, as permissões devem ser definidas como Read-Only, as informações armazenadas nesta região só podem ser gravadas durante o processo de fabricação.


  • BIOS - apenas esta região é mapeada na memória.





  • Intel Converged Security and Management Engine (CSME / ME) - o firmware para suportar diferentes tecnologias Intel e ME.
  • Gigabit Ethernet (GbE) - só pode ser acessado diretamente pelo controlador Gigabit Ethernet.
  • Dados da plataforma
  • Controlador Embutido (EC)


O Flash Descriptor e o Intel ME são as únicas regiões necessárias.

Tabela de interface de firmware Intel (FIT)

O FIT é uma estrutura de dados dentro da região do BIOS e contém várias entradas que descrevem a configuração da plataforma. Cada entrada na tabela tem 16 bytes de tamanho. O primeiro é chamado de cabeçalho FIT , o outro é chamado de entrada FIT . Ele está localizado por um ponteiro FIT em um endereço físico 0xFFFFFFC0 (4GB - 0x40).


Esses componentes devem ser processados antes de executar a primeira instrução da CPU do vetor de reinicialização . As entradas incluem atualizações de microcódigo da CPU, ACM de inicialização, políticas de inicialização/TPM/BIOS/TXT da plataforma e outras coisas. Mas pelo menos o FIT deve incluir as entradas FIT Header e Microcode Update . Portanto, o uso comum do FIT é atualizar o microcódigo antes de executar o vetor de redefinição .


Aqui está a aparência do mapa de memória:






Estrutura de Firmware Incorporado AMD

Infelizmente, há muito menos informações, não consegui encontrar nenhuma documentação vazada do chipset AMD com detalhes sobre seu layout. Portanto, não posso dizer melhor do que a documentação do coreboot diz . Ele foi escrito com base na documentação da AMD , disponível apenas sob NDA.


Na verdade, será suficiente saber que o análogo AMD do Flash Descriptor é uma estrutura de firmware incorporada e contém ponteiros para a tabela de diretórios do PSP , tabela de diretórios do BIOS e outros firmwares.


Inicialização de Silício

Se você deseja dar uma olhada em como exatamente a memória moderna e a CPU são inicializadas, tenho que incomodá-lo. A Intel e a AMD não têm pressa em liberar o Código de Inicialização do Silício para a comunidade. Na medida em que tais informações não estejam disponíveis publicamente, eles oferecem distribuição binária do código de inicialização de silício necessário. Isso deve ser considerado uma biblioteca para desenvolvedores de firmware e contém código binário para inicializar o controlador de memória, o chipset, a CPU e outras partes diferentes do sistema.

Pacote de suporte de firmware Intel (FSP)

Esse binário pode ser dividido em 4 componentes:


  • FSP-T: Configurando o ambiente de execução antecipada ("RAM Temporária") no qual o código C pode ser executado. Na prática, esse binário configura o CAR, mas também faz algumas inicializações iniciais de hardware, como configurar o espaço de configuração mapeado pela memória PCIe.
  • FSP-M: inicializando a memória permanente (como DRAM).
  • FSP-S: Concluindo a inicialização do silício, incluindo a inicialização da CPU e do controlador IO.
  • FSP-O: Componente opcional que fornece inicialização de dispositivos OEM.


Aqui está um repositório de binários Intel FSP publicados pela Intel que você pode encontrar em seu GitHub. A especificação FSP v2.1 pode ser obtida no site da Intel.

AMD Generic Encapsulated Software Architecture (AGESA)

AGESA para produtos anteriores à Família 17h é conhecido como v5 ou Arch2008 . Naquela época, o AGESA era de código aberto e o código estava disponível no repositório coreboot (foi obsoleto após a versão 4.18). A especificação do Arch2008 pode ser encontrada no site da AMD.


Com a introdução dos produtos da Família 17h (microarquitetura Zen), a AMD não publicou o código-fonte AGESA, apenas soluções binárias pré-construídas. Tal sucessor é chamado AGESA v9 e suporta Família 17h e posterior.

openSIL

Não há informações detalhadas disponíveis, apenas notícias .

Subsistemas Autônomos

Uma parte integrante do moderno processo de inicialização x86, sem a qual os núcleos x86 nunca seriam ativados. Portanto, é impossível desativá-los completamente . Essas tecnologias são responsáveis pela inicialização do hardware, verificação da integridade do sistema, gerenciamento de energia e inicialização da CPU. O firmware desses subsistemas é carregado e executado antes que o processador principal comece a executar seu próprio firmware. Um código nesses sistemas é executado independentemente dos núcleos da CPU da plataforma.


Enquanto muitas empresas de hardware incorporaram o princípio de segurança por meio da obscuridade , nem o código-fonte nem a documentação para esses subsistemas estão disponíveis. Felizmente, sabemos como isso afeta o processo de inicialização - consulte Sequências de energia de hardware .


Não entraremos em detalhes, pois já existem extensos artigos na Internet de pesquisadores de todo o mundo. Mas vou apenas dar-lhe uma breve descrição do que é.

Mecanismo de gerenciamento Intel (ME)

O Intel ME é um microprocessador i486/80486 separado integrado ao chipset Intel (PCH) desde 2008. Ele possui sua própria RAM, ROM integrada, pontes de barramento para todos os barramentos dentro do chipset (como resultado, ele pode acessar a rede e até mesmo a RAM principal da CPU), e assim por diante. Executa um sistema operacional personalizado baseado no MINIX.

Processador de segurança de plataforma AMD (PSP)

O AMD PSP é um núcleo ARM baseado na extensão Trustzone, que é inserida na matriz da CPU como um coprocessador. Este chip foi integrado à maioria das plataformas AMD desde 2013. Executa um sistema operacional não documentado e proprietário.

Sequências de energia de hardware

Este processo, também conhecido como Power On Sequence ou Power Sequencing , fornece vários níveis de tensão derivados e/ou trilhos de alimentação em uma ordem específica necessária na plataforma. Em termos mais simples, ele ativa vários componentes da plataforma em uma ordem específica. O processo varia dependendo do design do sistema ou da plataforma, mas normalmente um PC padrão inclui as seguintes etapas:


  • Você pressiona o botão de energia . Mas espere... este botão está no gabinete do computador e não é uma parte necessária de um computador. Normalmente, o botão liga/desliga é um cabo. Temos um botão de um lado e um interruptor que colocamos em dois pinos de metal na placa-mãe do outro lado. Quando pressionamos o botão, esses pinos são conectados para que a eletricidade possa passar por eles. Assista ao vídeo abaixo sobre como ligar um computador sem um botão liga/desliga, se estiver interessado.


  • A placa-mãe envia um sinal para a fonte de alimentação (PSU).


  • A fonte de alimentação recebe o sinal, fornece a quantidade adequada de eletricidade e envia um sinal de volta para a placa-mãe.
  • Depois que a placa-mãe recebe o sinal de energia boa, ela liga os componentes da plataforma, como núcleo, relógios, chipset, memória, controladores diferentes e assim por diante.
  • Uma variedade de subsistemas, incluindo subsistemas autônomos (discutidos acima), podem iniciar antes do processador principal.


  • Sistemas baseados em AMD (para Família 17h e posterior)


    • PSP executa em -chip BOOT ROM.

    • O PSP localiza a Embedded Firmware Table na ROM de BOOT fora do chip e executa o firmware do PSP.

    • O PSP analisa a tabela de diretórios do PSP para encontrar os estágios ABL e os executa.

    • Os estágios ABL inicializam a memória principal, localizam a imagem do BIOS na ROM de BOOT e a carregam na DRAM (descompacta se a imagem for compactada).


      Esta plataforma não tem motivos para usar o CAR porque a DRAM já está disponível e o PSP carrega a imagem do firmware nela.


  • Sistemas baseados em Intel
    • O chipset (ICH/PCH) encontra o Intel Flash Descriptor na ROM de BOOT.
    • O chipset copia o firmware CSME para a memória interna onde o Intel ME pode acessá-lo e o último começa a executá-lo.
    • O chipset mapeia a região do BIOS para a memória.
    • As atualizações de microcódigo localizadas na Tabela de Interface de Firmware são carregadas na CPU. Eles devem ser aplicados a cada inicialização do sistema .
    • (opcional) Se módulos de código autenticados (ACM) forem encontrados, essa entrada será executada.


  • Durante todo esse tempo, o sinal de reinicialização da CPU é ativado para evitar que a CPU inicie antes que outras partes do sistema estejam prontas. Quando a plataforma estiver pronta, a linha de reinicialização da CPU será desativada. Em um sistema com vários processadores ou vários núcleos, uma CPU é escolhida dinamicamente para ser o processador de inicialização (BSP) que executa todo o código de inicialização do firmware. Os processadores restantes, chamados de processadores de aplicativos (AP) neste ponto, permanecem parados até mais tarde, quando são explicitamente ativados pelo firmware/kernel.


  • Depois que a CPU é ligada pela primeira vez, ela opera em modo real . A maioria dos registradores tem valores bem definidos , incluindo o Instruction Pointer (IP), o Code Segment (CS) e o Descriptor Cache , que é uma cópia de cada descritor de segmento dentro do processador para permitir acesso rápido à memória do segmento.


    O Segment Descriptor é uma entrada na Global Descriptor Table (GDT) e contém o endereço base, limite de segmento e informações de acesso (essa parte é ignorada porque o modo real não possui controle de acesso como o modo protegido). Em vez de acessar o GDT (que está localizado na memória) a cada acesso à memória, as informações são armazenadas em um cache descritor.


    No entanto, o GDT não está envolvido no modo real, portanto, o processador gera entradas internamente. O registrador seletor CS, utilizado para acessar o descritor de segmento, é carregado com 0xF000 . O endereço base CS é inicializado em 0xFFFF_0000 . IP é inicializado para 0xFFF0 .


    Portanto, o processador começa a buscar instruções na memória localizada no endereço físico 0xFFFF_FFF0 ( 0xFFFF_0000 + 0x0000_FFF0 ). A primeira instrução executada naquele endereço é chamada de vetor de reinicialização .


    NOTA: Este truque dá acesso ao espaço de endereço alto, no entanto, você não pode acessar o código abaixo do endereço 0xFFFF_0000 . O endereço base CS permanece neste valor inicial até que o registro do seletor CS seja carregado pelo firmware. Isso pode ser feito executando um salto distante .


    Neste ponto, a melhor decisão é mudar para o modo protegido com 4 GB de endereçamento. Se o firmware não fizer isso, para que o modo real funcione, o chipset deve ser capaz de alias um intervalo de memória abaixo de 1 MB para um intervalo equivalente logo abaixo de 4 GB. Certos chipsets não possuem esse aliasing e podem exigir uma mudança para outro modo de operação antes que o primeiro salto longo seja executado.


  • O endereço está em uma seção de memória não volátil , então a CPU usa o método Execute In Place (XIP). Embora se for um sistema baseado em AMD, você provavelmente está lendo da memória principal.


  • A CPU executa um código de firmware.


Recomendo que você assista ao vídeo abaixo sobre a sequência de inicialização, que explica o processo usando a placa-mãe ASUS P9X79 como exemplo. Apesar de estar no idioma russo, você conseguirá entender tudo se ativar as legendas em inglês geradas automaticamente.




Este artigo forneceu muitas informações teóricas relacionadas a como a inicialização funciona. No entanto, para entender verdadeiramente esse processo, precisamos examinar mais de perto o código-fonte e a arquitetura do firmware existente.


No próximo artigo, vamos nos aprofundar no BIOS , UEFI e coreboot para examiná-los em detalhes.

Recursos