Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
O Modo Seguro Virtual (VSM) é um conjunto de recursos e iluminações do hipervisor oferecidos para partições de host e convidado que permite a criação e o gerenciamento de novos limites de segurança dentro do software do sistema operacional. O VSM é o recurso de hipervisor no qual se baseiam os recursos de segurança do Windows, incluindo o Device Guard, o Credential Guard, TPMs virtuais e VMs blindadas. Esses recursos de segurança foram introduzidos no Windows 10 e no Windows Server 2016.
O VSM permite que o software do sistema operacional nas partições raiz e convidada 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 confiável da Base de Computação Confiável (TCB) 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 na MMU da CPU e IOMMU no início da inicialização do sistema, o hipervisor pode proteger essas regiões isoladas contra acesso não autorizado, até mesmo de software do sistema operacional (por exemplo, kernel do sistema operacional e drivers de dispositivo) com acesso ao modo supervisor (ou seja, CPL0, ou "Anel 0").
Com essa arquitetura, mesmo que o software normal no nível do sistema em execução no modo 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 alcança e mantém o isolamento por meio de VTLs (Virtual Trust Levels). As VTLs são habilitadas e gerenciadas por partição e por processador virtual.
Os Níveis de Confiança Virtual são hierárquicos, sendo os níveis mais elevados mais privilegiados do que os níveis inferiores. VTL0 é o nível menos privilegiado, com VTL1 sendo mais privilegiado que VTL0, VTL2 sendo mais privilegiado que VTL1, etc.
Arquitetonicamente, até 16 níveis de VTLs são suportados; no entanto, um hipervisor pode optar por implementar menos de 16 VTL's. 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çamento físico de uma partição e, portanto, não podem ser modificadas pelo software no nível do sistema em execução na partição.
Como VTLs mais privilegiadas podem impor suas próprias proteções de memória, VTLs mais altas podem proteger efetivamente áreas de memória de VTLs mais baixas. Na prática, isso permite que uma VTL mais baixa proteja regiões de memória isoladas, protegendo-as com uma VTL mais alta. Por exemplo, VTL0 poderia armazenar um segredo em VTL1, momento em que apenas VTL1 poderia acessá-lo. Mesmo que o VTL0 seja comprometido, o segredo seria seguro.
Proteções VTL
Existem 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 convidada. O software executado 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 VP privados. O software executado em uma VTL inferior não pode acessar o estado de registro do processador virtual privado da VTL superior.
- Interrupções: Para além de um estado separado do processador, cada VTL tem também o seu próprio subsistema de interrupções (APIC local em x64, interface GIC CPU em ARM64). Isso permite que VTLs mais altas processem interrupções sem o risco de interferência de uma VTL mais baixa.
- Páginas de sobreposição: certas 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.
Deteção e status do VSM
O recurso VSM é anunciado para partições através do sinalizador de privilégio de partição AccessVsm. Somente partições com todos os seguintes privilégios podem utilizar o VSM: AccessVsm, AccessVpRegisters e AccessSynicRegs.
Deteção de capacidade VSM
Os convidados podem aceder a um relatório sobre as capacidades VSM através de um registo sintético.
Em plataformas x64
Em plataformas x64, este registo é acedido através de um MSR:
| Endereço MSR | Nome do Registo | Description |
|---|---|---|
| 0x000D0006 | HV_X64_REGISTER_VSM_CAPABILITIES | Relatório sobre os recursos do VSM. |
Nas plataformas ARM64
Nas plataformas ARM64, este registo é acedido através do HvRegisterVsmCapabilities usando a hiperchamada HvCallGetVpRegisters.
Formato do Registo
Em plataformas x64
| Bits | Description | Attributes |
|---|---|---|
| 63 | Dr6Compartilhado | Leitura |
| 62:47 | MbecVtlMask | Leitura |
| 46 | DenyLowerVtlStartup | Leitura |
| 45:0 | RsvdZ | Leitura |
Nas plataformas ARM64
| Bits | Description | Attributes |
|---|---|---|
| 63 | RsvdZ | Leitura |
| 62:47 | MbecVtlMask | Leitura |
| 46 | DenyLowerVtlStartup | Leitura |
| 45:0 | RsvdZ | Leitura |
Descrições de Campos
Dr6Shared (apenas x64): Indica ao convidado se o Dr6 é um registo partilhado entre os VTLs.
MbecVtlMask: Indica ao convidado os VTLs para os quais o MBEC pode ser ativado.
DenyLowerVtlStartup: Indica ao convidado se um VTL pode negar um reset de VP por um VTL mais baixo.
Registro de status do VSM
Além de um sinalizador de privilégio de partição, dois registros virtuais podem ser usados para obter informações adicionais 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, quais VTLs têm Controles de Execução Baseados em Modo habilitados, bem como a VTL máxima permitida.
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, qual está ativa, bem como o modo MBEC ativo em uma 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á atualmente ativo no processador virtual.
EnabledVtlSet é um bitmap das VTLs que estão habilitadas no processador virtual.
Estado inicial da VTL da partição
Quando uma partição é iniciada ou redefinida, ela começa a ser executada em VTL0. Todas as outras VTLs são desativadas na criação de partições.
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 torna a VTL disponível para um VP e define seu contexto inicial. Recomenda-se que todos os VPs tenham as mesmas VTLs habilitadas. Ter uma VTL ativada em alguns VPs (mas não em 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 proteções de acesso assim 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
Uma vez que uma VTL é habilitada para uma partição, ela pode 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 trocada, o estado privado da VTL também será trocado.
Configuração VTL
Uma vez que uma VTL tenha sido habilitada, sua configuração pode ser alterada por um VP em execução em uma VTL igual ou superior.
Configuração de partição
Os 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 mais baixas. VTLs não podem 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 deste registo são descritos abaixo.
Ativar proteções VTL
Depois que uma VTL for habilitada, o sinalizador EnableVtlProtection deve ser definido antes de começar a aplicar proteções de memória. Este sinalizador é write-once, o que significa que, uma vez definido, 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 atualmente e a quaisquer futuras páginas "adicionadas a quente". As páginas adicionadas a quente 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 no HV_REGISTER_VSM_PARTITION_CONFIG. Essa máscara deve ser definida no momento em que a VTL estiver ativada. Ele não pode ser alterado uma vez que é definido, e só é limpo por uma redefinição de partição.
| Pouco | Description |
|---|---|
| 0 | Leitura |
| 1 | Escreve |
| 2 | Execução do modo kernel (KMX) |
| 3 | Modo de Execução do Usuário (UMX) |
Memória zero na reposição
ZeroMemOnReset é um bit que controla se a memória é zerada 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 mais baixa. 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 em X64), bem como a hiperchamada HvCallStartVirtualProcessor .
InterceptVpStartup
Se o sinalizador InterceptVpStartup estiver definido, iniciar ou redefinir um processador virtual gerará uma intercetação para a VTL superior.
Configurando VTLs inferiores
O registro a seguir pode ser usado por VTLs mais altas para configurar o comportamento de VTLs mais baixas:
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) tem uma instância deste registo para cada VTL inferior a si própria. Por exemplo, VTL2 teria duas instâncias desse registro – uma para VTL1 e uma segunda para VTL0.
Os campos deste registo são descritos abaixo.
MbecHabilitado
Este campo configura se o MBEC está habilitado para a VTL inferior.
TlbBloqueado
Este campo bloqueia o TLB da VTL inferior. Esse recurso pode ser usado para evitar que VTLs mais baixas causem invalidações de TLB que podem interferir com 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, o VTL mais alto pode limpar esse bit. Além disso, uma vez que um VP retorna a uma VTL mais baixa, ele libera todos os bloqueios TLB que mantém no momento.
Entrada VTL
Uma VTL é "inserida" quando um VP muda de uma VTL inferior para uma superior. Isso pode acontecer pelos seguintes motivos:
- Chamada VTL: é quando o software deseja explicitamente invocar 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.
- Intercetação segura: certas ações desencadearão uma interrupção segura (acessando certas MSRs, por exemplo).
Uma vez que um VTL é inserido, ele deve sair voluntariamente. Um VTL mais alto não pode ser antecipado por um VTL mais baixo.
Identificando o motivo da entrada de VTL
Para reagir adequadamente a uma entrada, um VTL mais alto pode precisar saber o motivo pelo qual ela foi inserida. Para discernir entre os motivos de entrada, a entrada VTL está incluída na estrutura HV_VP_VTL_CONTROL .
Chamada VTL
Uma "chamada VTL" ocorre quando um VTL inferior inicia uma entrada num VTL superior (por exemplo, para proteger uma região de memória com o VTL superior) através da hiperchamada HvCallVtlCall .
As chamadas VTL preservam o estado dos registros compartilhados entre switches VTL. Os registos privados são preservados a um nível por VTL. A exceção a estas restrições são os registos/instruções exigidos pela sequência de chamadas VTL.
Em plataformas x64
Os seguintes registos são necessários para uma chamada VTL em x64:
| x64 | x86 | Description |
|---|---|---|
| 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 atualmente reservados.
Nas plataformas ARM64
Nas plataformas ARM64, uma chamada VTL é iniciada usando a instrução HVC com valor imediato 2. O hipervisor decodifica este valor imediato específico e processa-o como uma hiperchamada HvCallVtlCall. Não é necessário um estado específico de registo para a entrada de controlo no ARM64.
Restrições de chamada VTL
As chamadas VTL só podem ser iniciadas a partir do modo de processador mais privilegiado. Por exemplo, em sistemas x64 uma chamada VTL só pode vir do CPL0, e em sistemas ARM64 do EL1. Uma chamada VTL iniciada a partir de um modo processador que está longe de ser o mais privilegiado do sistema resulta no hipervisor a injetar uma exceção no processador virtual (#UD em x64, exceção de instrução indefinida no ARM64).
Uma chamada VTL só pode alternar para a próxima VTL mais alta. Ou seja, se houver vários VTLs ativados, uma chamada não pode "saltar" um VTL. As seguintes ações resultam numa exceção (#UD em x64, instrução indefinida em ARM64):
- Uma chamada VTL iniciada a partir de um modo de processador que é tudo menos o mais privilegiado no sistema (específico da arquitetura).
- Uma chamada VTL a partir do modo real (apenas x86/x64)
- Uma chamada VTL em um processador virtual onde 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 mudança para uma VTL inferior é conhecida como "retorno". Uma vez que um VTL tenha terminado o processamento, ele pode iniciar um retorno VTL para alternar para um VTL mais baixo. A única maneira de ocorrer um retorno de VTL é se um VTL mais alto iniciar voluntariamente um. Um VTL mais baixo nunca pode antecipar um VTL mais alto.
Retorno VTL
Um "retorno VTL" é quando um VTL mais alto inicia um switch para um VTL mais baixo através da chamada HvCallVtlReturn . Semelhante a uma chamada VTL, o estado do processador privado é trocado e o estado compartilhado permanece em vigor. Se o VTL mais baixo tiver explicitamente chamado para o VTL superior, o hipervisor incrementa o ponteiro de instrução do VTL superior antes de o retorno estar completo, para que possa continuar após uma chamada VTL.
Em plataformas x64
Uma sequência de códigos de retorno VTL em x64 requer a utilização dos seguintes registos:
| x64 | x86 | Description |
|---|---|---|
| RCX | EDX: EAX | Especifica uma entrada de controle de retorno VTL para o hipervisor |
| RAX | ECX | Reservado |
Nas plataformas ARM64
Nas plataformas ARM64, um retorno VTL é iniciado usando a instrução HVC com valor imediato 3. O hipervisor decodifica este valor imediato específico e processa-o como uma hiperchamada HvCallVtlReturn.
Entrada de Controlo de Retorno VTL
A entrada de controle de retorno VTL tem o seguinte formato:
| Bits | Campo | Description |
|---|---|---|
| 63:1 | RsvdZ | |
| 0 | Retorno rápido | Os registos não são restaurados |
As seguintes ações gerarão uma #UD exceção:
- Tentando um retorno VTL quando a VTL mais baixa está ativa no momento
- Tentando um retorno VTL com um valor de entrada de controle inválido
- Tentando um retorno VTL de um modo de processador que é tudo menos o mais privilegiado no sistema (específico da arquitetura)
Retorno Rápido
Como parte do processamento de um retorno, o hipervisor pode restaurar o estado de registro da VTL inferior a partir da estrutura HV_VP_VTL_CONTROL . Por exemplo, depois de processar uma interrupção segura, uma VTL mais alta pode desejar retornar sem interromper o estado da VTL inferior. Portanto, o hipervisor fornece um mecanismo para simplesmente restaurar os registradores da VTL inferior ao seu valor de pré-chamada armazenado na estrutura de controle da VTL.
Se esse comportamento não for necessário, um VTL mais alto pode usar um "retorno rápido". Um retorno rápido é quando o hipervisor não restaura o estado do registro da estrutura de controle. Isso deve ser utilizado sempre que possível para evitar processamento desnecessário.
Este campo pode ser definido com o bit 0 da entrada de retorno VTL. Se estiver definido como 0, os registos são restaurados a partir da estrutura HV_VP_VTL_CONTROL. Se esse bit estiver definido como 1, os registros não serão restaurados (um retorno rápido).
Hypercall Page Assist (apenas x64)
Nas plataformas x64, o hipervisor fornece mecanismos para ajudar com chamadas e retornos VTL através da página de hiperchamadas. 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 VTL e retornos podem ser acessadas executando instruções específicas na página hypercall. Os blocos de chamada/retorno estão localizados em um deslocamento na página de hiperchamada determinada pelo registro virtual HvRegisterVsmCodePageOffsets. Este é um registro somente leitura e em toda a partição, 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 VTL.
typedef union
{
UINT64 AsUINT64;
struct
{
UINT64 VtlCallOffset : 12;
UINT64 VtlReturnOffset : 12;
UINT64 ReservedZ : 40;
};
} HV_REGISTER_VSM_CODE_PAGE_OFFSETS;
Para resumir, os passos para chamar uma sequência de código usando a página de hypercall no x64 são os seguintes:
- Mapeie a página de hypercall para o espaço de GPA de um 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.
Nota: Nas plataformas ARM64, as chamadas e retornos VTL são realizados diretamente usando a instrução HVC com valores imediatos específicos (2 para chamada VTL, 3 para retorno VTL), conforme descrito nas secções de Chamada VTL e Retorno VTL. O registo HvRegisterVsmCodePageOffsets não está disponível no ARM64.
Proteções de acesso à memória
Uma proteção necessária fornecida pelo VSM é a capacidade de isolar acessos à memória.
VTLs mais altas têm um alto grau de controle sobre o tipo de acesso à memória permitido por VTLs mais baixas. Há três tipos básicos de proteções que podem ser especificados por uma VTL mais alta para uma página GPA específica: Leitura, Gravação e eXecute. Estes são definidos no quadro seguinte:
| Nome | Description |
|---|---|
| Leitura | Controla se o acesso de leitura é permitido a uma página de memória |
| Escreve | Controla se o acesso de gravação permitido a uma página de memória |
| Execute | Controla se as buscas de instruções são permitidas para uma página de memória. |
Estes três combinam-se 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
- Ler/escrever, executar
Se o "controle de execução baseado em modo (MBEC)" estiver habilitado, as proteções de execução do modo kernel e do usuário podem 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 ser potencialmente restringidas por várias outras VTLs, bem como pela partição do host. A ordem pela 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 do host. As VTLs de nível superior substituem as 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 conforme não sobreponha nenhum tipo que não seja de RAM sobre a RAM.
Violações de acesso à memória
Se um VP em execução em uma VTL mais baixa tentar violar uma proteção de memória definida por uma VTL mais alta, uma intercetação será gerada. Esta interceção é recebida pelo VTL superior que define a proteção. Isso permite que VTLs mais altas lidem com a violação caso a caso. Por exemplo, a VTL mais alta 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, ela pode querer fazer uma distinção entre o modo de usuário e o modo kernel ao conceder um privilégio de "execução". Por exemplo, se as verificações de integridade de código ocorressem em uma VTL mais alta, a capacidade de distinguir entre modo de usuário e modo kernel significaria que uma VTL poderia impor a integridade do código apenas para aplicativos de modo kernel.
Além das três proteções de memória tradicionais (ler, escrever, executar), o MBEC introduz uma distinção entre modo de usuário e modo kernel para proteções de execução. Assim, se o MBEC estiver habilitado, uma VTL tem a oportunidade de definir quatro tipos de proteções de memória:
| Nome | Description |
|---|---|
| Leitura | Controla se o acesso de leitura é permitido a uma página de memória |
| Escreve | Controla se o acesso de gravação permitido a uma página de memória |
| Modo de Execução do Usuário (UMX) | Controla se as buscas de instruções geradas no modo de usuário são permitidas para uma página de memória. NOTA: Se o MBEC estiver desativado, esta definição é ignorada. |
| Execução do modo kernel (KMX) | Controla se as buscas de instruções geradas no modo kernel são permitidas para uma página de memória. NOTA: Se o MBEC estiver desativado, esta definição controla os acessos de execução em modo de utilizador e em modo kernel. |
A memória marcada com as proteções "User-Mode Execute" só seria executável quando o processador virtual estivesse 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 estivesse em execução no modo kernel.
KMX e UMX podem ser definidos de forma independente, de modo que as permissões de execução sejam impostas de forma diferente entre o modo de usuário e kernel. Todas as combinações de UMX e KMX são suportadas, exceto KMX=1, UMX=0. O comportamento desta combinação é indefinido.
O MBEC é desativado por padrão para todas as VTLs e processadores virtuais. Quando o MBEC está desativado, o bit de execução do modo kernel determina a restrição de acesso à memória. Assim, se o MBEC estiver desativado, o código KMX=1 será executável no kernel e no modo de usuário.
Tabelas de Descritores (apenas x64)
Em plataformas x64, qualquer código em modo utilizador que aceda a tabelas de descritores deve estar em páginas GPA marcadas como KMX=UMX=1. O software de modo de utilizador que acede a tabelas de descritores a partir de uma página GPA marcada KMX=0 não é suportado e resulta numa falha de proteção geral.
O ARM64 não utiliza tabelas de descritores ao estilo x86; as permissões de acesso à memória são controladas através de entradas da tabela de tradução e do modelo de privilégios baseado em EL.
Configuração do MBEC
Para fazer uso do 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 HvRegisterVsmVpSecureConfigVtlX.
Interação MBEC com Prevenção de Execução em Modo Supervisor (SMEP) (apenas x64)
Supervisor-Mode Prevenção de Execução (SMEP) é uma funcionalidade de processador suportada em plataformas x64. O SMEP pode afetar o funcionamento do MBEC devido à sua 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 SO convidado (seja devido às capacidades de hardware ou ao modo de compatibilidade do processador), o MBEC funciona sem ser afetado.
- Se o SMEP estiver disponível e habilitado, o MBEC não será afetado.
- Se o SMEP estiver disponível e desativado, todas as restrições de execução serão regidas pelo controle KMX. Assim, apenas o código marcado KMX=1 terá permissão para ser executado.
Isolamento do estado do processador virtual
Os processadores virtuais mantêm estados separados para cada VTL ativa. No entanto, parte deste estado é privado de uma VTL específica, e o estado restante é compartilhado entre todas as VTLs.
O hipervisor mantém um contexto por VTL para cada processador virtual, armazenando todo o estado do processador visível pelos convidados que deve ser isolado entre VTLs. Cada VTL tem a sua própria instância de estado privado, incluindo registos de controlo, vetores de exceção, registos de configuração do sistema e registos sintéticos de hipervisores. Este armazenamento por VTL assegura o isolamento completo do contexto de execução ao alternar entre níveis de confiança.
O estado que é preservado por VTL (também conhecido como estado privado) é salvo pelo hipervisor nas transições VTL. Se um comutador VTL for iniciado, o hipervisor salvará o estado privado atual para a VTL ativa e, em seguida, alternará para o estado privado da VTL de destino. O estado compartilhado permanece ativo independentemente dos comutadores VTL.
Estado privado
Cada VTL mantém o seu próprio contexto completo de execução, composto por:
- Estado de Execução: Ponteiro de instrução (PC/RIP), ponteiro de pilha (SP/RSP), flags do processador (PSTATE/RFLAGS)
- Registos de Controlo: Configuração de gestão de memória (tabelas de páginas, controlos de tradução, atributos de memória)
- Tratamento de exceções: Vetores de exceção/interrupção, registos de síndrome de exceção, registos de endereço de falha
- Configuração do Sistema: Controlos de funcionalidades do processador, controlos de depuração, configuração do temporizador
- Registos Sintéticos: Registos de interface de hipervisor específicos para cada VTL (página de hiperchamada, ID do sistema operativo convidado, TSC de referência, controlador de interrupções sintético)
Este isolamento garante que cada VTL tem controlo independente sobre o seu ambiente de execução e não pode observar ou interferir com o estado privado de outros VTLs.
Em plataformas x64
Em plataformas x64, cada VTL mantém o seu próprio contexto de execução através de MSRs e registos específicos da arquitetura. O hipervisor preserva estes em switches VTL, garantindo o isolamento completo do ambiente de execução.
MSRs privados controlam mecanismos de chamada do sistema, atributos de memória, funcionalidades do processador e a interface do hipervisor.
MSRs Arquitetónicos:
- SYSENTER_CS, SYSENTER_ESP, SYSENTER_EIP, ESTRELA, LSTAR, CSTAR, SFMASK, EFER, PAT, KERNEL_GSBASE, FS. BASE, GS. BASE, TSC_AUX
- Registos APIC locais (incluindo CR8/TPR)
MSRs sintéticos:
- 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_VP_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
Registos privados controlam o ambiente de execução, a gestão de memória e o tratamento de exceções:
- Estado de Execução: RIP (ponteiro de instrução), RSP (ponteiro de pilha), RFLAGS (flags de processador)
- Gestão de Memória: CR0 (controlo do processador), CR3 (base de tabela de páginas), CR4 (funcionalidades do processador)
- Tabelas de Descritores: IDTR (tabela de descritores de interrupções), GDTR (tabela global de descritores)
- Registos de Segmento: CS, DS, ES, FS, GS, SS, TR, LDTR
- Controlo de depuração: DR7 (registo de controlo de depuração)
- Carimbo de tempo: TSC (contador de carimbo de hora, fornecendo referência de tempo independente por VTL)
- Estado de Depuração: DR6 (registo de estado de depuração - depende do tipo de processador; ler HvRegisterVsmCapabilities para determinar se é partilhado ou privado)
Nas plataformas ARM64
Nas plataformas ARM64, cada VTL mantém o seu próprio contexto completo de registos do sistema. O hipervisor preserva estes registos através dos switches VTL, garantindo o isolamento completo do ambiente de execução e exceção.
Registos de estado de execução controlam o fluxo do programa e o modo processador:
- PC (contador de programa), SP_EL0, SP_EL1 (ponteiros de pilha)
- PSTATE (flags de estado do processador), FPCR/FPSR (controlo/estado em ponto flutuante)
- ELR_EL1, SPSR_EL1 (registo do link de exceção, estado do programa guardado)
Registos de Gestão de Memória e Controlo de Tradução configuram a memória virtual:
- TTBR0_EL1, TTBR1_EL1 (registos base da tabela de tradução)
- TCR_EL1 (registo de controlo de tradução)
- MAIR_EL1 (registo de indireção do atributo de memória)
- SCTLR_EL1 (registo de controlo do sistema)
- CONTEXTIDR_EL1 (identificador de contexto)
Os registos de gestão de exceções e interrupções controlam o comportamento das exceções:
- VBAR_EL1 (registo de endereço base vetorial - localização da tabela vetorial)
- ESR_EL1 (registo da síndrome de exceção)
- FAR_EL1 (registo de endereço de falha)
- AFSR0_EL1, AFSR1_EL1 (registos auxiliares de estado de falha)
Registos de configuração do sistema controlam as características do processador:
- CPACR_EL1 (controlo de acesso por coprocessador)
- ACTLR_EL1 (registo auxiliar de controlo)
- AMAIR_EL1 (registo de indireção do atributo de memória auxiliar)
- ZCR_EL1, SMCR_EL1 (configuração SVE/SME, se suportada)
Os registos de Depuração e Monitorização de Desempenho são privados VTL. Isto inclui todos os registos do sistema de depuração (MDSCR_EL1, DBGBCR_EL1[], DBGBVR_EL1[], DBGWCR_EL1[], DBGWVR_EL1[], etc.) e todos os registos do monitor de desempenho (registos PMU PMCR_EL0 e relacionados)
Registos de Configuração do Temporizador :
- CNTKCTL_EL1 (controlo do temporizador do kernel)
- CNTV_CTL_EL0, CNTV_CVAL_EL0 (controlo virtual do temporizador e comparar valor)
Registos de identificação de threads :
- TPIDR_EL0, TPIDR_EL1 TPIDRRO_EL0 (ID de thread registado)
Registos de Hipervisores Sintéticos (acedidos através de hiperchamadas, não diretamente como registos do sistema):
- HvRegisterGuestOsId (identificação do sistema operativo convidado)
- HvRegisterHypercallMsrValue (habilitação da interface hypercall)
- HvRegisterReferenceTsc (página do contador de hora de referência)
- HvRegisterVpAssistPage (Página de assistência do VP para este VTL)
- Registos sintéticos do controlador de interrupções (SynIC)
- Registos sintéticos do temporizador (Stimer0-3)
- Interface local de controlador de interrupções (interface CPU GIC)
Este isolamento abrangente de registos por VTL garante que cada VTL tem controlo total sobre o seu ambiente de execução, gestão de memória, gestão de exceções e interface do hipervisor sem interferências de outros VTLs.
Estado compartilhado
Os VTLs partilham certos estados do processador para reduzir a sobrecarga das transições VTL e permitir uma comunicação eficiente entre níveis de confiança. O estado partilhado inclui registos de uso geral usados para computação e passagem de dados, estado de ponto flutuante e certos registos de estado do sistema que não afetam os limites de segurança.
Ao partilhar registos de uso geral entre VTLs, uma chamada VTL pode passar parâmetros diretamente nos registos, e um retorno VTL pode reenviar resultados sem necessidade de comunicação baseada em memória. Este design melhora significativamente o desempenho das chamadas cross-VTL, mantendo ao mesmo tempo o isolamento de segurança proporcionado pelo estado privado.
Em plataformas x64
Em plataformas x64, o estado partilhado inclui registos de uso geral para computação, registos de informação do sistema e certos registos de estado que não afetam o isolamento de segurança.
Os MSRs partilhados fornecem informação e configuração do sistema que são comuns entre VTLs:
- 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
Os registos partilhados permitem a passagem e computação eficiente de dados entre VTLs:
- General-Purpose Registos: Rax, Rbx, Rcx, Rdx, Rsi, Rdi, Rbp, R8-R15 (usados para cálculo e passagem de parâmetros durante chamadas VTL)
- Informação de Exceção: CR2 (endereço linear de falha de página - partilhado para contexto de gestão de exceções)
- Dados de Depuração: DR0-DR3 (registos de endereços de depuração - usados para endereços de ponto de interrupção)
-
Floating-Point e Estado Vetorial:
- Estado de ponto flutuante X87 (computação legada de ponto flutuante)
- Estado XMM (vetores SSE de 128 bits)
- Estado AVX (vetores de 256 bits)
- XCR0/XFEM (máscara de ativação de funcionalidades estendidas)
- Estado de Depuração: DR6 (registo de estado de depuração - depende do tipo de processador; ler HvRegisterVsmCapabilities para determinar se é partilhado ou privado)
Nas plataformas ARM64
Nas plataformas ARM64, o estado partilhado inclui registos usados para computação, passagem de dados e operações em ponto flutuante. Esta partilha permite chamadas VTL eficientes ao permitir que parâmetros e resultados sejam passados diretamente em registos.
Os registos partilhados permitem o cálculo e a comunicação entre VTLs:
-
General-Purpose Registos: X0-X17, X19-X28 (usados para cálculo e passagem de parâmetros)
- X0-X7 tipicamente usado para parâmetros de função e valores de retorno durante chamadas VTL
- X8-X17, X19-X28 disponível para computação geral
- Nota: X18 (registo da plataforma) e PC são privados por VTL por razões de segurança
- Nota: X29 (FP/apontador de frame), X30 (registo LR/link) e SP são privados por VTL
-
Floating-Point e Estado Vetorial:
- Q0-Q31 (registos NEON/ponto flutuante de 128 bits para computação vetorial)
- Estado SIMD avançado (NEON) para operações vetoriais
- Nota: O estado SVE (Z0-Z31, P0-P15, FFR) e o estado SME são privados VTL. A parte inferior de 128 bits (registos Q) é partilhada, mas os bits superiores dos registos Z podem estar corrompidos em transições VTL. O software não deve depender da preservação do conteúdo dos registos Z entre os switches VTL.
- Nota: O estado SPE (Statistical Profiling Extension) é partilhado entre VTLs, exceto PMBSR_EL1 que é VTL-privado
-
Registos de Informação do Sistema (apenas de leitura ou não críticos de segurança):
- Identificação do sistema e registos de funcionalidades
- Informação de cache e tipos de TLB
O ARM64 segue o mesmo princípio do x64: o estado geral de computação é partilhado para maior eficiência, enquanto o estado de controlo e configuração que afeta os limites de segurança permanece privado para cada VTL.
Modo Real (apenas x64)
O modo real não é suportado para VTL superior a 0 em plataformas x64. VTLs superiores a 0 podem funcionar em modo de 32 ou 64 bits em x64.
Gerenciamento de interrupções VTL
Para alcançar um alto nível de isolamento entre os 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ó fica ativo se o processador virtual estiver sendo executado nessa VTL específica. Se um processador virtual alternar estados VTL, o controlador de interrupção ativo no processador também será comutado.
Em plataformas x64, cada VTL tem uma instância APIC local separada. Nas plataformas ARM64, cada VTL tem um GIC separado.
Uma interrupção direcionada a uma VTL que seja maior do que a VTL ativa causará um comutador VTL imediato. O VTL mais alto pode então receber a interrupção. Se o VTL superior não conseguir receber a interrupção devido à sua máscara de prioridade (TPR/CR8 em x64, máscara de prioridade GIC em ARM64), a interrupção é mantida como "pendente" e o VTL não commoda. Se houver várias VTLs com interrupções pendentes, a VTL mais alta terá precedência (sem aviso prévio à VTL inferior).
Quando uma interrupção é direcionada a uma VTL mais baixa, a interrupção não é entregue até a próxima vez que o processador virtual fizer a transição para a VTL de destino.
Em plataformas x64, IPIs INIT e de arranque direcionados para um VTL mais baixo são eliminados num processador virtual com um VTL mais alto ativado. Como os mecanismos arquitetónicos de arranque do processador podem estar bloqueados, a hiperchamada HvCallStartVirtualProcessor deve ser usada para iniciar processadores.
Nas plataformas ARM64, métodos de interface PSCI (como CPU_ON) para colocar processadores online são igualmente bloqueados quando um VTL superior está ativado. A hiperchamada HvCallStartVirtualProcessor fornece um mecanismo consistente multiplataforma para iniciar processadores.
Mascaramento de Interrupções e Comutadores VTL
Em plataformas x64
Para efeitos de comutação de VTLs, RFLAGS. IF não afeta se uma interrupção segura aciona um comutador VTL. Se RFLAGS. SE for limpo para mascarar interrupções, interrupções em VTLs mais altas ainda causarão um switch VTL para uma VTL mais alta. Apenas o valor mais elevado de TPR/CR8 do VTL é tido em conta ao decidir se deve interromper imediatamente.
Esse comportamento também afeta interrupções pendentes em um retorno VTL. Se os RFLAGS. Se o bit IF for limpo para mascarar interrupções em uma determinada VTL, e a VTL retornar (para uma VTL inferior), o hipervisor reavaliará quaisquer interrupções pendentes. Isso causará uma chamada imediata de volta para a VTL mais alta.
Nas plataformas ARM64
De forma semelhante, os bits da máscara de interrupção PSTATE (DAIF) não afetam se uma interrupção segura ativa um comutador VTL. Se as interrupções forem mascaradas via PSTATE, as interrupções direcionadas a VTLs mais elevados continuarão a causar um switch VTL. Apenas a máscara de prioridade GIC do VTL mais alto é tida em conta ao decidir se entrega imediatamente a interrupção.
Assistente 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 Virtual Interrupt Notification Assist (VINA) 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 do HvRegisterVsmVina. A instalação VINA gerará uma interrupção acionada por borda para o VTL superior atualmente ativo quando uma interrupção para o VTL inferior estiver pronta para entrega imediata.
A fim de evitar uma enxurrada de interrupções quando esta instalação é ativada, a instalação VINA inclui algum estado limitado. Quando uma interrupção VINA é gerada, o estado da instalação VINA é alterado para "Asserted". O envio de um fim de interrupção para o SINT associado à instalação VINA não limpará o estado "Afirmado". O estado afirmado só pode ser apurado de duas formas:
- O estado pode ser limpo manualmente escrevendo no campo VinaAsserted da estrutura HV_VP_VTL_CONTROL .
- O estado é automaticamente limpo na próxima entrada para a VTL se a opção "auto-reset on VTL entry" estiver ativada no registro HvRegisterVsmVina.
Isso permite que o código executado em uma VTL segura seja apenas notificado da primeira interrupção recebida para uma VTL inferior. Se um VTL seguro desejar ser notificado de interrupções adicionais, ele pode limpar o campo VinaAsserted da página de assistência VP e será notificado da próxima nova interrupção.
Intercetações seguras
O hipervisor permite que uma VTL mais alta instale intercetações para eventos que ocorrem no contexto de uma VTL inferior. Isso dá às VTLs mais altas um nível elevado de controle sobre os recursos de VTL mais baixas. As intercetações seguras podem ser usadas para proteger recursos críticos do sistema e evitar ataques de VTLs mais baixas.
Uma intercetação segura é enfileirada para a VTL mais alta e essa VTL é executada na VP.
Tipos de intercetação segura
| Tipo de intercetação | O Intercept aplica-se a: |
|---|---|
| Acesso à memória | Tentando acessar proteções GPA estabelecidas por uma VTL superior. |
| Controlar o acesso ao registo | Tentando acessar um conjunto de registros de controle especificados por uma VTL superior. |
Interceções aninhadas
Várias VTLs podem instalar intercetações seguras para o mesmo evento em uma VTL inferior. Assim, uma hierarquia é estabelecida para decidir onde os interceptos aninhados são notificados. A seguinte lista é a ordem pela qual a interceção é notificada:
- VTL inferior
- VTL mais alto
Tratamento de intercetações seguras
Uma vez que um VTL tenha sido notificado de uma intercetação segura, ele deve tomar medidas para que o VTL inferior possa continuar. A VTL mais alta pode lidar com a intercetação de várias maneiras, incluindo: injetando uma exceção, emulando o acesso ou fornecendo um proxy para o acesso. Em qualquer caso, se o estado privado do VP VTL inferior precisar ser modificado, HvCallSetVpRegisters deve ser usado.
Interceções Seguras de Registos (apenas x64)
Em plataformas x64, um VTL superior pode interceptar ao aceder a certos registos de controlo e MSRs. Isso é conseguido definindo HvX64RegisterCrInterceptControl usando a hiperchamada HvCallSetVpRegisters . Definir o bit de controle em HvX64RegisterCrInterceptControl acionará uma intercetação para cada acesso do registro de controle correspondente.
Esta funcionalidade é específica do x64, pois interceta registos de controlo x64 (CR0, CR4, XCR0) e MSRs x64 (EFER, LSTAR, STAR, etc.). As plataformas ARM64 podem suportar capacidades de interceção semelhantes através de diferentes mecanismos.
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;
Registos de máscaras
Para permitir um controle mais preciso, um subconjunto de registros de controle também tem registros de máscara correspondentes. Os registos de máscaras podem ser utilizados para instalar interceções num subconjunto dos registos de controlo correspondentes. Quando um registro de máscara não estiver definido, qualquer acesso (conforme definido por HvX64RegisterCrInterceptControl) acionará uma intercetação.
O hipervisor suporta os seguintes registos de máscara: HvX64RegisterCrInterceptCr0Mask, HvX64RegisterCrInterceptCr4Mask e HvX64RegisterCrInterceptIa32MiscEnableMask.
DMA e dispositivos
Os dispositivos efetivamente têm o mesmo nível de privilégio que VTL0. Quando o VSM está habilitado, toda a memória alocada no dispositivo é marcada como VTL0. Todos os acessos DMA têm os mesmos privilégios que VTL0.