Protected Mode
Protected mode é o modo de CPU que introduz a maquinaria da qual praticamente todo sistema operacional moderno depende: proteção de memória aplicada por hardware em vez de convenção, e múltiplos níveis de privilégio que a CPU verifica ativamente em vez de apenas documentar. Onde o real mode confia igualmente em todo código em execução, o protected mode dá à CPU um lugar (tabelas de descritores) para registrar para que cada região de memória serve e quem tem permissão de tocá-la, e aplica esses registros em todo acesso.
Segmentação
Seção intitulada “Segmentação”O real mode calcula um endereço como segmento × 16 + offset, com o valor de segmento usado diretamente, sem consulta alguma envolvida. O protected mode reinterpreta o conteúdo do registrador de segmento por completo: em vez de um endereço-base deslocável, ele se torna um seletor (um índice em uma tabela de descritores, mais alguns bits baixos registrando o nível de privilégio requisitado), e a CPU consulta a base, o limite e os direitos de acesso reais do segmento na tabela para a qual o seletor aponta. Essa indireção é o que torna a proteção possível: o software pode carregar livremente qualquer valor de seletor, mas a CPU só permite um acesso se os direitos do descritor correspondente permitirem, e apenas dentro da faixa que o limite do descritor especifica.
A Global Descriptor Table
Seção intitulada “A Global Descriptor Table”A GDT contém os descritores para os quais seletores apontam, carregada com a instrução LGDT a partir de uma pequena estrutura fornecendo o tamanho e o endereço da tabela, o mesmo formato limite/base que LIDT usa para a tabela de interrupções. Cada descritor de 8 bytes empacota um endereço-base de 32 bits, um limite de 20 bits (interpretado como bytes ou páginas de 4 KB dependendo de um bit de granularidade), e direitos de acesso incluindo o próprio nível de privilégio do descritor e se ele descreve código ou dados.
struct gdt_entry { uint16_t limit_low; uint16_t base_low; uint8_t base_mid; uint8_t access; uint8_t granularity; // nibble alto: flags; nibble baixo: bits 19-16 do limite uint8_t base_high;} __attribute__((packed));Uma GDT convencionalmente começa com um descritor nulo no índice 0: carregar um seletor nulo é uma forma legítima de marcar um registrador de segmento como não usado, e a CPU gera uma falha se código de fato tentar usá-lo para um acesso, exatamente o comportamento que um placeholder deliberadamente inválido deveria ter. Além da entrada nula, uma GDT mínima de kernel precisa de pelo menos um descritor de código e um de dados para o ring 0, e tipicamente um par correspondente para o ring 3, se execução em user mode for suportada.
Aplicação de privilégio
Seção intitulada “Aplicação de privilégio”Todo seletor de segmento carrega um Requested Privilege Level de dois bits em seus bits baixos, e todo descritor carrega seu próprio Descriptor Privilege Level. A CPU compara esses (junto com o Current Privilege Level, rastreado como parte do segmento de código atualmente carregado) em operações relevantes, recusando um acesso onde o privilégio requisitado é numericamente maior (menos privilegiado) do que o descritor alvo permite. Essa verificação tripla (CPL, DPL, RPL) é o que torna inútil para código do ring 3 simplesmente carregar um seletor de dados do ring 0 e esperar acesso de ring 0: os próprios bits de privilégio do seletor, combinados com os do descritor, ainda bloqueiam isso.
Uma estrutura adicional, o Task State Segment (TSS), contém o stack pointer para o qual a CPU troca automaticamente quando uma interrupção ou exceção elevando privilégio ocorre. Sem ela, uma transição ring 3 → ring 0 não tem stack de kernel definida para onde ir, o que torna um TSS válido um requisito prático para qualquer kernel que suporte código em user mode, mesmo que o propósito original do TSS, troca de tarefas por hardware, tenha pouco uso hoje.
Entrando em protected mode
Seção intitulada “Entrando em protected mode”A transição em si é uma sequência curta e rígida: carregar uma GDT válida com LGDT, definir o bit 0 (PE) de CR0, e realizar um far jump para o seletor de um descritor de código. Esse far jump faz trabalho real: esvazia o pipeline da CPU de quaisquer instruções decodificadas em real mode que ainda estivessem na fila e força uma nova busca sob as regras de decodificação do novo modo.
lgdt [gdt_descriptor]mov eax, cr0or eax, 1mov cr0, eaxjmp CODE_SEG:protected_mode_entryRegistradores de segmento além do de código (DS, ES, SS, e assim por diante) também precisam ser recarregados com seletores válidos de protected mode imediatamente depois do jump: seus valores de real mode não são automaticamente reinterpretados, e usá-los sem recarregar lê qualquer valor de seletor desatualizado que estivesse lá, geralmente falhando na primeira vez que é usado para um acesso.
Notas de implementação
Seção intitulada “Notas de implementação”O protected mode não remove, por si só, o teto de endereçamento de 1 MB associado ao cálculo de endereço de 20 bits do real mode: esse limite é um efeito colateral da linha A20, um recurso de compatibilidade legado que mascara o 21º bit de endereço a menos que explicitamente habilitado, e precisa ser habilitado separadamente (através de um de vários mecanismos históricos, mais comumente o controlador de teclado ou uma porta fast-A20 dedicada) antes que o código consiga endereçar memória acima de 1 MB de forma confiável, independentemente de qual modo de CPU esteja ativo. Esquecer esse passo produz uma classe de bug particularmente confusa: acessos de memória parecem funcionar, mas silenciosamente fazem alias para um endereço 1 MB abaixo do pretendido, já que o bit superior é simplesmente descartado em vez de causar qualquer falha.
Referências
Seção intitulada “Referências”- ^ Intel, Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A, Capítulos 3 e 5: define descritores de segmento, a GDT, e aplicação de nível de privilégio.
Ver também
Seção intitulada “Ver também”- x86 Architecture: a visão geral de modos mais ampla que este artigo expande.
- Long Mode: a transição adicional do protected mode para execução de 64 bits.
- A Task State Segment: a estrutura completa para a qual a breve menção à TSS deste artigo se aprofunda.
- A Linha A20: a peculiaridade de endereçamento por trás do teto de 1 MB observado acima.
- Unreal Mode: um truque para alcançar memória acima desse teto sem realizar a transição completa deste artigo.