Em um
artigo anterior (link), falei sobre o conceito básico de um hipervisor baseado na tecnologia de virtualização de hardware Intel. Agora, proponho expandir os recursos do hypervisor adicionando suporte à arquitetura de multiprocessador (SMP) e também considero um exemplo de como o hypervisor pode fazer alterações no sistema operacional convidado.
Todas as ações adicionais serão realizadas no PC com a seguinte configuração:
CPU: Intel Core i7 5820K
Placa-mãe: Asus X99-PRO
Memória RAM: 16GB
SO convidado: Windows 7 x32 com PAE desativado
Começarei descrevendo a localização dos componentes do hypervisor no disco rígido (todos os valores são especificados em setores).
O processo de carregamento de um hypervisor difere da versão anterior apenas na presença de um novo módulo hypervisor.ap , cujo objetivo é a inicialização básica do processador AP.O processo de carregamento de módulos na memória:
Suporte SMPEu implementei um hipervisor no princípio do multiprocessamento simétrico, o que significa que a mesma cópia do VMX será lançada em todos os processadores lógicos presentes. Além disso, as tabelas IDT e GDT, bem como as tabelas para memória de paginação, serão comuns a todos os processadores lógicos. Fiz isso porque o hypervisor inicializará imediatamente a memória para o espaço de endereço do SO convidado e não há necessidade de reatribuir dinamicamente os endereços físicos de páginas individuais. Além disso, com essa abordagem, você não precisa monitorar a correspondência dos caches TLB do processador no lado do hypervisor.
O processo de inicialização do BSP e do AP será diferente. Todas as principais estruturas envolvidas no hypervisor serão criadas durante a inicialização do BSP. Além disso, o estado da atividade para processadores AP do modo não raiz vmx será definido como estado HLT. Assim, o ambiente do SO convidado será emulado de acordo com o que seria sem o uso da virtualização.
Inicializando o BSP:
- Inicialização Spinlock
- Inicializando e carregando tabelas GDT e IDT
- Inicializando tabelas de paginação
- Inicializando estruturas do VMCS e criando uma tabela EPT comum
- Ativação de processadores AP. Para fazer isso, uma sequência de interrupção INIT - SIPI é enviada para cada AP. O vetor para a interrupção do SIPI é 0x20, que corresponde à transferência do controle AP em 0x20000 (módulo hypervisor.ap)
- Iniciando o sistema operacional convidado em 0x7C00 (módulo win7.mbr)
Inicialização AP:
- Depois de ativar o AP, o processador está no modo real. O módulo hypervisor.ap inicializa as tabelas de memória e paginação para alternar para o modo longo
- Faça o download do IDT, GDT e do catálogo de tabelas de paginação criadas durante a fase de inicialização do BSP
- Inicialização de estruturas VMCS e carregamento de tabelas EPT criadas no estágio de inicialização do BSP
- Alternando para o modo não raiz vmx com o estado HLT ativo
Podemos dizer que a implementação do suporte SMP no hypervisor é bastante simples, mas há alguns pontos que eu gostaria de chamar a atenção.
1. Suporte legado USB
Os novos modelos de placas-mãe podem não ter conectores PS / 2; portanto, o Suporte USB herdado é usado para garantir compatibilidade com versões anteriores. Isso significa que você pode trabalhar com um teclado ou mouse USB usando os mesmos métodos (portas de entrada / saída) que o padrão PS / 2. A implementação do USB Legacy Support depende não apenas do modelo da placa-mãe, mas também pode acenar em diferentes versões do firmware. Na minha placa-mãe Asus X99-PRO, o USB Legacy Support é implementado através de interrupções SMI, no processador em que ocorre a emulação PS / 2. Escrevo sobre isso com detalhes, porque, no meu caso (versão de firmware 3801), o Suporte Legado USB não é compatível com o modo longo e, quando retorna do SMM, o processador entra no estado de desligamento.
A solução mais fácil nessa situação é desativar o USB Legacy Support antes de mudar para o modo longo. No entanto, no Windows, o método de pesquisa de teclado PS / 2 é usado no estágio de seleção das opções de inicialização, portanto, o Suporte Legado USB deve ser ativado novamente antes do carregamento do SO convidado.
2. Chave de Tarefa de Hardware
Nos sistemas operacionais modernos, a alternância entre tarefas é implementada, via de regra, por métodos de software. No entanto, no Windows7, os seletores apontando para TSS são atribuídos à interrupção 2 - NMI e 8 - Double Fault, o que significa que essas interrupções levarão à alternância de contexto de hardware. O Intel VMX não oferece suporte ao Switch de tarefas de hardware e uma tentativa de executá-lo leva à saída da VM. Para esses casos, escrevi meu manipulador de alternância de tarefas (função GuestTaskSwitch). Uma interrupção de falha dupla ocorre apenas no caso de um conflito grave do sistema causado pelo manuseio inadequado de outras interrupções. No processo de depuração, eu não o encontrei. Mas a NMI aparece nos processadores AP no momento da reinicialização do Windows. Isso ainda levanta minhas dúvidas, porque não está claro se essas NMIs são o resultado de um processo de reinicialização regular ou se é essa operação incorreta do hypervisor em alguns dos estágios anteriores. Se você tiver alguma informação sobre esse assunto, fale nos comentários ou escreva-me em uma mensagem pessoal.
Alterações no SO convidadoSinceramente, não pude decidir por um longo tempo exatamente quais mudanças o hypervisor deve fazer no trabalho do SO convidado. O fato é que, por um lado, eu queria mostrar algo interessante, como a introdução de nossos manipuladores em protocolos de rede básicos, mas, por outro lado, tudo se resumia a uma grande quantidade de código, e havia pouco a ver com o assunto de um hypervisor. Além disso, não queria vincular o hypervisor a nenhum conjunto específico de ferro.
Como resultado, foi encontrado o seguinte compromisso: nesta versão do hypervisor, o controle sobre as chamadas do sistema a partir do modo de usuário é implementado, ou seja, será possível controlar a operação de aplicativos em execução no SO convidado. Esse tipo de controle é bastante simples de implementar e, além disso, permite obter um resultado visual do trabalho.
O controle sobre a operação dos aplicativos será realizado no nível das chamadas do sistema. E o objetivo principal será alterar o resultado da função
NtQuerySystemInformation para que, quando você ligar com o argumento
SystemProcessInformation (
0x05 ), possa interceptar as informações do processo.
No Windows 7, o programa aplicativo para chamar a função de sistema usa o comando assembler sysenter, após o qual o controle é transferido para o processador
KiFastCallEntry para o kernel no nível r0. Para retornar ao nível do aplicativo r3, use o comando sysexit.
Para obter acesso aos resultados da
execução da função
NtQuerySystemInformation, é necessário salvar o número da função chamada toda vez que o comando sysenter é executado. Em seguida, ao executar
sysexit, compare o valor armazenado com o número da função que está sendo interceptada e, se houver uma correspondência, faça alterações nos dados retornados pela função.
O Intel VMX não fornece meios diretos de monitorar a execução de
sysenter / sysexit ; no entanto, se você escrever o valor 0 em
Guest MSR IA32_SYSENTER_CS , os
comandos sysenter / sysexit gerarão uma exceção GP que pode ser usada para chamar o manipulador de saída VM. Para que a exceção GP chame VM Exit, você precisa definir 13 bits no campo
Exception Bitmap no VMCS.
A estrutura abaixo é usada para emular o par sysenter / sysexit.
typedef struct{ QWORD ServiceNumber; QWORD Guest_Sys_CS; QWORD Guest_Sys_EIP; QWORD Guest_Sys_ESP; } SysEnter_T;
O campo
ServiceNumber contém o número da função que está sendo chamada e é atualizado a cada chamada para o sysenter.
Os campos
Guest_Sys_CS, Guest_Sys_EIP, Guest_Sys_ESP são atualizados quando o SO convidado tenta gravar no registro MSR correspondente. Para isso,
são definidas máscaras de gravação no
endereço MSR-Bitmap .
O SO convidado não deve ver as alterações feitas pelo hypervisor na operação das chamadas de função do sistema. Ao definir a máscara de leitura para
MSR IA32_SYSENTER_CS, você pode retornar o sistema operacional convidado ao seu valor de registro original ao ler.
A seguir, é apresentado um esquema de emulação de comando
sysenter / sysexit .

No
estágio de emulação
sysexit , o número armazenado da função chamada é comparado com o número NtQuerySystemInformation (0x105). No caso de uma correspondência, verifica-se que NtQuerySystemInformation é chamado com o argumento System Process Information e, nesse caso, a função
ChangeProcessNames (DWORD SPI_GVA, DWORD SPI_size) faz alterações nas estruturas que contêm informações sobre os processos.
SPI_GVA é o endereço virtual convidado da estrutura
SYSTEM_PROCESS_INFORMATIONSPI_size é o tamanho total de estruturas em bytes.
A estrutura
SYSTEM_PROCESS_INFORMATION se parece com esta:
typedef struct _SYSTEM_PROCESS_INFORMATION { ULONG NextEntryOffset; ULONG NumberOfThreads; BYTE Reserved1[48]; UNICODE_STRING ImageName; KPRIORITY BasePriority; HANDLE UniqueProcessId; PVOID Reserved2; ULONG HandleCount; ULONG SessionId; PVOID Reserved3; SIZE_T PeakVirtualSize; SIZE_T VirtualSize; ULONG Reserved4; SIZE_T PeakWorkingSetSize; SIZE_T WorkingSetSize; PVOID Reserved5; SIZE_T QuotaPagedPoolUsage; PVOID Reserved6; SIZE_T QuotaNonPagedPoolUsage; SIZE_T PagefileUsage; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; LARGE_INTEGER Reserved7[6]; } SYSTEM_PROCESS_INFORMATION;
Não há nada complicado na análise; o principal é não esquecer de converter o endereço virtual do convidado em físico, para isso é usada a função
GuestLinAddrToPhysAddr () .
Para maior clareza, substituí os dois primeiros caracteres nos nomes de todos os processos por um sinal '
:) '. O resultado dessa substituição é visível na captura de tela.
SumárioEm geral, as tarefas definidas no início do artigo foram concluídas. O hipervisor garante a operação estável do sistema operacional convidado e também controla a chamada das funções do sistema no nível do aplicativo. Observo que a principal desvantagem do uso da emulação de comando
sysenter / sysexit é um aumento significativo nas chamadas de saída da VM, o que afeta o desempenho e isso é especialmente perceptível quando o sistema operacional convidado está no modo de processador único. Essa desvantagem pode ser eliminada se você controlar as chamadas apenas no contexto dos processos selecionados.
E isso é tudo por enquanto. As fontes do artigo podem ser obtidas
aquiObrigado pela atenção.