Pular para o conteúdo

Unreal Mode

Unreal mode é uma técnica para alcançar memória acima do teto de 1 MB que o real mode normalmente impõe, sem realizar a transição completa para o protected mode que de outra forma seria exigida para isso. Ela explora um detalhe de como a CPU de fato implementa carregamento de segmento em vez de qualquer modo oficialmente documentado próprio, comum em bootloaders escritos à mão que precisam tocar memória além de 1 MB brevemente (copiar uma imagem de kernel para seu endereço final de carregamento, por exemplo) antes de realizar a transição real e completa mais tarde, no estágio de boot em que essa transição normalmente acontece.

O que de fato acontece num carregamento de segmento

Seção intitulada “O que de fato acontece num carregamento de segmento”

Todo carregamento de registrador de segmento, em qualquer modo, faz a CPU ler o descritor correspondente de qualquer tabela atualmente ativa e armazenar em cache internamente a base, o limite, e os direitos de acesso desse descritor; só essa cópia em cache, não uma nova consulta à tabela, é consultada em todo acesso de memória subsequente usando esse segmento, motivo pelo qual o próprio cálculo segmento × 16 + offset do real mode coexiste com esse mecanismo de cache sem normalmente ser visível como duas coisas separadas.

; ainda em real mode o tempo todo: o bit PE nunca é tocado
lgdt [gdt_descriptor] ; carrega uma GDT contendo um descritor de dados plano de 32 bits
mov eax, cr0
or al, 1 ; define PE brevemente
mov cr0, eax
mov bx, FLAT_DATA_SEL ; carrega o descritor de 32 bits num registrador de segmento
mov ds, bx ; -- o limite de 4 GB do descritor agora está em cache na "shadow" de DS
and al, 0xFE ; limpa PE de novo, de volta ao real mode
mov cr0, eax

Carregar um registrador de segmento com um seletor enquanto brevemente em protected mode coloca em cache a base completa de 32 bits e o limite de 4 GB desse seletor, vindos de seu descritor da GDT, na porção oculta do registrador de segmento; limpar CR0.PE de novo depois faz a CPU voltar a interpretar esse mesmo registrador de segmento como um valor de segmento de real mode comum para fins de cálculo de endereço, mas não recarrega nem reseta por si só a informação de descritor em cache sentada por trás dele.

O efeito: um segmento de real mode com um limite de protected mode

Seção intitulada “O efeito: um segmento de real mode com um limite de protected mode”

A consequência é um registrador de segmento que ainda se comporta como real mode para cálculo de endereço (segmento × 16 + offset), enquanto o hardware de acesso de memória da CPU continua impondo o limite de 4 GB em cache do carregamento anterior em protected mode em vez do limite usual de 64 KB de segmento do real mode, permitindo que o código calcule endereços da forma comum do real mode enquanto ainda consegue ler e escrever bem além do que o real mode sozinho algum dia permitiria. Essa é exatamente a brecha da qual o unreal mode depende: nada sobre entrar ou sair do protected mode em si é o que concede acesso à memória acima de 1 MB; é especificamente o limite desatualizado em cache deixado num registrador de segmento que foi brevemente carregado enquanto o protected mode estava ativo, um efeito colateral de como a CPU armazena estado de descritor em cache, não uma extensão de real mode documentada e intencional.

Por que isso conta como efeito colateral, não como modo

Seção intitulada “Por que isso conta como efeito colateral, não como modo”

Os próprios manuais de arquitetura da Intel descrevem exatamente dois modos de operação relevantes aqui, real e protected, sem nada intermediário; “unreal mode” nomeia uma consequência específica e reproduzível da implementação de cache de segmento da CPU, não um terceiro modo que o próprio processador reconhece ou documenta como um recurso deliberado. Nada garante que esse comportamento de cache é preservado identicamente em toda implementação ou em toda geração futura de CPU da forma que o comportamento de um modo oficialmente especificado seria, o que é parte do motivo pelo qual código que depende disso geralmente é tratado como usando um truque bem conhecido mas não oficial, útil e amplamente confiável na prática em hardware x86 real e em essencialmente todo emulador, mas não algo que a própria arquitetura formalmente se compromete a suportar.

A utilidade da técnica é limitada especificamente a segmentos de dados (DS, ES, FS, GS, SS): CS não pode receber o mesmo tratamento para execução de código, já que busca e decodificação de instrução em real mode ainda seguem as próprias regras de 16 bits do real mode independentemente de qualquer limite em cache sentado por trás de CS, significando que unreal mode estende a faixa de dados endereçável sem estender o código que uma CPU rodando em real mode consegue de fato executar além de codificação de 16 bits. Como o estado em cache do unreal mode vive na porção oculta e de outra forma invisível de um registrador de segmento, qualquer recarregamento subsequente de segmento a partir de um valor de real mode (um tratador de interrupção que recarrega DS a partir de um valor fixo de segmento de real mode, por exemplo) descarta silenciosamente o limite de 4 GB em cache e reverte esse registrador para um segmento de real mode comum de 64 KB, uma fonte comum de código que funciona até alguma interrupção ou chamada de sub-rotina não relacionada sobrescrever exatamente o registrador de segmento do qual o truque dependia.

  1. ^ Intel, Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A, Capítulo 3.4.3: descreve o cache de descritor de segmento (a porção “oculta” de um registrador de segmento) do qual essa técnica depende.
  • Protected Mode: a transição completa que esse truque evita realizar, e as tabelas de descritor das quais seus próprios carregamentos de segmento leem.
  • The Boot Process: o estágio de boot, dentro de um bootloader escrito à mão, onde essa técnica tipicamente aparece.