O que contém o endereço físico 0 no Linux x86?

12

Não tenho certeza se esta pergunta deve ser feita aqui ou em reverseengineering.stackexchange.com

Citações da wikipedia :

No processador 8086, a tabela de interrupção é chamada IVT (tabela de vetores de interrupção). O IVT sempre reside no mesmo local na memória, variando de 0x0000 a 0x03ff, e consiste em 256 ponteiros distantes no modo real de quatro bytes (256 × 4 = 1024 bytes de memória).

Isto é o que eu encontro no monitor qemu:

(qemu) xp/128xw 0
0000000000000000: 0xf000ff53 0xf000ff53 0xf000e2c3 0xf000ff53
0000000000000010: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000020: 0xf000fea5 0xf000e987 0xf000d62c 0xf000d62c
0000000000000030: 0xf000d62c 0xf000d62c 0xf000ef57 0xf000d62c
0000000000000040: 0xc0005526 0xf000f84d 0xf000f841 0xf000e3fe
0000000000000050: 0xf000e739 0xf000f859 0xf000e82e 0xf000efd2
0000000000000060: 0xf000d648 0xf000e6f2 0xf000fe6e 0xf000ff53
0000000000000070: 0xf000ff53 0xf000ff53 0xf0006aa4 0xc0008930
0000000000000080: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000090: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000a0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000b0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000c0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000d0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000e0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000f0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000100: 0xf000ec59 0xf000ff53 0xf000ff53 0xc0006730
0000000000000110: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000120: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000130: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000140: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000150: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000160: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000170: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000180: 0x00000000 0x00000000 0x00000000 0x00000000
0000000000000190: 0x00000000 0x00000000 0x00000000 0xf000ff53
00000000000001a0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000001b0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000001c0: 0xf000d611 0xf000ec4e 0xf000ec4e 0xf000ec4e
00000000000001d0: 0xf000d61a 0xf000d623 0xf000d608 0xf000ec4e
00000000000001e0: 0xf000ff53 0x00000000 0xf000ff53 0xf000ff53
00000000000001f0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53

Não sei o que fazer com esses valores. Não se parece com uma tabela de descritor de interrupção (a desreferenciação desses valores fornece todos os nulos). Então, o que eu estou realmente vendo aqui?

rodeio
fonte

Respostas:

9

Qualquer que seja o seu firmware, ele contém.

Em um sistema moderno ideal, o processador nunca entra no modo real, como expliquei nesta seção de perguntas e respostas do SU: Em que modo os PCs modernos com chip Intel de 64 bits executam o setor de inicialização? , o primeiro KiB de memória física é tão irrelevante quanto Johan Myréen achou que era outra resposta aqui. Mas muitos firmwares modernos (ainda) têm suporte de compatibilidade , o que significa que

  • eles podem voltar atrás (sim, voltar , já que passaram diretamente do modo irreal para o modo protegido) do modo protegido para o modo real, a fim de executar os softwares de sistema criados para o modo real, como programas de inicialização PC / AT de estilo antigo em MBRs e VBRs; e
  • eles fornecem as antigas APIs de firmware em modo real e configuram todas as estruturas de dados para essas APIs, nas quais os softwares de sistema mencionados anteriormente se baseiam.

Uma dessas estruturas de dados é o IVT de modo real. As APIs antigas de firmware em modo real são baseadas em intinstruções e o IVT em modo real é preenchido pelo firmware como parte de sua inicialização com ponteiros para as várias rotinas de manipulação de firmware para essas instruções.

Os softwares de sistema em modo protegido não precisam das APIs de firmware em modo real e nunca executam o processador em modo real; portanto, o IVT em modo real nos primeiros 1KiB de memória física não é utilizado. (O modo protegido v8086 não endereça o endereço físico 00000000 e para cima, lembre-se. Ele endereça endereços lógicos 00000000 e para cima, que são traduzidos por tabelas de páginas.) Nos sistemas EFI modernos, o firmware entrega um mapa de memória da memória física para o sistema operacional bootstrap, informando quais partes estão reservadas para o firmware para seus próprios fins de API de modo protegido e quais partes o sistema operacional é livre para seguir em frente e usar em seu conjunto de memória física. Em teoria, a primeira página da memória física pode estar na última categoria.

Na prática, em primeiro lugar, os firmwares frequentemente marcam a primeira página da memória física como "código de serviços de inicialização", o que significa que um sistema operacional pode reivindicá-la e seguir em frente e usá-la como parte de seu conjunto de memórias físicas, mas somente após a inicialização. os serviços de tempo do firmware EFI foram desligados pelo sistema operacional e o firmware foi reduzido para fornecer apenas seus serviços de tempo de execução. Um exemplo disso pode ser visto no log do kernel do Linux (com a add_efi_memmapopção) mostrado por Finnbarr P. Murphy:

[0.000000] efi: mem00: tipo = 3, attr = 0xf, intervalo = [0x0000000000000000-0x0000000000001000) (0MB)
que xe decodifica com outro programa de uma forma mais legível por humanos como:

[# 00] Tipo: Atributo EfiBootServicesCode: 0xF
      Phys: 0000000000000000-0000000000000000
      Virt: 0000000000000000-0000000000001000

Na prática, em segundo lugar, o Linux ignora explicitamente esse intervalo de memória física, mesmo que o firmware diga que pode prosseguir e usá-lo. Você encontrará isso nos firmwares EFI e não-EFI, assim que o Linux tiver o mapa de memória física, ele será corrigido ( em uma função chamadatrim_bios_range ), resultando em mensagens de log do kernel, como:

[0.000000] e820: atualização [mem 0x00000000-0x00000fff] utilizável ==> reservado

Isso não é muito para lidar com os firmwares EFI modernos, onde o IVT em modo real não faz parte da API do firmware, como é para lidar com os firmwares PC98 antigos, onde faz parte da API do firmware, mas os firmwares o reportam (via mesma API) que a memória física disponível para ser alegremente substituída pelo sistema operacional.

Portanto, embora em teoria esse intervalo de memória física possa conter código ou dados arbitrários, dependendo das necessidades momentâneas dos alocadores de memória do kernel e da memória virtual paginada por demanda; na prática, o Linux apenas o deixa intocado conforme o firmware originalmente o configurava.

E no seu sistema, o firmware o preenchia com entradas IVT em modo real. As entradas IVT em modo real são apenas 16:16, é claro, e se você olhar sua memória usando um hexdump de 2 bytes, poderá ver isso claramente. Alguns exemplos:

  • A maioria das entradas do seu IVT aponta para F000: FF53, um endereço na área ROM do firmware em modo real. Provavelmente é uma rotina fictícia que nada mais faz do que uma iret.
  • A entrada 1E da IVT aponta para F000: 6AA4, uma tabela na mesma área de ROM.
  • A entrada 1F da IVT aponta para C000: 8930, uma tabela na área de firmware da ROM de vídeo em modo real.
  • A entrada da IVT 43 aponta para C000: 6730, outra tabela na área de firmware da ROM de vídeo em modo real.

Leitura adicional

JdeBP
fonte
Não, quero dizer o que escrevi. Manual do desenvolvedor de software de arquitetura Intel, volume 3, capítulo 20, § 2.
JdeBP
Bem, você tem agora, porque é; como a primeira frase dessa seção explica. Suspeito disso que o não reconhecimento da abreviação comum "v8086" seja uma espécie de besteira. (-:
JdeBP
Você precisa aprender a ler substantivos atribuíveis. Ou então aprenda a viver sem sopa de cogumelos.
precisa saber é
7

A arquitetura original do processador 8086 (implementada como Modo Real nos processadores 80286+) não tem relevância para o Linux, que opera no Modo Protegido. Não há tabela de vetor de interrupção no endereço físico 0; em vez disso, é usada uma tabela de descritor de interrupção que contém descritores de interrupção. O IDT pode estar localizado em qualquer lugar da memória.

O kernel do Linux obtém um mapa de memória física do firmware (BIOS ou EFI), que informa quais quadros de página de memória física são utilizáveis ​​e quais são reservados ou ausentes. O intervalo de quadros de página utilizáveis ​​não é contíguo, mas normalmente possui enormes buracos. Tradicionalmente, o kernel do Linux x86 pulou o início da memória física, mesmo que esteja marcada como utilizável. Portanto, o endereço físico 0 não é usado pelo kernel do Linux.

Johan Myréen
fonte
Isso faz sentido. Alguma idéia de qual é o conteúdo restante dessa página não usada?
Rodrigo10
Pesquisando no Google 53 ffrevela que essa é provavelmente uma tabela de vetores de interrupção no Modo Real 8086 configurada pelo firmware ou pelo carregador de inicialização.
Johan Myréen
4

Dumping memory

Aqui está uma maneira alternativa de despejar o conteúdo da memória dentro do sistema versus fazer isso externamente:

$ head /dev/mem | hexdump -C
00000000  53 ff 00 f0 53 ff 00 f0  53 ff 00 f0 53 ff 00 f0  |S...S...S...S...|
00000010  53 ff 00 f0 53 ff 00 f0  cc e9 00 f0 53 ff 00 f0  |S...S.......S...|
00000020  a5 fe 00 f0 87 e9 00 f0  53 ff 00 f0 46 e7 00 f0  |........S...F...|
00000030  46 e7 00 f0 46 e7 00 f0  57 ef 00 f0 53 ff 00 f0  |F...F...W...S...|
00000040  22 00 00 c0 4d f8 00 f0  41 f8 00 f0 fe e3 00 f0  |"...M...A.......|
00000050  39 e7 00 f0 59 f8 00 f0  2e e8 00 f0 d4 ef 00 f0  |9...Y...........|
00000060  a4 f0 00 f0 f2 e6 00 f0  6e fe 00 f0 53 ff 00 f0  |........n...S...|
00000070  ed ef 00 f0 53 ff 00 f0  c7 ef 00 f0 ed 57 00 c0  |....S........W..|
00000080  53 ff 00 f0 53 ff 00 f0  53 ff 00 f0 53 ff 00 f0  |S...S...S...S...|
...
...
000afea0  00 00 00 00 00 00 00 00  aa aa aa 00 aa aa aa 00  |................|
000afeb0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000b0000  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
000c0000  55 aa 40 e9 62 0a 00 00  00 00 00 00 00 00 00 00  |[email protected]...........|
000c0010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 49 42  |..............IB|

Análise

A parte superior acima de 000c0000 pode estar relacionada ao carregador de inicialização. Por que eu suspeitaria disso? O código 55aah no local 000c0000normalmente pode ser uma marca na memória de coisas como um gatilho para o BIOS executar um carregador de inicialização secundário.

Referência: Assinatura de Inicialização - BIOS

  ss # 1

No entanto, dado que esse 55aah ocorre na faixa de c0000h-effffh, é mais provável que essa parte seja o PNP Expansion Header:

Referência: especificação de inicialização do BIOS

3.3 Dispositivos com cabeçalhos de expansão PnP

Todos os dispositivos IPL com ROMs de opção devem conter um cabeçalho de ROM de opção válido que resida entre os endereços de memória do sistema C0000h e EFFFFh em um limite de 2k e começa com 55AAh. A inicialização de um dispositivo só pode ser controlada se ele tiver um cabeçalho de expansão PnP. O cabeçalho de expansão, cujo endereço reside no cabeçalho ROM da opção padrão no deslocamento + 1Ah, contém informações importantes usadas para configurar o dispositivo. Ele também contém indicadores para codificar na opção ROM do dispositivo (BCV ou BEV) que o BIOS chamará para inicializar a partir do dispositivo. Veja o Apêndice A para a estrutura do cabeçalho de expansão PnP. Há duas maneiras de inicializar um dispositivo IPL com um cabeçalho de expansão PnP. Ele deve conter um BCV ou um BEV.

53ff ...

Quanto aos dados 53ffh que estão no início. Não está claro para mim o que realmente é. Pesquisando mais, é provável que algo que o kernel do Linux tenha escrito lá depois que o MBR do BIOS foi transferido para o kernel do Linux para inicializar.

Normalmente, o carregador de inicialização carrega o kernel na memória e depois pula para o kernel. O kernel poderá recuperar a memória usada pelo carregador de inicialização (porque ele já executou seu trabalho). No entanto, é possível incluir o código do SO no setor de inicialização e mantê-lo residente após o início do SO

Indo mais longe, consegui encontrar este parágrafo em um trabalho de pesquisa intitulado: Injeção de código malicioso via / dev / mem :

1 O dispositivo mem

/ dev / mem é a interface do driver para a memória fisicamente endereçável. A intenção original de mem e kmem era auxiliar na depuração do kernel. Podemos usar o dispositivo como um dispositivo de caractere comum, usando lseek () para selecionar um deslocamento de endereço. O dispositivo kmem é semelhante, mas fornece uma imagem da memória do kernel no contexto do endereçamento virtual. O servidor Xorg utiliza o dispositivo mem para acessar a memória de vídeo VESA, bem como a IVT (BIOS ROM Interrupt Vector Table) localizada no endereço físico 0x00000000 para manipular os modos de vídeo no modo VM86. O DOSEMU também usa isso para acessar o BIOS IVT para poder interromper o BIOS para várias tarefas (leituras de disco, impressão no console etc.).

Referências

slm
fonte