Estou tentando descobrir se é possível executar uma VM Linux cuja RAM é suportada apenas por uma única página física.
Para simular isso, modifiquei o manipulador de falhas de página aninhada no KVM para remover o bit presente de todas as entradas da tabela de páginas aninhadas (NPT), exceto a que corresponde à falha de página processada no momento.
Ao tentar iniciar um convidado Linux, observei que as instruções de montagem que usam operandos de memória, como
add [rbp+0x820DDA], ebp
levar a um loop de falha de página até restaurar o bit atual para a página que contém a instrução e também para a página mencionada no operando (neste exemplo [rbp+0x820DDA]
).
Eu estou querendo saber por que esse é o caso. A CPU não deve acessar as páginas de memória seqüencialmente, ou seja, primeiro leia as instruções e depois acesse o operando da memória? Ou o x86 exige que a página de instruções e todas as páginas de operandos estejam acessíveis ao mesmo tempo?
Estou testando no AMD Zen 1.
Respostas:
Sim, eles exigem o código da máquina e todos os operandos da memória.
Sim, é logicamente o que acontece, mas uma exceção de falha de página interrompe esse processo em duas etapas e descarta qualquer progresso. A CPU não tem como lembrar de que instrução estava no meio quando ocorreu uma falha de página.
Quando um manipulador de falhas de página retorna após manipular uma falha de página válida, RIP = o endereço da instrução com falha, para que a CPU tente executá-la do zero .
Seria legal para o sistema operacional modificar o código da máquina da instrução com falha e esperar executar uma instrução diferente após
iret
o manipulador de falhas de página (ou qualquer outra exceção ou manipulador de interrupção). Portanto, o AFAIK é arquitetonicamente necessário que a CPU refaça a busca de código do CS: RIP no caso de que você está falando. (Supondo que ele retorne ao CS: RIP com falha em vez de agendar outro processo enquanto aguarda o disco na falha da página de disco rígido ou entregando um SIGSEGV a um manipulador de sinal em uma falha de página inválida.)Provavelmente também é necessário arquiteturalmente para entrada / saída de hypervisor. E mesmo que não seja explicitamente proibido no papel, não é assim que as CPUs funcionam.
A @torek comenta que alguns microprocessadores (CISC) decodificam parcialmente as instruções e despejam o estado do microrregistro em uma falha de página , mas o x86 não é assim.
Algumas instruções são interrompíveis e podem progredir parcialmente, como
rep movs
(memcpy em uma lata) e outras instruções de string, ou reunir carregamentos / armazenamentos de dispersão. Mas o único mecanismo é atualizar registros arquiteturais como RCX / RSI / RDI para operações de cadeia de caracteres ou os registros de destino e máscara para reuniões (por exemplo, manual para AVX2vpgatherdd
). Não manter o código de operação / decodificação resulta em algum registro interno oculto e reiniciá-lo após o iret de um manipulador de falhas de página. Estas são instruções que fazem vários acessos de dados separados.Lembre-se também de que o x86 (como a maioria dos ISAs) garante que as instruções sejam atômicas. interrupções / exceções: elas acontecem completamente, ou não acontecem, antes de uma interrupção. Interrompendo uma instrução de montagem enquanto estiver em operação . Por exemplo,
add [mem], reg
seria necessário descartar a carga se a parte da loja falhar, mesmo sem umlock
prefixo.O pior número de páginas de espaço de usuário convidado presentes para avançar pode ser 6 (mais subárvores de tabela de página de kernel de convidado separadas para cada uma):
movsq
oumovsw
instruções de 2 bytes que ultrapassam o limite de uma página; portanto, são necessárias as duas páginas para decodificar.[rsi]
também uma divisão de página[rdi]
também uma divisão de páginaSe alguma dessas 6 páginas falhar, estamos de volta à estaca zero.
rep movsd
também é uma instrução de 2 bytes, e progredir em uma etapa dela teria o mesmo requisito. Casos semelhantes, comopush [mem]
oupop [mem]
podem ser construídos com uma pilha desalinhada.Uma das razões (ou benefícios colaterais) para / de tornar as cargas coletadas / armazenamentos de dispersão "interruptíveis" (atualizando o vetor de máscara com o progresso) é evitar aumentar esse espaço mínimo para executar uma única instrução. Também para melhorar a eficiência de lidar com várias falhas durante uma coleta ou dispersão.
O @Brandon aponta nos comentários que um convidado precisará de suas tabelas de páginas na memória , e as divisões de páginas no espaço do usuário também podem ser divisões de 1GiB, de modo que os dois lados estejam em subárvores diferentes do PML4 de nível superior. O passeio pela página HW precisará tocar em todas essas páginas da tabela de páginas de convidados para progredir. Uma situação dessa patologia dificilmente acontecerá por acaso.
O TLB (e os internos do caminhante de páginas) têm permissão para armazenar em cache alguns dos dados da tabela de páginas e não são necessários para reiniciar o percurso da página do zero, a menos que o SO tenha
invlpg
criado ou definido um novo diretório de página de nível superior CR3. Nenhuma delas é necessária ao alterar uma página de não presente para presente; O x86 on paper garante que não é necessário (portanto, o "cache negativo" de PTEs não presentes não é permitido, pelo menos não visível ao software). Portanto, a CPU pode não VMexit, mesmo que algumas páginas da tabela de páginas físicas do convidado não estejam realmente presentes.Os contadores de desempenho da PMU podem ser ativados e configurados de modo que a instrução também exija um evento perf para gravar em um buffer PEBS para essa instrução. Com a máscara de um contador configurada para contar apenas as instruções de espaço do usuário, não o kernel, é bem possível que continue tentando estourar o contador e armazenar uma amostra no buffer sempre que você retornar ao espaço do usuário, produzindo uma falha de página.
fonte
push dword [foo
" (ou até apenascall [foo]
) com tudo desalinhado no "limite da tabela de ponteiros de diretório de páginas" (adicionando até 6 páginas, 6 tabelas de páginas, 6 diretórios de páginas, 6 PDPTs e um PML4); com o recurso "amostragem precisa baseada em eventos com buffer PEBS" da CPU ativado e configurado para que ospush
dados de monitoramento de desempenho sejam adicionados ao buffer PEBS. Para um "número mínimo de páginas fornecidas pelo host para que o hóspede possa progredir em casos patológicos", eu gostaria de ter pelo menos 16 páginas.EIP
. Portanto, há uma pergunta lógica de acompanhamento. Qual é o número mínimo de páginas necessárias, assumindo um esquema de correção de instruções inteligente? Por exemplo, copie o valor não alinhado para um buffer temporário alinhado, emule a instrução e IRET para a próxima instrução.iret
instruções do sistema operacional também precisa estar na memória. Esta é uma instrução de um byte, portanto, uma página extra. O endereço de interrupção do manipulador de falhas da página também precisa estar na memória, mas pode ser a mesma página acima.