Sim, eles exigem o código da máquina e todos os operandos da memória.
A CPU não deve acessar as páginas de memória sequencialmente, ou seja, primeiro leia as instruções e depois acesse o operando 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 um lock
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
ou movsw
instruções de 2 bytes que ultrapassam o limite de uma página; portanto, são necessárias as duas páginas para decodificar.
- operando fonte qword
[rsi]
também uma divisão de página
- operando de destino qword
[rdi]
também uma divisão de página
Se 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, como push [mem]
ou pop [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.