Modo de seguridad virtual

El modo seguro virtual (VSM) es un conjunto de funcionalidades de hipervisor e iluminaciones que se ofrecen para hospedar y las particiones invitadas, lo que permite la creación y administración de nuevos límites de seguridad dentro del software del sistema operativo. VSM es la instalación del hipervisor en la que se basan Windows características de seguridad, como Device Guard, Credential Guard, TPM virtuales y máquinas virtuales blindadas. Estas características de seguridad se introdujeron en Windows 10 y Windows Server 2016.

VSM permite que el software del sistema operativo en las particiones raíz e invitado cree regiones aisladas de memoria para el almacenamiento y el procesamiento de recursos de seguridad del sistema. El acceso a estas regiones aisladas se controla y concede únicamente a través del hipervisor, que es una parte altamente privilegiada y de confianza alta de la base de proceso de confianza (TCB) del sistema. Dado que el hipervisor se ejecuta en un nivel de privilegio mayor que el software del sistema operativo y tiene un control exclusivo de los recursos clave de hardware del sistema, como los controles de permisos de acceso a la memoria en la CPU MMU y IOMMU al principio de la inicialización del sistema operativo, el hipervisor puede proteger estas regiones aisladas del acceso no autorizado, incluso desde el software del sistema operativo (por ejemplo, el kernel del sistema operativo y los controladores de dispositivos) con acceso al modo de supervisión (es decir, CPL0, o "Anillo 0").

Con esta arquitectura, incluso si el software de nivel de sistema normal que se ejecuta en modo de supervisor (por ejemplo, kernel, controladores, etc.) está en peligro por software malintencionado, los recursos de regiones aisladas protegidas por el hipervisor pueden permanecer protegidos.

Nivel de confianza virtual (VTL)

VSM logra y mantiene el aislamiento a través de niveles de confianza virtual (VTL). Las VTL están habilitadas y administradas tanto por partición como por procesador virtual.

Los niveles de confianza virtual son jerárquicos, y los niveles superiores tienen más privilegios que los niveles inferiores. VTL0 es el nivel con privilegios mínimos, con VTL1 con más privilegios que VTL0, VTL2 con más privilegios que VTL1, etc.

Arquitectónicamente, se admiten hasta 16 niveles de VTL; sin embargo, un hipervisor puede optar por implementar menos de 16 VTL. Actualmente, solo se implementan dos VTL.

typedef UINT8 HV_VTL, *PHV_VTL;

#define HV_NUM_VTLS 2
#define HV_INVALID_VTL ((HV_VTL) -1)
#define HV_VTL_ALL 0xF

Cada VTL tiene su propio conjunto de protecciones de acceso a memoria. Estas protecciones de acceso se administran mediante el hipervisor en el espacio de direcciones físicos de una partición y, por tanto, el software de nivel de sistema que se ejecuta en la partición no puede modificarse.

Dado que las VTL con más privilegios pueden aplicar sus propias protecciones de memoria, las VTL más altas pueden proteger eficazmente las áreas de memoria de las VTL inferiores. En la práctica, esto permite que un VTL inferior proteja las regiones de memoria aisladas protegiéndolos con un VTL mayor. Por ejemplo, VTL0 podría almacenar un secreto en VTL1, en cuyo momento solo VTL1 podía acceder a él. Incluso si VTL0 está en peligro, el secreto sería seguro.

Protecciones de VTL

Hay varias facetas para lograr el aislamiento entre las VTL:

  • Protecciones de acceso a memoria: cada VTL mantiene un conjunto de protecciones de acceso a memoria física invitada. El software que se ejecuta en un VTL determinado solo puede acceder a la memoria de acuerdo con estas protecciones.
  • Estado del procesador virtual: los procesadores virtuales mantienen un estado independiente por VTL. Por ejemplo, cada VTL define un conjunto de registros vpn privados. El software que se ejecuta en un VTL inferior no puede acceder al estado de registro del procesador virtual privado de VTL superior.
  • Interrupciones: junto con un estado de procesador independiente, cada VTL también tiene su propio subsistema de interrupción (APIC local). Esto permite que las VTL más altas procesen interrupciones sin arriesgar la interferencia de un VTL inferior.
  • Páginas superpuestas: ciertas páginas de superposición se mantienen por VTL de forma que las VTL más altas tengan acceso confiable. Por ejemplo, hay una página de superposición de hiperllamada independiente por VTL.

Detección y estado de VSM

La funcionalidad de VSM se anuncia en particiones a través de la marca de privilegios de partición AccessVsm. Solo las particiones con todos los privilegios siguientes pueden usar VSM: AccessVsm, AccessVpRegisters y AccessSynicRegs.

Detección de funcionalidades de VSM

Los invitados deben usar el siguiente registro específico del modelo para acceder a un informe sobre las funcionalidades de VSM:

Dirección MSR Nombre del registro Descripción
0x000D0006 HV_X64_REGISTER_VSM_CAPABILITIES Informe sobre las funcionalidades de VSM.

El formato de Register VSM Capabilities MSR es el siguiente:

Bits Descripción Atributos
63 Dr6Shared Lectura
62:47 MbecVtlMask Lectura
46 DenyLowerVtlStartup Lectura
45:0 RsvdZ Lectura

Dr6Shared indica al invitado si Dr6 es un registro compartido entre las VTL.

MvecVtlMask indica al invitado las VTL para las que se puede habilitar Mbec.

DenyLowerVtlStartup indica al invitado si un Vtl puede denegar un restablecimiento de VPN mediante un VTL inferior.

Registro de estado de VSM

Además de una marca de privilegios de partición, se pueden usar dos registros virtuales para obtener información adicional sobre el estado de VSM: HvRegisterVsmPartitionStatus y HvRegisterVsmVpStatus.

HvRegisterVsmPartitionStatus

HvRegisterVsmPartitionStatus es un registro de solo lectura por partición que se comparte entre todas las VTL. Este registro proporciona información sobre qué VTL se han habilitado para la partición, que tienen habilitados los controles de ejecución basados en modo, así como el VTL máximo permitido.

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 EnabledVtlSet : 16;
        UINT64 MaximumVtl : 4;
        UINT64 MbecEnabledVtlSet: 16;
        UINT64 ReservedZ : 28;
    };
} HV_REGISTER_VSM_PARTITION_STATUS;

HvRegisterVsmVpStatus

HvRegisterVsmVpStatus es un registro de solo lectura y se comparte entre todas las VTL. Es un registro por VP, lo que significa que cada procesador virtual mantiene su propia instancia. Este registro proporciona información sobre qué VTL se han habilitado, que está activa, así como el modo MBEC activo en una VP.

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 ActiveVtl : 4;
        UINT64 ActiveMbecEnabled : 1;
        UINT64 ReservedZ0 : 11;
        UINT64 EnabledVtlSet : 16;
        UINT64 ReservedZ1 : 32;
    };
} HV_REGISTER_VSM_VP_STATUS;

ActiveVtl es el identificador del contexto de VTL que está activo actualmente en el procesador virtual.

ActiveMbecEnabled especifica que MBEC está activo actualmente en el procesador virtual.

EnabledVtlSet es un mapa de bits de los VTL habilitados en el procesador virtual.

Estado inicial de la VTL de partición

Cuando se inicia o restablece una partición, comienza a ejecutarse en VTL0. Todas las demás VTL están deshabilitadas en la creación de particiones.

Habilitación de VTL

Para empezar a usar un VTL, un VTL inferior debe iniciar lo siguiente:

  1. Habilite el VTL de destino para la partición. Esto hace que el VTL esté disponible con carácter general para la partición.
  2. Habilite el VTL de destino en uno o varios procesadores virtuales. Esto hace que el VTL esté disponible para una VP y establece su contexto inicial. Se recomienda que todas las máquinas virtuales tengan las mismas VTL habilitadas. Tener una VTL habilitada en algunas máquinas virtuales (pero no todas) puede provocar un comportamiento inesperado.
  3. Una vez que la VTL está habilitada para una partición y VP, puede empezar a establecer protecciones de acceso una vez establecida la marca EnableVtlProtection.

Tenga en cuenta que las VTL no necesitan ser consecutivas.

Habilitación de un VTL de destino para una partición

La hiperllamada HvCallEnablePartitionVtl se usa para habilitar un VTL para una partición determinada. Tenga en cuenta que antes de que el software pueda ejecutarse realmente en un VTL determinado, debe habilitarse VTL en procesadores virtuales de la partición.

Habilitación de un VTL de destino para procesadores virtuales

Una vez habilitada una VTL para una partición, se puede habilitar en los procesadores virtuales de la partición. La hiperllamada HvCallEnableVpVtl se puede usar para habilitar las VTL para un procesador virtual, que establece su contexto inicial.

Los procesadores virtuales tienen un "contexto" por VTL. Si se cambia un VTL, también se cambia el estado privado del VTL.

Configuración de VTL

Una vez que se ha habilitado una VTL, una VP que se ejecuta en un VTL igual o superior puede cambiar su configuración.

Configuración de la partición

Los atributos de toda la partición se pueden configurar mediante el registro HvRegisterVsmPartitionConfig. Hay una instancia de este registro para cada VTL (mayor que 0) en cada partición.

Cada VTL puede modificar su propia instancia de HV_REGISTER_VSM_PARTITION_CONFIG, así como instancias de VTL inferiores. Es posible que las VTL no modifiquen este registro para las VTL más altas.

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 EnableVtlProtection : 1;
        UINT64 DefaultVtlProtectionMask : 4;
        UINT64 ZeroMemoryOnReset : 1;
        UINT64 DenyLowerVtlStartup : 1;
        UINT64 ReservedZ : 2;
        UINT64 InterceptVpStartup : 1;
        UINT64 ReservedZ : 54; };
} HV_REGISTER_VSM_PARTITION_CONFIG;

Los campos de este registro se describen a continuación.

Habilitación de protecciones de VTL

Una vez que se ha habilitado una VTL, se debe establecer la marca EnableVtlProtection para poder empezar a aplicar protecciones de memoria. Esta marca es de escritura una vez, lo que significa que una vez que se ha establecido, no se puede modificar.

Máscara de protección predeterminada

De forma predeterminada, el sistema aplica protecciones RWX a todas las páginas asignadas actualmente y a las páginas futuras "agregadas en caliente". Las páginas agregadas en caliente hacen referencia a cualquier memoria que se agregue a una partición durante una operación de cambio de tamaño.

Una VTL superior puede establecer una directiva de protección de memoria predeterminada diferente especificando DefaultVtlProtectionMask en HV_REGISTER_VSM_PARTITION_CONFIG. Esta máscara debe establecerse en el momento en que el VTL está habilitado. No se puede cambiar una vez establecido y solo se borra mediante un restablecimiento de partición.

bit Descripción
0 Lectura
1 Escritura
2 Ejecución del modo kernel (KMX)
3 Ejecución en modo de usuario (UMX)

Memoria cero al restablecer

ZeroMemOnReset es un bit que controla si la memoria está cero antes de restablecer una partición. Esta configuración está activada de forma predeterminada. Si se establece el bit, la memoria de la partición se cero al restablecerse para que una VTL superior no pueda verse comprometida por una VTL inferior. Si se borra este bit, la memoria de la partición no se aplica a cero en el restablecimiento.

DenyLowerVtlStartup

La marca DenyLowerVtlStartup controla si un procesador virtual se puede iniciar o restablecer mediante VTL inferiores. Esto incluye formas arquitectónicas de restablecer un procesador virtual (por ejemplo, SIPI en X64), así como la hiperllamada HvCallStartVirtualProcessor .

InterceptVpStartup

Si se establece la marca InterceptVpStartup, iniciar o restablecer un procesador virtual genera una interceptación en el VTL superior.

Configuración de VTL inferiores

Las VTL más altas pueden usar el registro siguiente para configurar el comportamiento de las VTL inferiores:

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 MbecEnabled : 1;
        UINT64 TlbLocked : 1;
        UINT64 ReservedZ : 62;
    };
} HV_REGISTER_VSM_VP_SECURE_VTL_CONFIG;

Cada VTL (superior a 0) tiene una instancia de este registro para cada VTL inferior a sí misma. Por ejemplo, VTL2 tendría dos instancias de este registro: una para VTL1 y una segunda para VTL0.

Los campos de este registro se describen a continuación.

MbecEnabled

Este campo configura si MBEC está habilitado para el VTL inferior.

TlbLocked

Este campo bloquea el TLB del VTL inferior. Esta funcionalidad se puede usar para evitar que las VTL inferiores provoquen invalidaciones de TLB que podrían interferir con un VTL mayor. Cuando se establece este bit, todas las solicitudes de vaciado del espacio de direcciones del VTL inferior se bloquean hasta que se levante el bloqueo.

Para desbloquear el TLB, el VTL superior puede borrar este bit. Además, una vez que una VP vuelve a un VTL inferior, libera todos los bloqueos de TLB que contiene en el momento.

Entrada de VTL

Una VTL se "introduce" cuando una VP cambia de un VTL inferior a una superior. Esto puede deberse a los siguientes motivos:

  1. Llamada de VTL: es cuando el software desea invocar código explícitamente en un VTL superior.
  2. Interrupción segura: si se recibe una interrupción para una VTL superior, la VP entrará en el VTL superior.
  3. Interceptación segura: ciertas acciones desencadenarán una interrupción segura (por ejemplo, el acceso a determinadas MSR).

Una vez que se introduce un VTL, debe salir voluntariamente. Una VTL superior no puede ser adelantada por un VTL inferior.

Identificación del motivo de entrada de VTL

Para reaccionar adecuadamente a una entrada, es posible que un VTL mayor tenga que saber el motivo por el que se ha escrito. Para distinguir entre los motivos de entrada, la entrada VTL se incluye en la estructura de HV_VP_VTL_CONTROL .

Llamada de VTL

Una llamada "VTL" es cuando un VTL inferior inicia una entrada en un VTL superior (por ejemplo, para proteger una región de memoria con el VTL superior) a través de la hiperllamada HvCallVtlCall .

Las llamadas VTL conservan el estado de los registros compartidos en los conmutadores de VTL. Los registros privados se conservan en un nivel por VTL. La excepción a estas restricciones son los registros requeridos por la secuencia de llamadas de VTL. Los siguientes registros son necesarios para una llamada de VTL:

x64 x86 Descripción
RCX EDX:EAX Especifica una entrada de control de llamada de VTL al hipervisor.
RAX ECX Reservada

Todos los bits de la entrada del control de llamadas VTL están reservados actualmente.

Restricciones de llamadas de VTL

Las llamadas VTL solo se pueden iniciar desde el modo de procesador con más privilegios. Por ejemplo, en sistemas x64, una llamada VTL solo puede provenir de CPL0. Una llamada de VTL iniciada desde un modo de procesador que es cualquier cosa, pero el más privilegiado del sistema da como resultado que el hipervisor inserte una excepción de #UD en el procesador virtual.

Una llamada de VTL solo puede cambiar al siguiente VTL más alto. En otras palabras, si hay varias VTL habilitadas, una llamada no puede "omitir" un VTL. Las siguientes acciones producen una excepción de #UD:

  • Una llamada de VTL iniciada desde un modo de procesador que es cualquier cosa, pero el más privilegiado en el sistema (específico de la arquitectura).
  • Una llamada VTL desde el modo real (x86/x64)
  • Una llamada de VTL en un procesador virtual donde el VTL de destino está deshabilitado (o aún no se ha habilitado).
  • Una llamada de VTL con un valor de entrada de control no válido

Salida de VTL

Un modificador a un VTL inferior se conoce como "retorno". Una vez que un VTL ha terminado de procesarse, puede iniciar una devolución de VTL para cambiar a un VTL inferior. La única forma en que se puede producir un retorno de VTL es si un VTL mayor inicia voluntariamente uno. Un VTL inferior nunca puede adelantar uno más alto.

Devolución de VTL

Una "devolución de VTL" es cuando un VTL superior inicia un conmutador en un VTL inferior a través del hiperllamada HvCallVtlReturn . De forma similar a una llamada de VTL, el estado del procesador privado se desactiva y el estado compartido permanece en su lugar. Si el VTL inferior ha llamado explícitamente al VTL superior, el hipervisor incrementa el puntero de instrucción de VTL superior antes de que se complete la devolución para que pueda continuar después de una llamada de VTL.

Una secuencia de código de retorno de VTL requiere el uso de los siguientes registros:

x64 x86 Descripción
RCX EDX:EAX Especifica una entrada de control de retorno de VTL para el hipervisor.
RAX ECX Reservada

La entrada de control de retorno de VTL tiene el siguiente formato:

Bits Campo Descripción
63:1 RsvdZ
0 Devolución rápida Los registros no se restauran

Las siguientes acciones generarán una excepción de #UD:

  • Intento de devolución de VTL cuando el VTL más bajo está activo actualmente
  • Intento de devolución de VTL con un valor de entrada de control no válido
  • Intentar un VTL devuelta desde un modo de procesador que sea cualquier cosa, pero el más privilegiado del sistema (específico de la arquitectura)

Devolución rápida

Como parte del procesamiento de una devolución, el hipervisor puede restaurar el estado de registro del VTL inferior desde la estructura HV_VP_VTL_CONTROL . Por ejemplo, después de procesar una interrupción segura, es posible que un VTL mayor desee devolver sin interrumpir el estado de la VTL inferior. Por lo tanto, el hipervisor proporciona un mecanismo para simplemente restaurar los registros del VTL inferior a su valor de llamada previa almacenado en la estructura de control VTL.

Si este comportamiento no es necesario, un VTL superior puede usar una "devolución rápida". Una devolución rápida es cuando el hipervisor no restaura el estado de registro de la estructura de control. Esto se debe utilizar siempre que sea posible para evitar el procesamiento innecesario.

Este campo se puede establecer con el bit 0 de la entrada de devolución de VTL. Si se establece en 0, los registros se restauran a partir de la estructura HV_VP_VTL_CONTROL. Si este bit está establecido en 1, los registros no se restauran (una devolución rápida).

Asistencia de página de Hypercall

El hipervisor proporciona mecanismos para ayudar con las llamadas VTL y devuelve a través de la página de hiperllamada. En esta página se abstrae la secuencia de código específica necesaria para cambiar las VTL.

Se puede tener acceso a las secuencias de código para ejecutar llamadas de VTL y devolver mediante la ejecución de instrucciones específicas en la página de hiperllamada. Los fragmentos de llamada y devolución se encuentran en un desplazamiento en la página de hiperllamada determinada por el registro virtual HvRegisterVsmCodePageOffset. Se trata de un registro de solo lectura y de toda la partición, con una instancia independiente por VTL.

Una VTL puede ejecutar una llamada o devolución de VTL mediante la instrucción CALL. Una LLAMADA a la ubicación correcta en la página de hiperllamada iniciará una llamada o devolución de VTL.

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 VtlCallOffset : 12;
        UINT64 VtlReturnOffset : 12;
        UINT64 ReservedZ : 40;
    };
} HV_REGISTER_VSM_CODE_PAGE_OFFSETS;

En resumen, los pasos para llamar a una secuencia de código mediante la página de hiperllamada son los siguientes:

  1. Asignación de la página de hiperllamada al espacio de GPA de una VTL
  2. Determine el desplazamiento correcto para la secuencia de código (llamada o devolución de VTL).
  3. Ejecute la secuencia de código mediante CALL.

Protecciones de acceso a memoria

Una protección necesaria proporcionada por VSM es la capacidad de aislar los accesos a la memoria.

Las VTL más altas tienen un alto grado de control sobre el tipo de acceso a la memoria permitido por las VTL inferiores. Hay tres tipos básicos de protecciones que se pueden especificar mediante un VTL superior para una página GPA determinada: Lectura, Escritura y eXecute. Estos se definen en la tabla siguiente:

Nombre Descripción
Leer Controla si se permite el acceso de lectura a una página de memoria.
Escritura Controla si se permite el acceso de escritura a una página de memoria.
Execute Controla si se permiten capturas de instrucciones para una página de memoria.

Estos tres se combinan para los siguientes tipos de protección de memoria:

  1. Sin acceso
  2. Solo lectura, sin ejecución
  3. Solo lectura, ejecución
  4. Lectura y escritura, sin ejecutar
  5. Lectura y escritura, ejecución

Si está habilitado el "control de ejecución basado en modo (MBEC)", las protecciones de ejecución del modo de usuario y kernel se pueden establecer por separado.

Las VTL más altas pueden establecer la protección de memoria de un GPA a través de la hiperllamada HvCallModifyVtlProtectionMask .

Jerarquía de protección de memoria

Los permisos de acceso a memoria se pueden establecer mediante una serie de orígenes para un VTL determinado. Los permisos de cada VTL pueden restringirse potencialmente por una serie de otras VTL, así como por la partición de host. El orden en que se aplican las protecciones es el siguiente:

  1. Protecciones de memoria establecidas por el host
  2. Protecciones de memoria establecidas por VTL superiores

En otras palabras, las protecciones de VTL sustituyen a las protecciones de host. Las VTL de nivel superior sustituyen a las VTL de nivel inferior. Tenga en cuenta que es posible que un VTL no establezca permisos de acceso a memoria para sí mismo.

Se espera que una interfaz compatible no superponga ningún tipo de RAM sobre RAM.

Infracciones de acceso a memoria

Si una VP que se ejecuta en un VTL inferior intenta infringir una protección de memoria establecida por una VTL superior, se genera una interceptación. Esta interceptación la recibe el VTL superior que establece la protección. Esto permite que las VTL más altas se ocupen de la infracción por caso. Por ejemplo, el VTL superior puede optar por devolver un error o emular el acceso.

Control de ejecución basado en modo (MBEC)

Cuando una VTL coloca una restricción de memoria en un VTL inferior, puede que desee distinguir entre el modo de usuario y kernel al conceder un privilegio de "ejecución". Por ejemplo, si se realizaran comprobaciones de integridad de código en un VTL superior, la capacidad de distinguir entre el modo de usuario y el modo kernel significaría que un VTL podría aplicar la integridad de código solo para las aplicaciones en modo kernel.

Además de las tres protecciones de memoria tradicionales (lectura, escritura, ejecución), MBEC presenta una distinción entre el modo de usuario y el modo kernel para las protecciones de ejecución. Por lo tanto, si MBEC está habilitado, un VTL tiene la oportunidad de establecer cuatro tipos de protecciones de memoria:

Nombre Descripción
Leer Controla si se permite el acceso de lectura a una página de memoria.
Escritura Controla si se permite el acceso de escritura a una página de memoria.
Ejecución en modo de usuario (UMX) Controla si se permiten capturas de instrucciones generadas en modo de usuario para una página de memoria. NOTA: Si MBEC está deshabilitado, esta configuración se omite.
Ejecución del modo kernel (UMX) Controla si se permiten capturas de instrucciones generadas en modo kernel para una página de memoria. NOTA: Si MBEC está deshabilitado, esta configuración controla el modo de usuario y el modo kernel ejecutan accesos.

La memoria marcada con las protecciones "Ejecutar en modo de usuario" solo sería ejecutable cuando el procesador virtual se ejecuta en modo de usuario. Del mismo modo, la memoria "Ejecución en modo kernel" solo sería ejecutable cuando el procesador virtual se ejecuta en modo kernel.

KMX y UMX se pueden establecer de forma independiente para que los permisos de ejecución se apliquen de forma diferente entre el modo de usuario y kernel. Se admiten todas las combinaciones de UMX y KMX, excepto KMX=1, UMX=0. El comportamiento de esta combinación no está definido.

MBEC está deshabilitado de forma predeterminada para todas las VTL y procesadores virtuales. Cuando MBEC está deshabilitado, el modo kernel ejecuta bit determina la restricción de acceso a la memoria. Por lo tanto, si MBEC está deshabilitado, el código KMX=1 es ejecutable tanto en el kernel como en el modo de usuario.

Tablas de descriptores

Cualquier código en modo de usuario que tenga acceso a las tablas de descriptores debe estar en las páginas GPA marcadas como KMX=UMX=1. Las tablas de descriptores de acceso de software en modo de usuario desde una página de GPA marcada como KMX=0 no se admiten y se produce un error de protección general.

Configuración de MBEC

Para usar el control de ejecución basado en modo, debe habilitarse en dos niveles:

  1. Cuando la VTL está habilitada para una partición, MBEC debe estar habilitada mediante HvCallEnablePartitionVtl.
  2. MBEC debe configurarse por VP y por VTL mediante HvRegisterVsmVpSecureVtlConfig.

Interacción de MBEC con la prevención de ejecución del modo supervisor (SMEP)

Supervisor-Mode Prevención de ejecución (SMEP) es una característica de procesador compatible con algunas plataformas. SMEP puede afectar al funcionamiento de MBEC debido a su restricción de acceso de supervisor a las páginas de memoria. El hipervisor se adhiere a las siguientes directivas relacionadas con SMEP:

  • Si SMEP no está disponible para el sistema operativo invitado (ya sea debido a funcionalidades de hardware o modo de compatibilidad del procesador), MBEC no se ve afectado.
  • Si SMEP está disponible y está habilitado, MBEC funciona sin verse afectado.
  • Si SMEP está disponible y está deshabilitado, todas las restricciones de ejecución se rigen por el control KMX. Por lo tanto, solo se permitirá ejecutar el código marcado como KMX=1.

Aislamiento de estado del procesador virtual

Los procesadores virtuales mantienen estados independientes para cada VTL activo. Sin embargo, algunos de este estado son privados para un VTL determinado y el estado restante se comparte entre todas las VTL.

El hipervisor guarda el estado que se conserva por VTL (a.k.a. estado privado) en las transiciones de VTL. Si se inicia un conmutador VTL, el hipervisor guarda el estado privado actual para el VTL activo y, a continuación, cambia al estado privado del VTL de destino. El estado compartido permanece activo independientemente de los conmutadores de VTL.

Estado privado

En general, cada VTL tiene sus propios registros de control, registro RIP, registro RSP y MSR. A continuación se muestra una lista de registros y MSR específicos que son privados para cada VTL.

MSR privados:

  • SYSENTER_CS, SYSENTER_ESP, SYSENTER_EIP, STAR, LSTAR, CSTAR, SFMASK, EFER, PAT, KERNEL_GSBASE, FS. BASE, GS. BASE, TSC_AUX
  • HV_X64_MSR_HYPERCALL
  • HV_X64_MSR_GUEST_OS_ID
  • HV_X64_MSR_REFERENCE_TSC
  • HV_X64_MSR_APIC_FREQUENCY
  • HV_X64_MSR_EOI
  • HV_X64_MSR_ICR
  • HV_X64_MSR_TPR
  • HV_X64_MSR_APIC_ASSIST_PAGE
  • HV_X64_MSR_NPIEP_CONFIG
  • HV_X64_MSR_SIRBP
  • HV_X64_MSR_SCONTROL
  • HV_X64_MSR_SVERSION
  • HV_X64_MSR_SIEFP
  • HV_X64_MSR_SIMP
  • HV_X64_MSR_EOM
  • HV_X64_MSR_SINT0: HV_X64_MSR_SINT15
  • HV_X64_MSR_STIMER0_CONFIG: HV_X64_MSR_STIMER3_CONFIG
  • HV_X64_MSR_STIMER0_COUNT: HV_X64_MSR_STIMER3_COUNT
  • Registros APIC locales (incluido CR8/TPR)

Registros privados:

  • RIP, RSP
  • RFLAGS
  • CR0, CR3, CR4
  • DR7
  • IDTR, GDTR
  • CS, DS, ES, FS, GS, SS, TR, LDTR
  • TSC
  • DR6 (*dependiente del tipo de procesador. Lee el registro virtual HvRegisterVsmCapabilities para determinar el estado compartido o privado).

Estado compartido

Los VTL comparten el estado para reducir la sobrecarga de los contextos de conmutación. El estado de uso compartido también permite cierta comunicación necesaria entre las VTL. La mayoría de los registros de uso general y de punto flotante se comparten, al igual que la mayoría de las MSR arquitectónicas. A continuación se muestra la lista de msr y registros específicos que se comparten entre todas las VTL:

MSR compartidos:

  • HV_X64_MSR_TSC_FREQUENCY
  • HV_X64_MSR_VP_INDEX
  • HV_X64_MSR_VP_RUNTIME
  • HV_X64_MSR_RESET
  • HV_X64_MSR_TIME_REF_COUNT
  • HV_X64_MSR_GUEST_IDLE
  • HV_X64_MSR_DEBUG_DEVICE_OPTIONS
  • MTRR
  • MCG_CAP
  • MCG_STATUS

Registros compartidos:

  • Rax, Rbx, Rcx, Rdx, Rsi, Rdi, Rbp
  • CR2
  • R8 : R15
  • DR0 – DR5
  • Estado de punto flotante X87
  • Estado XMM
  • Estado de AVX
  • XCR0 (XFEM)
  • DR6 (*dependiente del tipo de procesador. Lee el registro virtual HvRegisterVsmCapabilities para determinar el estado compartido o privado).

Modo real

No se admite el modo real para cualquier VTL mayor que 0. Las VTL mayores que 0 se pueden ejecutar en modo de 32 o 64 bits.

Administración de interrupciones de VTL

Para lograr un alto nivel de aislamiento entre los niveles de confianza virtual, el modo seguro virtual proporciona un subsistema de interrupción independiente para cada VTL habilitado en un procesador virtual. Esto garantiza que un VTL pueda enviar y recibir interrupciones sin interferencias de un VTL menos seguro.

Cada VTL tiene su propio controlador de interrupción, que solo está activo si el procesador virtual se ejecuta en ese VTL determinado. Si un procesador virtual cambia los estados de VTL, también se cambia el controlador de interrupción activo en el procesador.

Una interrupción dirigida a un VTL que es mayor que el VTL activo provocará un conmutador VTL inmediato. A continuación, el VTL superior puede recibir la interrupción. Si el VTL superior no puede recibir la interrupción debido a su valor TPR/CR8, la interrupción se mantiene como "pendiente" y el VTL no cambia. Si hay varias VTL con interrupciones pendientes, el VTL más alto tiene prioridad (sin previo aviso a la VTL inferior).

Cuando una interrupción se dirige a una VTL inferior, la interrupción no se entrega hasta la próxima vez que el procesador virtual pase a la VTL de destino. Las IP de inicio e INIT destinadas a un VTL inferior se quitan en un procesador virtual con una VTL superior habilitada. Dado que INIT/SIPI está bloqueado, se debe usar la hiperllamada HvCallStartVirtualProcessor para iniciar procesadores.

RFLAGS. SI

Para el cambio de VTL, RFLAGS. SI no afecta a si una interrupción segura desencadena un conmutador VTL. Si RFLAGS. SI se borra para enmascarar las interrupciones, las interrupciones en VTL superiores seguirán causando un conmutador VTL a un VTL mayor. Solo se tiene en cuenta el valor de TPR/CR8 del VTL superior al decidir si se interrumpe inmediatamente.

Este comportamiento también afecta a las interrupciones pendientes en una devolución de VTL. Si el RFLAGS. Si el bit se borra para enmascarar las interrupciones en un VTL determinado y el VTL vuelve (a un VTL inferior), el hipervisor volverá a evaluar las interrupciones pendientes. Esto provocará una llamada inmediata al VTL superior.

Asistencia de notificación de interrupción virtual

Las VTL más altas se pueden registrar para recibir una notificación si bloquean la entrega inmediata de una interrupción a un VTL inferior del mismo procesador virtual. Las VTL más altas pueden habilitar Virtual Interrupt Notification Assist (VINA) a través de un registro virtual HvRegisterVsmVina:

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 Vector : 8;
        UINT64 Enabled : 1;
        UINT64 AutoReset : 1;
        UINT64 AutoEoi : 1;
        UINT64 ReservedP : 53;
    };
} HV_REGISTER_VSM_VINA;

Cada VTL de cada VP tiene su propia instancia de VINA, así como su propia versión de HvRegisterVsmVina. La instalación de la VINA generará una interrupción desencadenada por el borde al VTL más activo actualmente cuando una interrupción del VTL inferior está lista para la entrega inmediata.

Para evitar una inundación de interrupciones que se producen cuando esta instalación está habilitada, la instalación de la VINA incluye algún estado limitado. Cuando se genera una interrupción de la VINA, el estado de la instalación de la VINA cambia a "Aserción". El envío de un final de interrupción al SINT asociado a la instalación de la VINA no borrará el estado "Aserdo". El estado aserdo solo se puede borrar de una de estas dos maneras:

  1. El estado se puede borrar manualmente escribiendo en el campo VinaAsserted de la estructura HV_VP_VTL_CONTROL .
  2. El estado se borra automáticamente en la entrada siguiente al VTL si la opción "restablecer automáticamente en la entrada VTL" está habilitada en el registro HvRegisterVsmVina.

Esto permite que el código que se ejecuta en un VTL seguro solo se notifique de la primera interrupción que se recibe para un VTL inferior. Si un VTL seguro desea recibir una notificación de interrupciones adicionales, puede borrar el campo VinaAsserted de la página de asistencia vp, y se le notificará de la siguiente nueva interrupción.

Interceptaciones seguras

El hipervisor permite que un VTL mayor instale interceptaciones para los eventos que tienen lugar en el contexto de un VTL inferior. Esto proporciona a las VTL mayores un nivel elevado de control sobre los recursos de VTL inferiores. Las interceptaciones seguras se pueden usar para proteger los recursos críticos del sistema y evitar ataques de VTL inferiores.

Una interceptación segura se pone en cola en el VTL superior y ese VTL se ejecuta en la VP.

Tipos de interceptación segura

Tipo de interceptación Intercept se aplica a
Acceso a memoria Intento de acceder a las protecciones de GPA establecidas por un VTL superior.
Control del acceso de registro Intentando acceder a un conjunto de registros de control especificados por un VTL superior.

Interceptaciones anidadas

Varias VTL pueden instalar interceptaciones seguras para el mismo evento en un VTL inferior. Por lo tanto, se establece una jerarquía para decidir dónde se notifican las interceptaciones anidadas. La siguiente lista es el orden en el que se notifica la interceptación:

  1. VTL inferior
  2. VTL superior

Control de interceptaciones seguras

Una vez que se haya notificado una VTL de una interceptación segura, debe tomar medidas para que el VTL inferior pueda continuar. El VTL superior puede controlar la interceptación de varias maneras, entre las que se incluyen: insertar una excepción, simular el acceso o proporcionar un proxy al acceso. En cualquier caso, si es necesario modificar el estado privado de la VP de VTL inferior, se debe usar HvCallSetVpRegisters .

Interceptaciones de registro seguro

Un VTL mayor puede interceptar los accesos a determinados registros de control. Esto se logra estableciendo HvX64RegisterCrInterceptControl mediante la hiperllamada HvCallSetVpRegisters . Si se establece el bit de control en HvX64RegisterCrInterceptControl, se desencadenará una interceptación para cada acceso del registro de control correspondiente.

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 Cr0Write : 1;
        UINT64 Cr4Write : 1;
        UINT64 XCr0Write : 1;
        UINT64 IA32MiscEnableRead : 1;
        UINT64 IA32MiscEnableWrite : 1;
        UINT64 MsrLstarRead : 1;
        UINT64 MsrLstarWrite : 1;
        UINT64 MsrStarRead : 1;
        UINT64 MsrStarWrite : 1;
        UINT64 MsrCstarRead : 1;
        UINT64 MsrCstarWrite : 1;
        UINT64 ApicBaseMsrRead : 1;
        UINT64 ApicBaseMsrWrite : 1;
        UINT64 MsrEferRead : 1;
        UINT64 MsrEferWrite : 1;
        UINT64 GdtrWrite : 1;
        UINT64 IdtrWrite : 1;
        UINT64 LdtrWrite : 1;
        UINT64 TrWrite : 1;
        UINT64 MsrSysenterCsWrite : 1;
        UINT64 MsrSysenterEipWrite : 1;
        UINT64 MsrSysenterEspWrite : 1;
        UINT64 MsrSfmaskWrite : 1;
        UINT64 MsrTscAuxWrite : 1;
        UINT64 MsrSgxLaunchControlWrite : 1;
        UINT64 RsvdZ : 39;
    };
} HV_REGISTER_CR_INTERCEPT_CONTROL;

Registros de máscara

Para permitir un control más preciso, un subconjunto de registros de control también tiene registros de máscara correspondientes. Los registros de máscara se pueden usar para instalar interceptaciones en un subconjunto de los registros de control correspondientes. Cuando no se define un registro de máscara, cualquier acceso (definido por HvX64RegisterCrInterceptControl) desencadenará una interceptación.

El hipervisor admite los siguientes registros de máscara: HvX64RegisterCrInterceptCr0Mask, HvX64RegisterCrInterceptCr4Mask y HvX64RegisterCrInterceptIa32MiscEnableMask.

DMA y dispositivos

Los dispositivos tienen eficazmente el mismo nivel de privilegio que VTL0. Cuando VSM está habilitado, toda la memoria asignada por el dispositivo se marca como VTL0. Cualquier acceso DMA tiene los mismos privilegios que VTL0.