En un
artículo anterior (enlace), hablé sobre el concepto básico de un hipervisor basado en la tecnología de virtualización de hardware Intel. Ahora propongo ampliar las capacidades del hipervisor agregando soporte para la arquitectura multiprocesador (SMP), y también considero un ejemplo de cómo el hipervisor puede realizar cambios en el sistema operativo invitado.
Todas las acciones adicionales se llevarán a cabo en la PC con la siguiente configuración:
CPU: Intel Core i7 5820K
Placa base: Asus X99-PRO
Ram: 16 GB
SO invitado: Windows 7 x32 con PAE deshabilitado
Comenzaré describiendo la ubicación de los componentes del hipervisor en el disco duro (todos los valores se especifican en sectores).
El proceso de cargar un hipervisor difiere de la versión anterior solo en presencia de un nuevo módulo hypervisor.ap , cuyo propósito es la inicialización básica del procesador AP.El proceso de cargar módulos en la memoria:
Soporte SMPImplementé un hipervisor sobre el principio del multiprocesamiento simétrico, lo que significa que se lanzará la misma copia de VMX en todos los procesadores lógicos presentes. Además, las tablas IDT y GDT, así como las tablas para la memoria de paginación, serán comunes a todos los procesadores lógicos. Hice esto porque el hipervisor inicializará inmediatamente la memoria para el espacio de direcciones del sistema operativo invitado y no hay necesidad de reasignar dinámicamente las direcciones físicas de páginas individuales. Además, con este enfoque, no necesita monitorear la correspondencia de los cachés TLB del procesador al costado del hipervisor.
El proceso de inicialización para BSP y AP será diferente. Todas las estructuras principales involucradas en el hipervisor se crearán durante la inicialización del BSP. Además, el estado de actividad para los procesadores AP vmx en modo no root se establecerá en estado HLT. Por lo tanto, el entorno del SO huésped se emulará de acuerdo con lo que sería sin usar la virtualización.
Inicializando BSP:
- Inicialización de Spinlock
- Inicializando y cargando tablas GDT e IDT
- Inicializando tablas de paginación
- Inicializando estructuras VMCS y creando una tabla EPT común
- Activación de procesadores AP. Para hacer esto, se envía una secuencia de interrupción INIT - SIPI a cada AP. El vector para la interrupción SIPI es 0x20, que corresponde a la transferencia del control AP a 0x20000 (módulo hypervisor.ap)
- Inicio del SO invitado en 0x7C00 (módulo win7.mbr)
Inicialización AP:
- Después de activar el AP, el procesador está en modo real. El módulo hypervisor.ap inicializa las tablas de memoria y paginación para cambiar al modo largo
- Descargue IDT, GDT, así como el catálogo de tablas de paginación creadas durante la fase de inicialización de BSP
- Inicialización de estructuras VMCS y carga de tablas EPT creadas en la etapa de inicialización de BSP
- Cambio al modo vmx no root con estado HLT activo
Podemos decir que la implementación del soporte SMP en el hipervisor es bastante simple, pero hay algunos puntos sobre los que me gustaría llamar la atención.
1.soporte legacy usb
Es posible que los nuevos modelos de placa base no tengan conectores PS / 2, por lo que se utiliza la compatibilidad con USB Legacy para garantizar la compatibilidad con versiones anteriores. Esto significa que puede trabajar con un teclado o mouse usb utilizando los mismos métodos (puertos de entrada / salida) que con el estándar PS / 2. La implementación de la compatibilidad con USB Legacy no solo depende del modelo de la placa base, sino que también puede llamarse en diferentes versiones de firmware. En mi placa base Asus X99-PRO, el soporte USB Legacy se implementa a través de interrupciones SMI, en cuyo procesador se produce la emulación PS / 2. Escribo sobre esto con tanto detalle, porque en mi caso (versión de firmware 3801), el soporte USB heredado no es compatible con el modo largo y cuando regresa de SMM, el procesador entra en estado de apagado.
La solución más fácil en esta situación es desactivar la compatibilidad con USB Legacy antes de cambiar al modo largo. Sin embargo, en Windows, el método de sondeo del teclado PS / 2 se usa en la etapa de selección de las opciones de arranque, por lo que la compatibilidad con USB Legacy debe activarse nuevamente antes de que el SO huésped comience a cargarse.
2. Interruptor de tareas de hardware
En los sistemas operativos modernos, el cambio entre tareas se implementa, por regla general, mediante métodos de software. Sin embargo, en Windows7, los selectores que apuntan a TSS se asignan a la interrupción 2 - NMI y 8 - Falla doble, lo que significa que tales interrupciones conducirán a un cambio de contexto de hardware. Intel VMX no es compatible con el conmutador de tareas de hardware, y un intento de ejecutarlo lleva a VM Exit. Para tales casos, escribí mi controlador de interruptor de tareas (función GuestTaskSwitch). Una interrupción de doble falla ocurre solo en el caso de un conflicto grave del sistema causado por el manejo inadecuado de otras interrupciones. En el proceso de depuración, no lo encontré. Pero NMI aparece en los procesadores AP al momento de reiniciar Windows. Esto todavía plantea mis dudas porque no está claro si estos NMI son el resultado de un proceso de reinicio regular o si se trata de una operación incorrecta del hipervisor en algunas de las etapas anteriores. Si tiene alguna información sobre este tema, hable en los comentarios o escríbame en un mensaje personal.
Cambios en el sistema operativo invitadoHonestamente, no pude decidir durante mucho tiempo exactamente qué cambios debería hacer el hipervisor en el trabajo del sistema operativo invitado. El hecho es que, por un lado, quería mostrar algo interesante, como la introducción de nuestros controladores en protocolos básicos de red, pero por otro lado, todo se reduciría a una gran cantidad de código, y había poco que ver con el tema de un hipervisor. Además, no quería vincular el hipervisor a ningún conjunto específico de hierro.
Como resultado, se encontró el siguiente compromiso: en esta versión del hipervisor, se implementa el control sobre las llamadas del sistema desde el modo de usuario, en otras palabras, será posible controlar el funcionamiento de las aplicaciones que se ejecutan en el sistema operativo invitado. Este tipo de control es bastante simple de implementar y además le permite obtener un resultado visual del trabajo.
El control sobre el funcionamiento de las aplicaciones se realizará a nivel de llamadas del sistema. Y el objetivo principal será cambiar el resultado de la función
NtQuerySystemInformation para que cuando llame con el argumento
SystemProcessInformation (
0x05 ), pueda interceptar la información del proceso.
En Windows 7, el programa de aplicación para llamar a la función del sistema usa el comando del ensamblador de sysenter, después de lo cual el control se transfiere al procesador
KiFastCallEntry al kernel en el nivel r0. Para volver al nivel de aplicación r3, use el comando sysexit.
Para obtener acceso a los resultados de la función
NtQuerySystemInformation , debe guardar el número de la función llamada cada vez que se ejecuta el comando sysenter. Luego, cuando ejecute
sysexit, compare el valor almacenado con el número de la función que se está interceptando y, si hay una coincidencia, realice cambios en los datos devueltos por la función.
Intel VMX no proporciona medios directos para monitorear la ejecución de
sysenter / sysexit , sin embargo, si escribe el valor 0 en
Guest MSR IA32_SYSENTER_CS , los
comandos sysenter / sysexit generarán una excepción de GP que se puede usar para llamar al controlador de salida de VM. Para que la excepción GP llame a VM Exit, debe establecer 13 bits en el campo
Exception Bitmap de VMCS.
La siguiente estructura se utiliza para emular el par sysenter / sysexit.
typedef struct{ QWORD ServiceNumber; QWORD Guest_Sys_CS; QWORD Guest_Sys_EIP; QWORD Guest_Sys_ESP; } SysEnter_T;
El campo
ServiceNumber contiene el número de la función que se llama y se actualiza con cada llamada al sysenter.
Los campos
Guest_Sys_CS, Guest_Sys_EIP, Guest_Sys_ESP se actualizan cuando el SO huésped intenta escribir en el registro MSR correspondiente. Para hacer esto,
se configuran las máscaras de escritura en la
dirección de mapa de bits MSR .
El SO huésped no debería ver los cambios realizados por el hipervisor en la operación de las llamadas a funciones del sistema. Al configurar la máscara de lectura para
MSR IA32_SYSENTER_CS, puede devolver el sistema operativo invitado a su valor de registro original al leer.
El siguiente es un esquema de emulación de comando
sysenter / sysexit .

En la
etapa de emulación
sysexit , el número almacenado de la función llamada se compara con el número NtQuerySystemInformation (0x105). En el caso de una coincidencia, se verifica que se llame a NtQuerySystemInformation con el argumento de información del proceso del sistema y, de ser así, la función
ChangeProcessNames (DWORD SPI_GVA, DWORD SPI_size) realiza cambios en las estructuras que contienen información sobre los procesos.
SPI_GVA es la dirección virtual invitada de la estructura
SYSTEM_PROCESS_INFORMATIONSPI_size es el tamaño total de las estructuras en bytes.
La estructura
SYSTEM_PROCESS_INFORMATION en sí se ve así:
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;
No hay nada complicado en su análisis, lo principal es no olvidar traducir la dirección virtual del invitado en física, para esto se utiliza la función
GuestLinAddrToPhysAddr () .
Para mayor claridad, reemplacé los dos primeros caracteres en los nombres de todos los procesos con un signo '
:) '. El resultado de dicho reemplazo es visible en la captura de pantalla.
ResumenEn general, se completaron las tareas establecidas al comienzo del artículo. El hipervisor garantiza el funcionamiento estable del sistema operativo invitado y también controla la llamada de las funciones del sistema desde el nivel de la aplicación. Observo que el principal inconveniente de usar la emulación de comandos
sysenter / sysexit es un aumento significativo en las llamadas de salida de VM, que afecta el rendimiento y esto es especialmente notable cuando el sistema operativo invitado está en modo de procesador único. Esta desventaja puede eliminarse si controla las llamadas solo en el contexto de los procesos seleccionados.
Y eso es todo por ahora. Las fuentes del artículo pueden consultarse
aquí.Gracias por su atencion