Sí, requieren el código de máquina y todos los operandos de memoria.
¿No debería la CPU acceder a las páginas de memoria secuencialmente, es decir, primero leer las instrucciones y luego acceder al operando de la memoria?
Sí, eso es lógicamente lo que sucede, pero una excepción de falla de página interrumpe ese proceso de 2 pasos y descarta cualquier progreso. La CPU no tiene forma de recordar qué instrucción estaba en el medio de cuando ocurrió un fallo de página.
Cuando un manejador de fallas de página regresa después de manejar una falla de página válida, RIP = la dirección de la instrucción de falla, por lo que la CPU vuelve a intentar ejecutarla desde cero .
Sería legal para el sistema operativo modificar el código de máquina de la instrucción de falla y esperar que ejecute una instrucción diferente después iret
del manejador de fallas de página (o cualquier otra excepción o manejador de interrupciones). Entonces, AFAIK es arquitectónicamente necesario que la CPU rehaga la búsqueda de código de CS: RIP en el caso de que esté hablando. (Suponiendo que incluso regrese al CS con errores: RIP en lugar de programar otro proceso mientras espera el disco en una falla de página dura, o entregar un SIGSEGV a un controlador de señal en una falla de página no válida).
Probablemente también sea arquitectónicamente necesario para la entrada / salida del hipervisor. E incluso si no está explícitamente prohibido en papel, no es cómo funcionan las CPU.
@torek comenta que algunos microprocesadores (CISC) decodifican parcialmente las instrucciones y vuelcan el estado del microregistro en una falla de página , pero x86 no es así.
Algunas instrucciones son interrumpibles y pueden hacer progresos parciales, como rep movs
(memcpy in a can) y otras instrucciones de cadena, o reunir cargas / almacenes de dispersión. Pero el único mecanismo es actualizar los registros arquitectónicos como RCX / RSI / RDI para operaciones de cadena, o los registros de destino y máscara para los recopiladores (por ejemplo, manual para AVX2vpgatherdd
). No mantener el código de operación / decodificación da como resultado un registro interno oculto y reiniciarlo después de iret desde un controlador de fallas de página. Estas son instrucciones que hacen múltiples accesos de datos separados.
También tenga en cuenta que x86 (como la mayoría de los ISA) garantiza que las instrucciones son atómicas wrt. interrupciones / excepciones: suceden completamente o no suceden antes de una interrupción. Interrumpir una instrucción de ensamblaje mientras está en funcionamiento . Entonces, por ejemplo add [mem], reg
, sería necesario descartar la carga si la parte de la tienda fallara, incluso sin un lock
prefijo.
El peor número de páginas de espacio de usuario invitado presentes para avanzar puede ser 6 (más subárboles de tabla de páginas de kernel invitado separados para cada uno):
movsq
o movsw
instrucción de 2 bytes que abarca un límite de página, por lo que se necesitan ambas páginas para que se decodifique.
- operando fuente qword
[rsi]
también una división de página
- qword operando de destino
[rdi]
también una división de página
Si alguna de estas 6 páginas falla, volvemos al punto de partida.
rep movsd
también es una instrucción de 2 bytes, y avanzar en un paso tendría el mismo requisito. Casos similares como push [mem]
o pop [mem]
podrían construirse con una pila desalineada.
Una de las razones (o beneficios secundarios) para / de hacer que las cargas de recolección / almacenamiento de dispersiones sean "interrumpibles" (actualizar el vector de máscara con su progreso) es evitar aumentar esta huella mínima para ejecutar una sola instrucción. También para mejorar la eficiencia del manejo de múltiples fallas durante una reunión o dispersión.
@Brandon señala en los comentarios que un invitado necesitará sus tablas de páginas en la memoria , y las divisiones de página del espacio de usuario también pueden ser divisiones de 1GiB, por lo que los dos lados están en subárboles diferentes del nivel superior PML4. El recorrido de la página de HW deberá tocar todas estas páginas de la tabla de páginas de invitados para avanzar. Una situación tan patológica es poco probable que ocurra por casualidad.
Los TLB (y los elementos internos del caminante de páginas) pueden almacenar en caché algunos de los datos de la tabla de páginas, y no están obligados a reiniciar el recorrido de la página desde cero a menos que el sistema operativo lo haya hecho invlpg
o haya establecido un nuevo directorio de página de nivel superior CR3. Ninguno de estos es necesario cuando se cambia una página de no presente a presente; x86 en papel garantiza que no es necesario (por lo que no se permite el "almacenamiento en caché negativo" de PTE no presentes, al menos no visible para el software). Por lo tanto, es posible que la CPU no VMexit incluso si algunas de las páginas de tabla de páginas físicas de invitado no están realmente presentes.
Los contadores de rendimiento de PMU se pueden habilitar y configurar de manera que la instrucción también requiera un evento de rendimiento para escribir en un búfer PEBS para esa instrucción. Con una máscara de contador configurada para contar solo las instrucciones de espacio de usuario, no el núcleo, podría ser que siga intentando desbordar el contador y almacenar una muestra en el búfer cada vez que regrese al espacio de usuario, produciendo un error de página.