Model-Specific Registers
Um model-specific register é um registrador de controle que, diferente de EAX ou CR0, não tem instrução dedicada nem codificação fixa própria; em vez disso, todo o espaço de MSRs é alcançado através de exatamente duas instruções de propósito geral, RDMSR e WRMSR, que selecionam qual registrador tocar por número em vez de por nome. O EFER de Long Mode e os registradores STAR/LSTAR/FMASK que System Calls configura para o caminho rápido de syscall são ambos MSRs, e ambos os artigos os usam sem cobrir o mecanismo em si, o que este artigo preenche.
Por que um mecanismo de acesso separado
Seção intitulada “Por que um mecanismo de acesso separado”Registradores comuns são endereçados diretamente na codificação de uma instrução, o que só escala até um conjunto pequeno e fixo: x86-64 tem dezesseis registradores de propósito geral, e adicionar um décimo sétimo significaria alargar toda instrução que pudesse referenciar um deles. MSRs contornam isso por completo movendo o seletor para dados em vez de codificação: ECX contém um índice de 32 bits escolhido no momento da chamada, então a mesma codificação de duas instruções alcança milhares de registradores possíveis sem que nenhum deles precise de espaço de opcode dedicado, ao custo de um load extra de registrador antes de todo acesso que um registrador codificado diretamente não precisaria.
mov ecx, 0xC0000080 ; índice de MSR: IA32_EFERrdmsr ; resultado: EDX:EAX = valor do MSR (64 bits, dividido entre dois registradores de 32 bits)
or eax, 1 << 8 ; define LMEwrmsr ; escreve EDX:EAX de volta no mesmo MSR selecionado por ECXRDMSR e WRMSR ambos operam sobre um valor de 64 bits apesar de EAX e EDX terem apenas 32 bits de largura cada: a metade baixa do valor vai em EAX, a metade alta em EDX, uma divisão anterior à própria existência de registradores de propósito geral de 64 bits e que permaneceu fixa por compatibilidade mesmo agora que um único registrador de 64 bits poderia conter o valor inteiro diretamente.
Por núcleo, não compartilhado
Seção intitulada “Por núcleo, não compartilhado”Cada núcleo de CPU tem sua própria cópia independente de todo o espaço de MSR; escrever um MSR num núcleo não tem efeito algum sobre a cópia desse mesmo índice de MSR em qualquer outro núcleo. Essa é uma consequência real e fácil de não notar para qualquer coisa configurada uma vez durante o boot no processador bootstrap: EFER, os registradores de configuração de syscall, e FS_BASE/GS_BASE todos precisam ser escritos de novo em todo núcleo adicional conforme ele é ligado sob SMP, não copiados do núcleo de boot, já que não há estado compartilhado algum entre núcleos aqui de onde copiar em primeiro lugar. FS_BASE e GS_BASE em particular são comumente usados justamente por serem por núcleo: um kernel aponta GS_BASE para uma estrutura de dados por núcleo distinta em cada núcleo, depois a alcança a partir de qualquer caminho de código através de um offset fixo de GS, sem precisar de um mecanismo separado para determinar qual núcleo está executando no momento.
Falhando num MSR não suportado
Seção intitulada “Falhando num MSR não suportado”Diferente do CPUID, que silenciosamente retorna o que quer que a maior leaf suportada forneça quando consultado fora da faixa, RDMSR ou WRMSR contra um índice que a CPU atual não implementa levanta #GP (general protection fault), uma exceção comum que a IDT de um kernel já trata, em vez de qualquer comportamento tratado como caso especial. Essa é uma segunda razão, independente (além de checar bits de recurso do CPUID diretamente), para verificar que um recurso está presente antes de tocar o MSR que o configura: um wrmsr no bit LME de IA32_EFER numa CPU que não suporta long mode algum falha exatamente da mesma forma que um wrmsr num índice completamente inventado e inexistente falharia, sem distinção alguma na falha em si entre “índice errado” e “índice certo, recurso não suportado”.
MSRs comumente usados
Seção intitulada “MSRs comumente usados”| MSR | Índice | Propósito |
|---|---|---|
IA32_EFER | 0xC0000080 | Habilitação/ativação de long mode, habilitação de NX |
IA32_APIC_BASE | 0x1B | Endereço-base físico do APIC local e bit de habilitação |
IA32_FS_BASE | 0xC0000100 | Endereço-base contra o qual endereçamento relativo a FS resolve |
IA32_GS_BASE | 0xC0000101 | Endereço-base contra o qual endereçamento relativo a GS resolve |
IA32_STAR | 0xC0000081 | Seletores de segmento carregados em syscall/sysret |
IA32_LSTAR | 0xC0000082 | Endereço de ponto de entrada para o qual syscall salta |
IA32_FMASK | 0xC0000084 | Bits de EFLAGS limpos na entrada de syscall |
Note a divisão entre as duas faixas de numeração acima: índices abaixo de aproximadamente 0x4000 são arquiteturais, definidos identicamente entre implementações, enquanto a faixa prefixada com 0xC000_0000 contém registradores que a AMD introduziu para x86-64 e a Intel depois adotou por compatibilidade, uma convenção de nomenclatura (prefixo IA32_ independentemente da faixa numérica) que tem mais a ver com história de fabricante do que com qualquer diferença funcional visível a partir do software.
Notas de implementação
Seção intitulada “Notas de implementação”RDMSR e WRMSR são ambas instruções exclusivas de ring 0 sem equivalente algum ao bitmap de permissão de I/O da TSS que port I/O tem para estender acesso ao ring 3; não há mecanismo algum para deixar código desprivilegiado tocar um MSR diretamente, o que é parte do motivo pelo qual dados por núcleo alcançados via GS_BASE acima normalmente são expostos ao user space apenas indiretamente, através de uma chamada de sistema ou de uma página somente leitura preenchida pelo kernel, em vez de deixar o ring 3 ler o MSR em si. Um kernel sondando quais MSRs uma dada CPU de fato implementa não tem instrução direta de enumeração para chamar, diferente da estrutura de leaves do CPUID; a abordagem prática é checar o bit de recurso específico do CPUID associado à funcionalidade de cada MSR (os MSRs de syscall, por exemplo, só têm sentido uma vez que o CPUID já confirmou que a própria instrução é suportada) em vez de sondar índices de MSR diretamente e tratar as falhas resultantes.
Referências
Seção intitulada “Referências”- ^ Intel, Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 4: a referência arquitetural completa de MSRs.
- ^ AMD, AMD64 Architecture Programmer’s Manual, Volume 2, Apêndice A: a listagem de MSRs do lado AMD, incluindo os registradores de syscall
STAR/LSTAR/FMASK.
Ver também
Seção intitulada “Ver também”- Long Mode: o artigo cujo uso do MSR
EFEReste artigo explica o mecanismo geral por trás. - System Calls: os MSRs
STAR/LSTAR/FMASKque configuram o caminho rápido de syscall. - CPUID: a instrução complementar para checar se um recurso é suportado antes de tocar o MSR que o configura.