Por que a primeira instrução BIOS está localizada em 0xFFFFFFF0 ("parte superior" da RAM)?

51

Eu sei que o BIOS carrega sua primeira instrução de 0xFFFFFFF0, mas por que esse endereço específico? Tenho várias perguntas e espero que você possa me ajudar com algumas delas, pelo menos.

Minhas perguntas:

  • Por que a primeira instrução do BIOS está localizada na "parte superior" de uma RAM de 4 GB?
  • O que aconteceria se meu computador tivesse apenas 1 GB de RAM?
  • E os sistemas com mais de 4 GB de RAM (por exemplo, 8 GB, 16 GB, etc.)?
  • Por que a pilha é inicializada com algum valor (nesse caso, um valor localizado em 0xFFFFFFF0)?

Eu li sobre isso esta tarde e ainda não entendi.

Fernando Paladini
fonte
28
Uma pergunta por pergunta, por favor.
Lightness Races com Monica
4
Eu gosto de como a resposta aceita nem sequer menciona os modos de memória segmentada ou de endereçamento, e o único lugar em que a linha A20 é tocada é nos comentários.
ICloud #
Os AVRs da Atmel iniciam a execução a partir do endereço 0, enquanto o Freescale HCS08 inicia a partir de 0xFFFE, iirc. Cada família de processadores tem suas próprias características.
Nick T
2
@imallett Gosto de como você decide reclamar aqui em vez de pedir ao pôster que atualize sua resposta com mais informações. Também gosto de como você acha que esse conhecimento que o OP seria capaz de detectar, embora o objetivo de fazer uma pergunta seja obter conhecimento sobre coisas que agora podemos entender completamente.
MonkeyZeus
2
@MonkeyZeus até o momento, outros nove comentaristas já fizeram isso, e ele ainda não mudou. Meu comentário, embora sarcástico, não foi vazio; é um aviso para futuros internautas, bem como para o OP.
imallett

Respostas:

57

0xFFFFFFF0é onde uma CPU compatível com x86 começa a executar instruções quando é ligada. Esse é um aspecto rígido e imutável (sem hardware extra) da CPU e diferentes tipos de CPUs se comportam de maneira diferente.

Por que a primeira instrução do BIOS está localizada na "parte superior" de uma RAM de 4 GB?

Ele está localizado na "parte superior" do espaço de endereço de 4 GB - e, na inicialização, o BIOS ou a UEFI ROM está configurado para responder às leituras desses endereços.

Minha teoria sobre por que isso é:

Quase tudo na programação funciona melhor com endereços contíguos. O projetista da CPU não sabe o que um construtor de sistema deseja fazer com a CPU; portanto, é uma má idéia para a CPU exigir que endereços smack no meio do espaço sejam necessários para vários propósitos. É melhor manter isso "fora do caminho" na parte superior ou inferior do espaço de endereço. Obviamente, lembre-se de que essa decisão foi tomada quando o 8086 era novo, que não possuía um MMU .

No 8086, vetores de interrupção existiam no local de memória 0 e acima. Os vetores de interrupção precisam estar em endereços conhecidos e se deseja estar na RAM para flexibilidade - ainda não foi possível ao projetista da CPU saber quanto RAM haveria em um sistema. Portanto, começar do zero e fazer o trabalho fazia sentido para eles (porque nenhum sistema em 1978, quando o 8086 foi inventado, teria 4 Gbytes de RAM - esperar que a RAM estivesse em 0xFFFFFFF0 não era uma boa ideia) e a ROM teria que ser no limite superior.

Obviamente, começando com pelo menos o 80286, os vetores de interrupção podem ser movidos para um local de partida diferente de 0, mas as modernas CPUs x86 de 64 bits ainda são inicializadas no modo 8086, para que tudo ainda funcione da maneira antiga para compatibilidade (tão ridículo quanto como parece em 2015 ainda precisar da sua CPU x86 para poder executar o DOS).

Portanto, como os vetores de interrupção começam de 0 e funcionam para cima, a ROM precisaria começar de cima e trabalhar para baixo.

O que aconteceria se meu computador tivesse apenas 1 GB de RAM?

Uma CPU de 32 bits possui 4.294.967.296 endereços, numerados de 0 (0x00000000) a 4294967295 (0xFFFFFFFF). A ROM pode residir em alguns endereços e a RAM pode residir em outros. Com o MMU da CPU, isso pode ser alternado em tempo real. A RAM não precisa viver em todos os endereços.

Com apenas 1 GB de RAM, alguns endereços não respondem nada quando são lidos ou gravados. Isso pode fazer com que dados inválidos sejam lidos quando esses endereços são acessados ​​ou um bloqueio do sistema.

E os sistemas com mais de 4 GB de RAM (por exemplo: 8 GB, 16 GB, etc.)?

Mantendo-o um pouco simples: as CPUs de 64 bits têm mais endereços (que é uma das coisas que as tornam em 64 bits - por exemplo, 0x0000000000000000 a 0xFFFFFFFFFFFFFFFFFF), por exemplo, para que a RAM extra "caiba". Supondo que a CPU esteja no modo longo . Até então, a RAM está lá, apenas não endereçável.

Por que a pilha é inicializada com algum valor (nesse caso, um valor localizado em 0xFFFFFFF0)?

Não consigo encontrar imediatamente nada sobre o que o x86 atribui ao ponteiro da pilha na inicialização, mas acabaria tendo que ser reatribuído por uma rotina de inicialização de qualquer maneira, uma vez que essa rotina descubra quanto RAM há no sistema. (@Eric Towers nos comentários abaixo relata que está definido como zero na inicialização.)

LawrenceC
fonte
7
É melhor pensar no espaço de endereço como um grande espaço no qual as coisas podem ser atribuídas pelo hardware. Quando a CPU lê / grava memória, na verdade realiza uma comunicação por um barramento, e o hardware pode garantir que coisas como RAM ou ROM estejam respondendo em intervalos de endereços específicos. Portanto, esse hardware deve garantir que a ROM responda em 0xFFFFFFF0 quando a CPU for redefinida. Não há obrigação inerente de que a ROM apareça logo após a RAM. Pode aparecer sempre que o hardware indicar, dependendo dos recursos desse hardware.
LawrenceC
4
É possível ter "brechas" ou espaços não atribuídos que não são usados ​​por ROM, RAM ou qualquer outra coisa - normalmente acessá-los causará um bloqueio do sistema.
LawrenceC
16
Esta resposta assume que a CPU pode usar 32 bits de endereço enquanto estiver no modo de 16 bits. Mas no modo de 16 bits, ele pode usar apenas 20 bits de endereço. O endereço 0xFFFFFFF0não está acessível até que a CPU tenha sido alterada para o modo de 32 bits. A última vez que examinei atentamente o código do BIOS, o ponto de entrada estava em 0xFFFF0.
kasperd
6
@ MichaelKjörling seu cálculo está errado. O segmento deslocado e o deslocamento não são ORed, eles são adicionados. Assim, o FFFF lógico: FFF0 é físico (1) 0FFE0 (onde 1 principal está presente se A20 estiver ativado).
Ruslan #
9
@kasperd Existe um hack no lugar - o gerenciador de memória tem os 12 bits mais altos definidos como 1 até o primeiro salto em distância. Então sim, logicamente, você está trabalhando com 0xFFFF0, mas, na realidade, ele mapeia 0xFFFFFFF0. Espero que isso tenha sido feito para compatibilidade com o 8086 - tanto ele quanto as CPUs mais modernas parecem usar 0xFFFF0, mas as CPUs de 32 bits realmente acessam 0xFFFFFFF0(mapeadas para a BIOS ROM).
Luaan
26

Não está localizado na parte superior da RAM; Ele está localizado na ROM, cujo endereço está no topo do espaço de endereço da memória, junto com qualquer memória nas placas de expansão, como os controladores Ethernet. Está lá para que não entre em conflito com a RAM, pelo menos até que você tenha 4 GB instalados. Os sistemas com 4 GB ou mais de RAM podem fazer duas coisas para resolver o conflito. Placas-mãe baratas simplesmente ignoram as partes da RAM que conflitam com a localização da ROM. Os decentes remapearão a RAM para que pareçam ter um endereço acima da marca de 4 GB.

Não tenho certeza do que você está perguntando sobre a pilha. Certamente não foi inicializado para estar em ROM. Quando a CPU é redefinida, ela está inicialmente no "modo real", onde atua exatamente como o 8086 original e usa endereçamento segmentado de 16 bits, permitindo acessar apenas 1 MB de memória. O código do BIOS está localizado na parte superior desse 1 MB. O BIOS escolhe em algum lugar na RAM para configurar a pilha, carregar e executar o primeiro setor da primeira unidade inicializável. Cabe ao sistema operacional alternar para o modo de 32 ou 64 bits, uma vez que assume e configura suas próprias pilhas (uma por tarefa / encadeamento).

psusi
fonte
11
Muito obrigado pela resposta, mas o @LawrenceC fornece mais detalhes sobre a resposta e me ajudou em como tudo funciona. De qualquer forma, obrigado! Dou-lhe um voto positivo: 3
Fernando Paladini
13

Primeiro, isso não tem nada a ver com RAM, realmente. Estamos falando de espaço de endereço aqui - mesmo se você tiver apenas 16 MiB de memória, ainda terá 32 bits de espaço de endereço em uma CPU de 32 bits.

Isso já responde à sua primeira pergunta, realmente - no momento em que foi projetada, os PCs do mundo real não tinham nem perto dos 4 GiB de memória completos; eles estavam mais na faixa de 1 a 16 MiB de memória. O espaço de endereço era, para todos os efeitos, gratuito.

Agora, por que exatamente 0xFFFFFFF0? A CPU não sabe quanto do BIOS existe. Alguns BIOS podem levar apenas alguns kilobytes, enquanto outros podem ocupar megabytes completos de memória - e eu nem estou entrando nas várias RAMs opcionais. A CPU deve estar conectada a algum endereço para iniciar - não há nada para configurar a CPU. Mas este é apenas um mapeamento do espaço de endereço - o endereço é mapeado diretamente no chip da ROM do BIOS (sim, isso significa que você não terá acesso aos 4 GiB de RAM completos neste momento, se você tiver muitos - mas isso não é nada de especial, muitos dispositivos exigem seu próprio intervalo no espaço de endereço). Em uma CPU de 32 bits, esse endereço fornece 16 bytes completos para a inicialização básica - o que é suficiente para configurar seus segmentos e, se necessário, o modo de endereço (lembre-se,"procedimento" de inicialização real . Neste ponto, você não usa RAM - tudo é apenas ROM mapeada. De fato, a RAM ainda não está pronta para ser usada neste momento - esse é um dos trabalhos do BIOS POST! Agora, você pode estar pensando - como um modo real de 16 bits acessa o endereço 0xFFFFFFF0? Claro, existem segmentos, então você tem espaço de endereço de 20 bits, mas isso ainda não é bom o suficiente. Bem, há um truque: os 12 bits altos do endereço são definidos até você executar seu primeiro salto em distância, fornecendo acesso ao espaço de endereços alto (enquanto rejeita o acesso a algo menor que 0xFFF00000 - até você executar um salto em comprimento) .

Tudo isso é oculto principalmente pelos programadores (para não mencionar os usuários) nos sistemas operacionais modernos. Você geralmente não tem acesso a nada tão baixo nível - algumas coisas já estão além do salvamento (você não pode alternar os modos da CPU à toa ou à toa), outras são tratadas exclusivamente pelo kernel do sistema operacional.

Portanto, uma visão mais agradável vem da codificação da velha escola no MS DOS. Outro exemplo típico de memória do dispositivo sendo mapeada diretamente para o espaço de endereçamento é o acesso direto à memória de vídeo. Por exemplo, se você deseja escrever texto rapidamente no visor, você escreveu diretamente no endereço B800:0000(mais deslocamento - no modo de texto 80x25, isso significa que (y * 80 + x) * 2se minha memória me servir corretamente - dois bytes por caractere, linha por linha). Se você queria desenhar pixel por pixel, utilizava o modo gráfico e o endereço inicial de A000:0000(normalmente, 320x200 a 8 bits por pixel). Fazer algo de alto desempenho geralmente significava mergulhar nos manuais do dispositivo, para descobrir como acessá-los diretamente.

Isso sobrevive até hoje - está apenas oculto. No Windows, você pode ver os endereços de memória mapeados para dispositivos no Gerenciador de dispositivos - basta abrir propriedades de algo como sua placa de rede, vá para a guia Recursos - todos os itens do intervalo de memória são mapeamentos da memória do dispositivo para o seu espaço de endereço principal. E em 32 bits, você verá que a maioria desses dispositivos está mapeada acima da marca 2 GiB (mais tarde 3 GiB) - novamente, para minimizar conflitos com a memória utilizável pelo usuário, embora isso não seja realmente um problema com a memória virtual ( os aplicativos não chegam nem perto do espaço real de endereço de hardware - eles têm seu próprio pedaço de memória virtualizado, que pode ser mapeado para RAM, ROM, dispositivos ou arquivo de paginação, por exemplo).

Quanto à pilha, bem, deve ajudar a entender que, por padrão, a pilha cresce a partir do topo. Portanto, se você fizer um push, o novo ponteiro da pilha estará em 0xFFFFFEC- em outras palavras, você não está tentando gravar no endereço de inicialização do BIOS :) O que obviamente significa que as rotinas de inicialização do BIOS podem usar a pilha com segurança antes de remapear novamente em algum lugar mais útil. Na programação da velha escola, antes que a paginação se tornasse o padrão de fato, a pilha geralmente era iniciada no final da RAM e o "estouro de pilha" acontecia quando você começava a substituir a memória do aplicativo. A proteção de memória mudou muito disso, mas, em geral, mantém a compatibilidade com versões anteriores o máximo possível - observe como até a mais moderna CPU x86-64 ainda pode inicializar o MS DOS 5 - ou como o Windows ainda pode executar muitos aplicativos DOS que não têm idéia sobre paginação.

Luaan
fonte
3
Excelente resposta, apenas para expandir e dizer que os processadores modernos estão começando a soltar hacks como o mascaramento da linha A20 , de modo que o suporte a casos extremos mais antigos está acabando.
Basic
2
Até o último parágrafo: O BIOS não pode usar a pilha "livremente": não pode gravar na ROM (para a qual 0xFFFFFFECseria mapeado). Isso significa não apenas não, pushmas, por exemplo, calltambém não . Eles devem esperar até que a RAM esteja pronta.
The Vee
7

Além de outros pontos mencionados, pode ser útil para entender o que um endereço é . Enquanto arquiteturas mais recentes complicam as coisas, historicamente uma máquina produzia em cada ciclo de memória o endereço desejado em 20 a 32 fios (dependendo da arquitetura, com alguns truques especiais para observar se era necessário um par ou um quarteto de bytes simultaneamente); várias partes do sistema de memória examinavam o estado desses fios e se ativavam quando viam certas combinações de valores altos e baixos.

Se uma máquina com 32 fios de endereço precisar apenas usar 1 MB de RAM e 64 KB de ROM [bastante plausível para alguns controladores incorporados], poderá ativar a RAM para todos os endereços em que o fio de endereço superior estava baixo e a ROM para todos os endereços em que estava. Alto. Os 20 fios inferiores do endereço seriam amarrados à RAM para selecionar um dos 1.048.576 bytes e os 16 inferiores também seriam conectados à ROM, para selecionar um dos 65.536 bytes. Os 11 fios restantes do endereço simplesmente não seriam conectados a nada.

Nessa máquina, os acessos aos endereços 0x00100000-0x001FFFFF seriam equivalentes aos acessos aos endereços RAM 0x00000000-0x000FFFFF. Da mesma forma com os endereços 0x000200000-0x0002FFFFF ou 0x7FF00000-0x7FFFFFFFF. Os endereços acima de 0x80000000 liam ROM, com um padrão de 64K repetindo-se por todo o espaço.

Embora o processador possua um espaço de endereço de 4.294.967.296 bytes, não é necessário que o hardware reconheça muitos endereços distintos. Colocar o vetor de redefinição próximo à parte superior do espaço de endereço é um design que funcionará bem, independentemente da quantidade ou quantidade de RAM e ROM que o sistema possui e evita a necessidade de decodificar completamente o espaço de endereço.

supercat
fonte
Ponto positivo - você não encontrará nenhum hardware de 64 bits que suporte algo próximo ao espaço de memória endereçável de 64 bits (ou mesmo 1x10 ^ -12).
Basic
3

Minha teoria é que, porque estamos usando lógica negativa, a digital (1) não possui tensão (O volts). Só precisamos colocar tensão nos últimos 4 bits na inicialização para que o contador de programa (ou ponteiro de instrução) atinja 1111 1111 1111 1111 1111 1111 1111 0000. Não precisamos endereçar os 28 bits superiores, já que a maioria dos CPUs (antigos) era de 16 bits e os nibbles inferiores podem ser endereçados por um único chip de endereço nos velhos tempos. Agora, como temos 64 bits com compatibilidade de 32 bits e 32 a 16 bits, o campo de hardware foi aprimorado, mas o método permanece. Além disso, os bioses nem sempre são programados em 64 ou 32 bits. Minha opinião também é que as memórias nem sempre são as mesmas, o BIOS deve estar localizado no mesmo primeiro segmento. A maneira como vemos as biografias endereçadas não é o endereço real o tempo todo. Apenas um ensinou de mim ...

Agguro
fonte
2

em RESET, um processador compatível com 8088/8086 executa as instruções em 0FFFF0, 16 bytes abaixo do limite de 1 megabyte. normalmente a ROM neste local (em implementações de PC) seria o BIOS; portanto, no final da ROM do BIOS, há um salto para o início da ROM do BIOS.

mostrado aqui: vetor inicial e assinatura de 'data' por trás dele, data do BIOS do IBM 5150 PC 8KB eprom dump: 19/10/1981

00001FEE  FF                db 0xff
00001FEF  FF                db 0xff
00001FF0  EA5BE000F0        jmp word 0xf000:0xe05b
00001FF5  3130              xor [bx+si],si
00001FF7  2F                das
00001FF8  3139              xor [bx+di],di

observe que o endereçamento é de uma ROM de 8KB $ 2000, que coloca o endereço inicial (o JMP distante absoluto, em qualquer outro local, nesse caso, dentro da própria ROM de 8KB, embora não seja o endereço mais baixo possível nessa ROM) em $ FFFF: $ 0 segmentado ou $ FFFF0 linear.

quanto à compatibilidade: se algum processador 'futuro' ou atual 'espera' que ele tenha muito mais Fs na frente do endereço, isso não importa. para compatibilidade de cpus mais recentes em sistemas mais antigos, as linhas de endereço adicionais permanecem desconectadas e, portanto, os dados no databus são exatamente os mesmos. desde que os bits menos significativos permaneçam em FFFF0.

(em um sistema com apenas 1 MB de RAM e a ROM posicionada no final desse RAM, e mais nada, ele pensará 'feliz' que está falando com o endereço mais alto e ainda obterá exatamente os mesmos dados, porque essas implementações nunca ouviram falar linhas de endereço maiores que A19)

observe que o mundo não é apenas 'pcs' ... o ibm pc foi um 'acidente', esses processadores nunca foram projetados especificamente para 'pcs' e envolvem muito mais coisas do que apenas pcs (como satélites, sistemas de armas, etc). Os modos protegido de 32 e 64 bits geralmente não são desejados. (o modo 8086 virtual é muito mais interessante, como um motivo para escolher uma versão mais recente (386 ou mais), por exemplo). portanto, há muito mais na 'compatibilidade com versões anteriores' do que apenas 'será executado'.

HRH Sven Olaf de CyberBunker
fonte
1

A placa-mãe garante que a instrução no vetor de redefinição seja um salto para o local da memória mapeado para o ponto de entrada do BIOS. Esse salto limpa implicitamente o endereço de base oculto presente na inicialização. Todos esses locais de memória têm o conteúdo certo necessário para a CPU, graças ao mapa de memória mantido pelo chipset. Todos eles são mapeados para a memória flash que contém o BIOS, pois, nesse ponto, os módulos de RAM possuem uma porcaria aleatória.

viktorkh
fonte