Tenho uma dúvida sobre como o BIOS é carregado e executado. Eu sei que a CPU começa a executar o BIOS em 0xFFFF0, mas como está o BIOS aí?
O BIOS está conectado a esse endereço na CPU, onde você não pode sobrescrevê-lo,
Ou é copiado para esse endereço de memória, como se fosse apenas uma memória normal na qual você pode ler/gravar.
Em caso afirmativo, como ele é copiado para esse endereço se não existe um programa?
Contexto : Estou escrevendo um emulador para uma CPU customizada e quero saber como o BIOS é armazenado e carregado na memória.
As especificidades, é claro, dependeriam do design do sistema, mas, por exemplo, o IBM PC (que tinha o 8088 mais barato, mas funcionalmente semelhante) tinha um chip ROM de 8kb . Isso foi conectado aos endereços de memória superiores, como você sugeriu. Portanto, na reinicialização, a CPU leria diretamente da ROM. Escrever no espaço de memória ROM obviamente não teria efeito no conteúdo da ROM.
Para responder sua pergunta sobre a emulação, carregue o BIOS desejado no endereço onde ele precisa estar (0x100000 menos o tamanho do seu BIOS) antes de iniciar sua CPU emulada em 0xFFFF0.
Fundo:
O 8086 (e compatível) inicia a execução no endereço 0xFFFF0. Segue-se que já deve haver uma instrução significativa (geralmente um salto para o início do BIOS). O BIOS não faz parte da CPU, porque o BIOS é específico do sistema. Se houvesse um BIOS na CPU, a CPU seria utilizável apenas naquele sistema.
Portanto, fornecer instruções para o BIOS está fora do escopo da CPU, o sistema deve fornecer essas instruções à CPU, antes que qualquer inicialização pela CPU possa ser feita.
Uma solução comum naquela época era fornecer o BIOS em um chip ROM. O 8086 possui um espaço de endereço de 1 MB ou 0x100000, então 0xFFFF0 está logo abaixo do topo. Quando a CPU quiser ler a memória, ela colocará o endereço desejado no barramento de endereços, e o sistema deverá decidir como reagir a esse endereço com base nos bits superiores do endereço. Para selecionar o chip ROM, você só precisa de um decodificador que detecte que os bits de endereço superiores estão todos definidos como 1, portanto, uma porta AND simples com algumas entradas servirá. Quantos bits são necessários depende do tamanho do BIOS. Um BIOS com 8kB ou 0x2000 iniciará em 0x100000-0x2000=0xFE000, portanto, as 7 principais linhas de endereço são necessárias para decodificar o 0xFE.
O acesso à ROM é bastante lento, então os sistemas posteriores decidiram copiar o conteúdo para a RAM e então executar o BIOS a partir da RAM. mas isso não se aplicava aos primeiros sistemas. O IBM PC original veio com 16kB de RAM, extensível até 64kB. Não fazia sentido desperdiçar 8kB de RAM apenas para executar o BIOS mais rapidamente. Também não fazia sentido desperdiçar os circuitos necessários para implementar isso. Como o seu emulador roda na RAM de qualquer maneira, não faz sentido implementar algo assim, a menos que você faça isso para o benefício de algum software existente que o espera.
Sem complicar a resposta, o BIOS ou firmware de um dispositivo não é armazenado fisicamente no processador. Existe um chip dedicado em um PCB para armazená-lo. O firmware foi projetado para verificar e iniciar todos os testes e processos necessários “conversando” com todos os componentes primários do PCB.
O firmware é iniciado a partir do chip e levado para a memória (RAM) para uso ativo. Os endereços associados em qualquer outro componente do dispositivo são meramente endereços utilizados pelo BIOS para executar/iniciar quaisquer processos necessários durante a inicialização.
Como você editou e especificou mais informações, acho que as seguintes postagens existentes podem ajudá-lo a entender melhor, então não terei que escrever tudo:
https://cs.stackexchange.com/questions/63839/where-does-the-cpu-get-its-first-instructions-from
https://stackoverflow.com/questions/7804724/how-is-the-bios-rom-mapped-into-address-space-on-pc
Nos sistemas 8088/8086, isso normalmente era feito mapeando/conectando fisicamente as linhas A16-A19 para habilitar (por meio das linhas de habilitação/seleção do chip 74138) o decodificador estilo 74138 correto que foi conectado às linhas de habilitação dos chips ROM. Outros 74138s na placa tinham suas linhas de habilitação/seleção de chip conectadas de maneira diferente, de modo que as últimas foram habilitadas quando o endereço da RAM estava no barramento (de endereço). Naquela época, você também tinha vários chips ROM físicos (menores que 64 KB), por isso um decodificador foi usado: as linhas A13-A15 eram normalmente decodificadas pelo mesmo 74138 para selecionar o chip ROM do conjunto.
Abaixo está um diagrama ( fonte ) de um sistema de memória um tanto completo como esse (o 139 são dois 138 no mesmo pacote). Suspeito que este seja mais um sistema acadêmico do que real: ele usa chips ROM de 4x16Kb (27128) na metade inferior do diagrama, enquanto a metade superior do diagrama é a SRAM (62256 chips). O primeiro IBM PC real usava DRAM para RAM, e é por isso que isso é mais acadêmico do que real.
Isso costumava ser chamado de "shadow RAM" e não apareceu até a era de 386 , eu acho. Como isso foi feito (em uma implementação):
O resto da descrição é um pouco confuso [para mim], mas essencialmente parece que o(s) controlador(es) de memória daquela época redirecionava o endereço da ROM grava na RAM, naquele modo inicial de inicialização/cópia. E então o BIOS mudou o controlador para o "modo sombra", a partir do qual as leituras para esses endereços também foram servidas da RAM (após a conclusão da cópia). Aparentemente, no último modo/"sombra", as gravações na ROM endereçadas foram desabilitadas/ignoradas [no nível do controlador de memória], portanto, a cópia RAM do BIOS não pôde ser alterada posteriormente. Isso provavelmente dependia bastante do controlador de memória.
O bit que controlava a inicialização versus o modo posterior/"sombra" estava em algum lugar no espaço de E/S, por exemplo, naquele controlador específico, após fazer a autocópia, a rotina do BIOS tinha que fazer:
Para maior clareza, não havia nada realmente específico para o processador '386 na forma como o modo sombra funcionava. Estava tudo no nível do controlador de memória [externo]. Só que esse recurso não existia nos primeiros PCs IBM, até onde eu sei.
O BIOS foi mapeado por hardware nos 8 KB superiores de memória e o processador o leu a partir daí, com todos os estados de espera extras associados exigidos pelos acessos à ROM.
Os PCs 8086 originais não tinham RAM suficiente para copiar a ROM (o original tinha apenas 16 KB de RAM no total) e, mesmo que tivessem, também não havia hardware para remapear a RAM no espaço de endereço ocupado pela ROM.
Lembro-me vagamente que o BIOS tinha cerca de 8 KB (um dos PCs IBM que eu tinha veio com uma lista (!) Do BIOS no final do manual encadernado em anel).
O iMC é exposto como um conjunto de registros no espaço de configuração PCI(e) e/ou registros MSR. Após uma reinicialização, os valores dos registros (por exemplo, os registros SAD) que controlam o buraco padrão (do endereço físico 0xa0000 a 0xfffff, incluído) são padronizados para não recuperar nenhuma transação de memória.
Quando um núcleo executa uma carga para, digamos, 0xffff0, o iMC não o recupera. A carga então chega ao Agente do Sistema que atua como destino padrão (à là decodificação subtrativa como em uma ponte PCI). Se um PCI(e) interno recuperar tal carga, ela será despachada para ele (por exemplo, o framebuffer VGA legado). Nesse caso, nenhum PCI(e) sensato o faria, então o Agente do Sistema envia a carga de memória pelo link DMI.
A jusante, a carga chega ao PCH que, após uma reinicialização, lê os pinos do strapon (possivelmente virtuais) para saber onde a ROM flash do firmware está localizada (normalmente através do barramento SPI ou barramento LPC) e então lê o descritor flash para descobrir a região flash e mapear por padrão a região do BIOS logo no final do limite de 4GiB (onde o buraco PCI está localizado). Além disso, por padrão, o PCH alia a região logo abaixo de 4GiB para 0xe0000-0xfffff.
Portanto, a carga para 0xffff0 é alias para 0xffffff0, recuperada pela ROM e decodificada para o deslocamento apropriado.
Como você pode ver facilmente, tudo isso é padrão.
Conforme você volta no tempo, a arquitetura muda, mas o princípio permanece o mesmo. No 8086 original, provavelmente havia apenas um barramento compartilhado simples e, como todos com conhecimento mínimo de eletrônica digital sabem, é muito fácil mapear dispositivos em endereços fixos em um único barramento compartilhado (foi assim que surgiram os números de porta IO legados).
É importante notar que sua compreensão da inicialização x86 está muito longe da coisa moderna e real. Não apenas os x86s modernos (sem suporte para x86S) inicializam com uma base CS de 0xffff0000, mas desde Haswell, a primeira coisa que uma CPU faz em um INIT é buscar (através de ucode) o FIT e executar os ACMs (que eventualmente retornarão para a inicialização herdada).
Além disso, geralmente é o PCH que acorda primeiro e ativa o pino/fio virtual correto para inicializar a CPU. O firmware PCH responsável por isso é o BUP (Bring UP) e está localizado na mesma flash ROM do firmware da CPU (apenas em uma região diferente). Como a ROM flash está conectada diretamente ao PCH, é fácil imaginar como ela é acessada.
Mesmo antes de o PCH ser ligado, o BMC (servidor), o chip SuperIO (desktop) ou o controlador incorporado (laptop) são ligados e geralmente são ucontrollers (ou seja, eles têm sua ROM interna). Então, na verdade, existem muitos softwares possíveis que são executados antes da primeira instrução x86 ser buscada.
Finalmente, era habitual que o firmware configurasse o iMC/NB para recuperar gravações no intervalo 0xe0000-0xfffff e depois ler e copiar todo esse intervalo para si mesmo. Finalmente, as gravações foram roteadas de volta para o PCH (que, por padrão, não as encaminha através do SPI/LCP) e as leituras foram recuperadas. Isso teve o efeito de copiar o firmware na memória, algo conhecido como sombreamento.
Ele pode ser lido diretamente (alguns circuitos que decodificam os endereços podem determinar qual memória selecionar para leitura, dependendo do intervalo de endereços real) ou pode ser copiado da ROM para a RAM (chamado sombreamento). Não há necessidade de nenhum programa para fazer isso, um circuito de hardware dedicado (e bastante simples) pode simplesmente percorrer o intervalo de endereços e fazer a cópia na inicialização, antes de deixar o processador iniciar.
Você pode querer assistir uma ou mais séries no YouTube de entusiastas (por exemplo, Ben Eater, James Sharman, Jack Oatley) criando microprocessadores da era de 8 bits com componentes discretos. É divertido e também seria muito útil no seu caso para realmente entender como esses circuitos realmente funcionam.
Adendo: como muitas outras respostas e comentários parecem ver o sombreamento de ROM como um procedimento complicado que requer suporte específico do processador e era inconcebível com a tecnologia da era 8086, esta é uma apresentação de hardware muito simples, graças a James Sharman (como mencionado acima): https://www.youtube.com/watch?v=yR9cw7QehCg
Embora eu espere que o vídeo nunca saia do YouTube, para ser consistente com a política SE de respostas independentes, uma descrição rápida: um contador itera sobre o intervalo de endereços real envolvido, endereçando ROM e RAM em sincronia. A ROM está conectada para declarar seus dados, a RAM para lê-los no barramento. À medida que o contador chega ao fim, sua saída de transporte aciona o circuito adicional para desabilitar completamente a ROM, para habilitar a RAM e para iniciar o processo de reinicialização real do processador.