Modo de segurança virtual
O VSM (Modo de Segurança Virtual) é um conjunto de funcionalidades e iluminações do hipervisor oferecidos para partições de host e convidado, o que permite a criação e o gerenciamento de novos limites de segurança dentro do software do sistema operacional. O VSM é a instalação do hipervisor na qual Windows recursos de segurança, incluindo Device Guard, Credential Guard, TPMs virtuais e VMs blindadas são baseados. Esses recursos de segurança foram introduzidos em Windows 10 e Windows Server 2016.
O VSM permite que o software do sistema operacional nas partições raiz e convidado crie regiões isoladas de memória para armazenamento e processamento de ativos de segurança do sistema. O acesso a essas regiões isoladas é controlado e concedido exclusivamente por meio do hipervisor, que é uma parte altamente privilegiada e altamente confiável da TCB (Base de Computação Confiável) do sistema. Como o hipervisor é executado em um nível de privilégio mais alto do que o software do sistema operacional e tem controle exclusivo dos principais recursos de hardware do sistema, como controles de permissão de acesso à memória no MMU da CPU e no IOMMU no início da inicialização do sistema operacional, o hipervisor pode proteger essas regiões isoladas contra acesso não autorizado, mesmo do software do sistema operacional (por exemplo, kernel do sistema operacional e drivers de dispositivo) com acesso no modo supervisor (por exemplo, CPL0, por exemplo, ou "Anel 0").
Com essa arquitetura, mesmo que o software de nível normal do sistema em execução no modo de supervisor (por exemplo, kernel, drivers etc.) seja comprometido por software mal-intencionado, os ativos em regiões isoladas protegidas pelo hipervisor podem permanecer protegidos.
Nível de Confiança Virtual (VTL)
O VSM obtém e mantém o isolamento por meio de VTLs (Níveis de Confiança Virtual). As VTLs são habilitadas e gerenciadas tanto por partição quanto por processador virtual.
Os Níveis de Confiança Virtual são hierárquicos, com níveis mais altos sendo mais privilegiados do que níveis mais baixos. VTL0 é o nível menos privilegiado, com VTL1 sendo mais privilegiado que VTL0, VTL2 sendo mais privilegiado que VTL1, etc.
Na arquitetura, há suporte para até 16 níveis de VTLs; no entanto, um hipervisor pode optar por implementar menos de 16 VTLs. Atualmente, apenas duas VTLs são implementadas.
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 tem seu próprio conjunto de proteções de acesso à memória. Essas proteções de acesso são gerenciadas pelo hipervisor no espaço de endereço físico de uma partição e, portanto, não podem ser modificadas pelo software de nível do sistema em execução na partição.
Como VTLs com mais privilégios podem impor suas próprias proteções de memória, VTLs mais altas podem proteger efetivamente áreas de memória contra VTLs inferiores. Na prática, isso permite que uma VTL inferior proteja regiões de memória isoladas protegendo-as com uma VTL mais alta. Por exemplo, o VTL0 poderia armazenar um segredo no VTL1, momento em que somente VTL1 poderia acessá-lo. Mesmo que o VTL0 esteja comprometido, o segredo seria seguro.
Proteções VTL
Há várias facetas para alcançar o isolamento entre VTLs:
- Proteções de Acesso à Memória: cada VTL mantém um conjunto de proteções de acesso à memória física de convidado. O software em execução em uma VTL específica só pode acessar a memória de acordo com essas proteções.
- Estado do Processador Virtual: os processadores virtuais mantêm um estado separado por VTL. Por exemplo, cada VTL define um conjunto de registros de VP privado. O software em execução em uma VTL inferior não pode acessar o estado de registro do processador virtual privado da VTL superior.
- Interrupções: junto com um estado de processador separado, cada VTL também tem seu próprio subsistema de interrupção (APIC local). Isso permite que VTLs mais altas processem interrupções sem correr o risco de interferência de uma VTL inferior.
- Páginas de sobreposição: determinadas páginas de sobreposição são mantidas por VTL, de modo que VTLs mais altas tenham acesso confiável. Por exemplo, há uma página de sobreposição de hiperchamada separada por VTL.
Detecção e status do VSM
A funcionalidade VSM é anunciada para partições por meio do sinalizador de privilégio de partição do AccessVsm. Somente partições com todos os seguintes privilégios podem utilizar VSM: AccessVsm, AccessVpRegisters e AccessSynicRegs.
Detecção de funcionalidade do VSM
Os convidados devem usar o seguinte registro específico do modelo para acessar um relatório sobre os recursos do VSM:
Endereço MSR | Nome do Registro | Descrição |
---|---|---|
0x000D0006 | HV_X64_REGISTER_VSM_CAPABILITIES | Relatório sobre os recursos do VSM. |
O formato do MSR de Recursos do VSM de Registro é o seguinte:
Bits | Descrição | Atributos |
---|---|---|
63 | Dr6Shared | Ler |
62:47 | MbecVtlMask | Ler |
46 | DenyLowerVtlStartup | Ler |
45:0 | RsvdZ | Ler |
Dr6Shared indica ao convidado se o Dr6 é um registro compartilhado entre as VTLs.
MvecVtlMask indica ao convidado as VTLs para as quais o Mbec pode ser habilitado.
DenyLowerVtlStartup indica ao convidado se uma Vtl pode negar uma redefinição de VP por uma VTL inferior.
Registro de Status do VSM
Além de um sinalizador de privilégio de partição, dois registros virtuais podem ser usados para saber mais sobre o status do VSM: HvRegisterVsmPartitionStatus
e HvRegisterVsmVpStatus
.
HvRegisterVsmPartitionStatus
HvRegisterVsmPartitionStatus é um registro somente leitura por partição que é compartilhado em todas as VTLs. Esse registro fornece informações sobre quais VTLs foram habilitadas para a partição, que as VTLs têm controles de execução baseados em modo habilitados, bem como o máximo de VTL 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 é um registro somente leitura e é compartilhado em todas as VTLs. É um registro por VP, o que significa que cada processador virtual mantém sua própria instância. Esse registro fornece informações sobre quais VTLs foram habilitadas, que estão ativas, bem como o modo MBEC ativo em um 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 é a ID do contexto VTL que está atualmente ativo no processador virtual.
ActiveMbecEnabled especifica que o MBEC está ativo no processador virtual no momento.
EnabledVtlSet é um bitmap das VTLs habilitadas no processador virtual.
Estado inicial da VTL de partição
Quando uma partição é iniciada ou redefinida, ela começa a ser executada em VTL0. Todas as outras VTLs estão desabilitadas na criação da partição.
Habilitação VTL
Para começar a usar uma VTL, uma VTL inferior deve iniciar o seguinte:
- Habilite a VTL de destino para a partição. Isso torna a VTL geralmente disponível para a partição.
- Habilite a VTL de destino em um ou mais processadores virtuais. Isso disponibiliza a VTL para um VP e define seu contexto inicial. É recomendável que todos os VPs tenham as mesmas VTLs habilitadas. Ter uma VTL habilitada em alguns VPs (mas não todos) pode levar a um comportamento inesperado.
- Depois que a VTL estiver habilitada para uma partição e VP, ela poderá começar a definir as proteções de acesso depois que o sinalizador EnableVtlProtection for definido.
Observe que as VTLs não precisam ser consecutivas.
Habilitando uma VTL de destino para uma partição
A hiperchamada HvCallEnablePartitionVtl é usada para habilitar uma VTL para uma determinada partição. Observe que, antes que o software possa realmente ser executado em uma VTL específica, essa VTL deve ser habilitada em processadores virtuais na partição.
Habilitando uma VTL de destino para processadores virtuais
Depois que uma VTL estiver habilitada para uma partição, ela poderá ser habilitada nos processadores virtuais da partição. A hiperchamada HvCallEnableVpVtl pode ser usada para habilitar VTLs para um processador virtual, que define seu contexto inicial.
Os processadores virtuais têm um "contexto" por VTL. Se uma VTL for alternada, o estado privado da VTL também será alternado.
Configuração de VTL
Depois que uma VTL for habilitada, sua configuração poderá ser alterada por um VP em execução em uma VTL igual ou superior.
Configuração de Partição
Atributos de toda a partição podem ser configurados usando o registro HvRegisterVsmPartitionConfig. Há uma instância desse registro para cada VTL (maior que 0) em cada partição.
Cada VTL pode modificar sua própria instância de HV_REGISTER_VSM_PARTITION_CONFIG, bem como instâncias para VTLs inferiores. As VTLs podem não modificar esse registro para VTLs mais 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;
Os campos desse registro são descritos abaixo.
Habilitar proteções VTL
Depois que uma VTL tiver sido habilitada, o sinalizador EnableVtlProtection deverá ser definido antes de começar a aplicar proteções de memória. Esse sinalizador é gravado uma vez, o que significa que, depois de definido, ele não pode ser modificado.
Máscara de Proteção Padrão
Por padrão, o sistema aplica proteções RWX a todas as páginas mapeadas no momento e a quaisquer páginas futuras "hot-added". As páginas adicionadas quentes referem-se a qualquer memória adicionada a uma partição durante uma operação de redimensionamento.
Uma VTL mais alta pode definir uma política de proteção de memória padrão diferente especificando DefaultVtlProtectionMask em HV_REGISTER_VSM_PARTITION_CONFIG. Essa máscara deve ser definida no momento em que a VTL está habilitada. Ela não pode ser alterada depois de definida e só é desmarcada por uma redefinição de partição.
bit | Descrição |
---|---|
0 | Ler |
1 | Gravar |
2 | Executar modo kernel (KMX) |
3 | Execução do modo de usuário (UMX) |
Memória zero na redefinição
ZeroMemOnReset é um bit que controla se a memória é zero antes de uma partição ser redefinida. Essa configuração está ativada por padrão. Se o bit estiver definido, a memória da partição será zerada após a redefinição para que a memória de uma VTL mais alta não possa ser comprometida por uma VTL inferior. Se esse bit for limpo, a memória da partição não será zerada na redefinição.
DenyLowerVtlStartup
O sinalizador DenyLowerVtlStartup controla se um processador virtual pode ser iniciado ou redefinido por VTLs inferiores. Isso inclui maneiras arquitetônicas de redefinir um processador virtual (por exemplo, SIPI no X64), bem como a hipercall HvCallStartVirtualProcessor .
InterceptVpStartup
Se o sinalizador InterceptVpStartup estiver definido, iniciar ou redefinir um processador virtual gerará uma interceptação para a VTL superior.
Configurando VTLs inferiores
O seguinte registro pode ser usado por VTLs mais altas para configurar o comportamento de VTLs inferiores:
typedef union
{
UINT64 AsUINT64;
struct
{
UINT64 MbecEnabled : 1;
UINT64 TlbLocked : 1;
UINT64 ReservedZ : 62;
};
} HV_REGISTER_VSM_VP_SECURE_VTL_CONFIG;
Cada VTL (maior que 0) tem uma instância desse registro para cada VTL menor do que a própria. Por exemplo, o VTL2 teria duas instâncias desse registro : uma para VTL1 e outra para VTL0.
Os campos desse registro são descritos abaixo.
MbecEnabled
Esse campo configura se o MBEC está habilitado para a VTL inferior.
TlbLocked
Esse campo bloqueia o TLB da VTL inferior. Essa funcionalidade pode ser usada para impedir que VTLs inferiores causem invalidações de TLB que podem interferir em uma VTL mais alta. Quando esse bit é definido, todas as solicitações de liberação de espaço de endereço da VTL inferior são bloqueadas até que o bloqueio seja levantado.
Para desbloquear o TLB, a VTL mais alta pode limpar esse bit. Além disso, uma vez que um VP retorna para uma VTL inferior, ele libera todos os bloqueios TLB que ele contém no momento.
Entrada VTL
Uma VTL é "inserida" quando um VP alterna de uma VTL inferior para uma mais alta. Isso pode ocorrer pelos seguintes motivos:
- Chamada VTL: é quando o software deseja invocar explicitamente o código em uma VTL mais alta.
- Interrupção segura: se uma interrupção for recebida para uma VTL mais alta, o VP entrará na VTL mais alta.
- Interceptação segura: determinadas ações dispararão uma interrupção segura (acessando determinadas MSRs, por exemplo).
Depois que uma VTL é inserida, ela deve sair voluntariamente. Uma VTL mais alta não pode ser preemptada por uma VTL inferior.
Identificando o motivo da entrada VTL
Para reagir adequadamente a uma entrada, talvez uma VTL mais alta precise saber o motivo pelo qual ela foi inserida. Para discernir entre os motivos de entrada, a entrada VTL é incluída na estrutura de HV_VP_VTL_CONTROL .
Chamada VTL
Uma "chamada VTL" é quando uma VTL inferior inicia uma entrada em uma VTL mais alta (por exemplo, para proteger uma região de memória com a VTL superior) por meio da hiperchamada HvCallVtlCall .
As chamadas VTL preservam o estado dos registros compartilhados entre comutadores VTL. Os registros privados são preservados em um nível por VTL. A exceção a essas restrições são os registros exigidos pela sequência de chamadas VTL. Os seguintes registros são necessários para uma chamada VTL:
x64 | x86 | Descrição |
---|---|---|
RCX | EDX:EAX | Especifica uma entrada de controle de chamada VTL para o hipervisor |
RAX | ECX | Reservado |
Todos os bits na entrada de controle de chamada VTL estão reservados no momento.
Restrições de chamada VTL
As chamadas VTL só podem ser iniciadas no modo de processador mais privilegiado. Por exemplo, em sistemas x64, uma chamada VTL só pode vir de CPL0. Uma chamada VTL iniciada a partir de um modo de processador que é tudo menos o mais privilegiado no sistema resulta no hipervisor injetando uma exceção #UD no processador virtual.
Uma chamada VTL só pode alternar para a próxima VTL mais alta. Em outras palavras, se houver várias VTLs habilitadas, uma chamada não poderá "ignorar" uma VTL. As seguintes ações resultam em uma exceção #UD:
- Uma chamada VTL iniciada a partir de um modo de processador que é tudo menos o mais privilegiado no sistema (arquitetura específica).
- Uma chamada VTL do modo real (x86/x64)
- Uma chamada VTL em um processador virtual em que a VTL de destino está desabilitada (ou ainda não foi habilitada).
- Uma chamada VTL com um valor de entrada de controle inválido
Saída VTL
Uma opção para uma VTL inferior é conhecida como um "retorno". Depois que uma VTL tiver terminado o processamento, ela poderá iniciar um retorno de VTL para alternar para uma VTL inferior. A única maneira de um retorno de VTL ocorrer é se uma VTL mais alta iniciar voluntariamente uma. Uma VTL inferior nunca pode antecipar uma maior.
Retorno VTL
Um "retorno VTL" é quando uma VTL mais alta inicia uma opção para uma VTL inferior por meio da hipercall HvCallVtlReturn . Semelhante a uma chamada VTL, o estado do processador privado é alternado e o estado compartilhado permanece em vigor. Se a VTL inferior tiver chamado explicitamente para a VTL mais alta, o hipervisor incrementará o ponteiro de instrução da VTL mais alta antes que o retorno seja concluído para que ele possa continuar após uma chamada VTL.
Uma sequência de código de retorno VTL requer o uso dos seguintes registros:
x64 | x86 | Descrição |
---|---|---|
RCX | EDX:EAX | Especifica uma entrada de controle de retorno VTL para o hipervisor |
RAX | ECX | Reservado |
A entrada de controle de retorno VTL tem o seguinte formato:
Bits | Campo | Descrição |
---|---|---|
63:1 | RsvdZ | |
0 | Retorno rápido | Os registros não são restaurados |
As seguintes ações gerarão uma exceção #UD:
- Tentar um retorno VTL quando a VTL mais baixa estiver ativa no momento
- Tentativa de um retorno VTL com um valor de entrada de controle inválido
- Tentar um retorno de VTL de um modo de processador que é tudo menos o mais privilegiado no sistema (arquitetura específica)
Retorno Rápido
Como parte do processamento de um retorno, o hipervisor pode restaurar o estado de registro da VTL inferior da estrutura HV_VP_VTL_CONTROL . Por exemplo, depois de processar uma interrupção segura, uma VTL mais alta pode querer retornar sem interromper o estado inferior da VTL. Portanto, o hipervisor fornece um mecanismo para simplesmente restaurar os registros da VTL inferior para o valor de pré-chamada armazenado na estrutura de controle VTL.
Se esse comportamento não for necessário, uma VTL mais alta poderá usar um "retorno rápido". Um retorno rápido é quando o hipervisor não restaura o estado de registro da estrutura de controle. Isso deve ser utilizado sempre que possível para evitar o processamento desnecessário.
Esse campo pode ser definido com o bit 0 da entrada de retorno de VTL. Se estiver definido como 0, os registros serão restaurados da estrutura HV_VP_VTL_CONTROL. Se esse bit estiver definido como 1, os registros não serão restaurados (um retorno rápido).
Assistente de Página de Hiperchamada
O hipervisor fornece mecanismos para ajudar com chamadas VTL e retorna por meio da página de hiperchamada. Esta página abstrai a sequência de código específica necessária para alternar VTLs.
As sequências de código para executar chamadas E retornos VTL podem ser acessadas executando instruções específicas na página de hiperchamada. As partes de chamada/retorno estão localizadas em um deslocamento na página de hiperchamada determinada pelo registro virtual HvRegisterVsmCodePageOffset. Esse é um registro somente leitura e partição em todo o mundo, com uma instância separada por VTL.
Uma VTL pode executar uma chamada/retorno VTL usando a instrução CALL. Uma CHAMADA para o local correto na página de hiperchamada iniciará uma chamada/retorno de VTL.
typedef union
{
UINT64 AsUINT64;
struct
{
UINT64 VtlCallOffset : 12;
UINT64 VtlReturnOffset : 12;
UINT64 ReservedZ : 40;
};
} HV_REGISTER_VSM_CODE_PAGE_OFFSETS;
Para resumir, as etapas para chamar uma sequência de código usando a página de hiperchamada são as seguintes:
- Mapear a página de hiperchamada para o espaço GPA de uma VTL
- Determine o deslocamento correto para a sequência de código (chamada ou retorno VTL).
- Execute a sequência de código usando CALL.
Proteções de Acesso à Memória
Uma proteção necessária fornecida pelo VSM é a capacidade de isolar acessos de memória.
VTLs mais altas têm um alto grau de controle sobre o tipo de acesso à memória permitido por VTLs inferiores. Há três tipos básicos de proteções que podem ser especificados por uma VTL mais alta para uma página de GPA específica: Leitura, Gravação e eXecute. Elas são definidas na tabela a seguir:
Name | Descrição |
---|---|
Ler | Controla se o acesso de leitura tem permissão para uma página de memória |
Gravar | Controla se o acesso de gravação é permitido em uma página de memória |
Execute (executar) | Controla se buscas de instrução são permitidas para uma página de memória. |
Estes três combinam para os seguintes tipos de proteção de memória:
- Sem acesso
- Somente leitura, sem execução
- Somente leitura, executar
- Leitura/gravação, sem execução
- Leitura/gravação, executar
Se o "MBEC (controle de execução baseado em modo)" estiver habilitado, as proteções de execução do modo de usuário e kernel poderão ser definidas separadamente.
VTLs mais altas podem definir a proteção de memória para um GPA por meio da hiperchamada HvCallModifyVtlProtectionMask .
Hierarquia de Proteção de Memória
As permissões de acesso à memória podem ser definidas por várias fontes para uma VTL específica. As permissões de cada VTL podem potencialmente ser restritas por uma série de outras VTLs, bem como pela partição do host. A ordem na qual as proteções são aplicadas é a seguinte:
- Proteções de memória definidas pelo host
- Proteções de memória definidas por VTLs mais altas
Em outras palavras, as proteções VTL substituem as proteções de host. VTLs de nível superior substituem VTLs de nível inferior. Observe que uma VTL pode não definir permissões de acesso à memória para si mesma.
Espera-se que uma interface de conformidade não sobreponha nenhum tipo de RAM por RAM.
Violações de acesso à memória
Se um VP em execução em uma VTL inferior tentar violar uma proteção de memória definida por uma VTL mais alta, uma interceptação será gerada. Essa interceptação é recebida pela VTL mais alta que define a proteção. Isso permite que VTLs mais altas lidem com a violação caso a caso. Por exemplo, a VTL superior pode optar por retornar uma falha ou emular o acesso.
Controle de execução baseado em modo (MBEC)
Quando uma VTL coloca uma restrição de memória em uma VTL inferior, talvez ele queira fazer uma distinção entre o modo de usuário e kernel ao conceder um privilégio de "executar". Por exemplo, se as verificações de integridade de código ocorressem em uma VTL mais alta, a capacidade de distinguir entre o modo de usuário e o modo kernel significaria que uma VTL poderia impor a integridade do código apenas para aplicativos no modo kernel.
Além das três proteções de memória tradicionais (leitura, gravação, execução), o MBEC introduz uma distinção entre o modo de usuário e o modo kernel para executar proteções. Assim, se o MBEC estiver habilitado, uma VTL terá a oportunidade de definir quatro tipos de proteções de memória:
Name | Descrição |
---|---|
Ler | Controla se o acesso de leitura tem permissão para uma página de memória |
Gravar | Controla se o acesso de gravação é permitido em uma página de memória |
Execução do modo de usuário (UMX) | Controla se buscas de instrução geradas no modo de usuário são permitidas para uma página de memória. OBSERVAÇÃO: se o MBEC estiver desabilitado, essa configuração será ignorada. |
Executar o modo kernel (UMX) | Controla se buscas de instrução geradas no modo kernel são permitidas para uma página de memória. OBSERVAÇÃO: se o MBEC estiver desabilitado, essa configuração controlará os acessos de execução no modo de usuário e kernel. |
A memória marcada com as proteções "Executar no Modo de Usuário" só seria executável quando o processador virtual estiver em execução no modo de usuário. Da mesma forma, a memória "Kernel-Mode Execute" só seria executável quando o processador virtual estiver em execução no modo kernel.
KMX e UMX podem ser definidos independentemente para que as permissões de execução sejam impostas de forma diferente entre o modo de usuário e kernel. Há suporte para todas as combinações de UMX e KMX, exceto KMX=1, UMX=0. O comportamento dessa combinação é indefinido.
O MBEC é desabilitado por padrão para todas as VTLs e processadores virtuais. Quando o MBEC é desabilitado, o bit de execução no modo kernel determina a restrição de acesso à memória. Portanto, se o MBEC estiver desabilitado, o código KMX=1 será executável no kernel e no modo de usuário.
Tabelas de descritores
Qualquer código do modo de usuário que acesse tabelas de descritor deve estar em páginas GPA marcadas como KMX=UMX=1. O software do modo de usuário que acessa tabelas de descritor de uma página gpa marcada como KMX=0 não tem suporte e resulta em uma falha de proteção geral.
Configuração do MBEC
Para usar o controle de execução baseado em modo, ele deve ser habilitado em dois níveis:
- Quando a VTL está habilitada para uma partição, o MBEC deve ser habilitado usando HvCallEnablePartitionVtl
- O MBEC deve ser configurado por VP e por VTL, usando HvRegisterVsmVpSecureVtlConfig.
Interação do MBEC com o Supervisor Mode Execution Prevention (SMEP)
Supervisor-Mode Prevenção de Execução (SMEP) é um recurso de processador com suporte em algumas plataformas. O SMEP pode afetar a operação do MBEC devido à restrição de acesso do supervisor às páginas de memória. O hipervisor adere às seguintes políticas relacionadas ao SMEP:
- Se o SMEP não estiver disponível para o sistema operacional convidado (seja por causa de recursos de hardware ou modo de compatibilidade do processador), o MBEC funcionará sem ser afetado.
- Se o SMEP estiver disponível e estiver habilitado, o MBEC funcionará sem ser afetado.
- Se o SMEP estiver disponível e estiver desabilitado, todas as restrições de execução serão regidas pelo controle KMX. Portanto, somente o código marcado como KMX=1 poderá ser executado.
Isolamento de estado do processador virtual
Os processadores virtuais mantêm estados separados para cada VTL ativa. No entanto, parte desse estado é privado para uma VTL específica e o estado restante é compartilhado entre todas as VTLs.
O estado que é preservado por VTL (também conhecido como estado privado) é salvo pelo hipervisor em transições VTL. Se uma opção VTL for iniciada, o hipervisor salvará o estado privado atual da VTL ativa e alternar para o estado privado da VTL de destino. O estado compartilhado permanece ativo, independentemente das opções de VTL.
Estado Privado
Em geral, cada VTL tem seus próprios registros de controle, registro RIP, registro RSP e MSRs. Abaixo está uma lista de registros específicos e MSRs que são privados para cada VTL.
MSRs 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 locais (incluindo CR8/TPR)
Registros privados:
- RIP, RSP
- RFLAGS
- CR0, CR3, CR4
- DR7
- IDTR, GDTR
- CS, DS, ES, FS, GS, SS, TR, LDTR
- TSC
- DR6 (*dependente do tipo de processador. Ler o registro virtual HvRegisterVsmCapabilities para determinar o status compartilhado/privado)
Estado Compartilhado
As VTLs compartilham o estado para reduzir a sobrecarga de contextos de comutação. O compartilhamento de estado também permite alguma comunicação necessária entre VTLs. A maioria dos registros de ponto flutuante e de finalidade geral são compartilhados, assim como a maioria das MSRs arquitetônicas. Abaixo está a lista de MSRs e registros específicos que são compartilhados entre todas as VTLs:
MSRs compartilhadas:
- 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
- MTRRs
- MCG_CAP
- MCG_STATUS
Registros compartilhados:
- Rax, Rbx, Rcx, Rdx, Rsi, Rdi, Rbp
- CR2
- R8 – R15
- DR0 – DR5
- Estado do ponto flutuante X87
- Estado do XMM
- Estado AVX
- XCR0 (XFEM)
- DR6 (*dependente do tipo de processador. Ler o registro virtual HvRegisterVsmCapabilities para determinar o status compartilhado/privado)
Modo Real
O modo real não tem suporte para nenhuma VTL maior que 0. VTLs maiores que 0 podem ser executadas no modo de 32 bits ou 64 bits.
Gerenciamento de interrupção VTL
Para obter um alto nível de isolamento entre níveis de confiança virtual, o Modo de Segurança Virtual fornece um subsistema de interrupção separado para cada VTL habilitada em um processador virtual. Isso garante que uma VTL seja capaz de enviar e receber interrupções sem interferência de uma VTL menos segura.
Cada VTL tem seu próprio controlador de interrupção, que só estará ativo se o processador virtual estiver em execução nessa VTL específica. Se um processador virtual alternar estados VTL, o controlador de interrupção ativo no processador também será alternado.
Uma interrupção direcionada a uma VTL maior que a VTL ativa causará uma opção VTL imediata. A VTL mais alta pode receber a interrupção. Se a VTL mais alta não puder receber a interrupção devido ao seu valor TPR/CR8, a interrupção será mantida como "pendente" e a VTL não mudará. Se houver várias VTLs com interrupções pendentes, a VTL mais alta terá precedência (sem aviso prévio para a VTL inferior).
Quando uma interrupção é direcionada a uma VTL inferior, a interrupção não é entregue até a próxima vez que o processador virtual fizer a transição para a VTL de destino. INIT e IPIs de inicialização direcionados a uma VTL inferior são descartados em um processador virtual com uma VTL mais alta habilitada. Como INIT/SIPI está bloqueado, a hiperchamada HvCallStartVirtualProcessor deve ser usada para iniciar processadores.
RFLAGS. SE
Para fins de alternar VTLs, RFLAGS. SE não afetar se uma interrupção segura dispara um comutador VTL. Se RFLAGS. SE estiver limpo para mascarar interrupções, as interrupções em VTLs mais altas ainda causarão uma opção de VTL para uma VTL mais alta. Somente o valor TPR/CR8 da VTL maior é levado em conta ao decidir se deve interromper imediatamente.
Esse comportamento também afeta interrupções pendentes após um retorno de VTL. Se o RFLAGS. Se o bit for limpo para mascarar interrupções em uma determinada VTL e a VTL retornar (para uma VTL inferior), o hipervisor reavaliará as interrupções pendentes. Isso fará com que uma chamada imediata volte para a VTL mais alta.
Assistência de Notificação de Interrupção Virtual
VTLs mais altas podem se registrar para receber uma notificação se estiverem bloqueando a entrega imediata de uma interrupção para uma VTL inferior do mesmo processador virtual. VTLs mais altas podem habilitar o VINA (Virtual Interrupt Notification Assist) por meio de um 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 em cada VP tem sua própria instância VINA, bem como sua própria versão de HvRegisterVsmVina. A instalação VINA gerará uma interrupção disparada de borda para a VTL mais alta atualmente ativa quando uma interrupção para a VTL inferior estiver pronta para entrega imediata.
Para evitar que uma inundação de interrupções ocorra quando essa instalação está habilitada, a instalação VINA inclui algum estado limitado. Quando uma interrupção VINA é gerada, o estado da instalação VINA é alterado para "Declarado". O envio de uma interrupção final para o SINT associado à instalação VINA não limpará o estado "Declarado". O estado declarado só pode ser limpo de uma das duas maneiras:
- O estado pode ser limpo manualmente escrevendo no campo VinaAsserted da estrutura HV_VP_VTL_CONTROL .
- O estado será desmarcado automaticamente na próxima entrada para a VTL se a opção "redefinir automaticamente na entrada VTL" estiver habilitada no registro HvRegisterVsmVina.
Isso permite que o código em execução em uma VTL segura seja notificado apenas da primeira interrupção recebida para uma VTL inferior. Se uma VTL segura quiser ser notificada de interrupções adicionais, ela poderá limpar o campo VinaAsserted da página de assistência de VP e será notificado da próxima nova interrupção.
Interceptações Seguras
O hipervisor permite que uma VTL mais alta instale interceptações para eventos que ocorrem no contexto de uma VTL inferior. Isso fornece às VTLs mais altas um nível elevado de controle sobre recursos de VTL inferior. Interceptações seguras podem ser usadas para proteger recursos críticos do sistema e evitar ataques de VTLs inferiores.
Uma interceptação segura é enfileirada na VTL superior e essa VTL é executada no VP.
Tipos de interceptação segura
Tipo de interceptação | Interceptação se aplica a |
---|---|
Acesso à memória | Tentando acessar as proteções de GPA estabelecidas por uma VTL mais alta. |
Controlar o acesso ao registro | Tentando acessar um conjunto de registros de controle especificados por uma VTL mais alta. |
Interceptações Aninhadas
Várias VTLs podem instalar interceptações seguras para o mesmo evento em uma VTL inferior. Assim, uma hierarquia é estabelecida para decidir onde as interceptações aninhadas são notificadas. A lista a seguir é a ordem de onde a interceptação é notificada:
- VTL inferior
- VTL superior
Manipulando interceptações seguras
Depois que uma VTL for notificada de uma interceptação segura, ela deverá tomar medidas para que a VTL inferior possa continuar. A VTL superior pode lidar com a interceptação de várias maneiras, incluindo: injetar uma exceção, emular o acesso ou fornecer um proxy para o acesso. De qualquer forma, se o estado privado do VP de VTL inferior precisar ser modificado, HvCallSetVpRegisters deverá ser usado.
Interceptações de Registro Seguro
Uma VTL mais alta pode interceptar em acessos a determinados registros de controle. Isso é obtido definindo HvX64RegisterCrInterceptControl usando a hipercall HvCallSetVpRegisters . Definir o bit de controle em HvX64RegisterCrInterceptControl disparará uma interceptação para cada acesso do registro de controle correspondente.
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 um controle mais refinado, um subconjunto de registros de controle também tem registros de máscara correspondentes. Os registros de máscara podem ser usados para instalar interceptações em um subconjunto dos registros de controle correspondentes. Quando um registro de máscara não é definido, qualquer acesso (conforme definido por HvX64RegisterCrInterceptControl) disparará uma interceptação.
O hipervisor dá suporte aos seguintes registros de máscara: HvX64RegisterCrInterceptCr0Mask, HvX64RegisterCrInterceptCr4Mask e HvX64RegisterCrInterceptIa32MiscEnableMask.
DMA e dispositivos
Os dispositivos têm efetivamente o mesmo nível de privilégio que o VTL0. Quando o VSM está habilitado, toda a memória alocada pelo dispositivo é marcada como VTL0. Todos os acessos DMA têm os mesmos privilégios que o VTL0.