Modalità protetta virtuale

La modalità di protezione virtuale (VSM) è un set di funzionalità di hypervisor e i servizi di riconoscimento dei dati aziendali offerti per ospitare e partizioni guest che consentono la creazione e la gestione di nuovi limiti di sicurezza all'interno del software del sistema operativo. VSM è la funzionalità dell'hypervisor su cui si basano Windows funzionalità di sicurezza, tra cui Device Guard, Credential Guard, TPM virtuali e macchine virtuali schermate. Queste funzionalità di sicurezza sono state introdotte in Windows 10 e Windows Server 2016.

VSM consente al software del sistema operativo nelle partizioni radice e guest di creare aree isolate di memoria per l'archiviazione e l'elaborazione degli asset di sicurezza del sistema. L'accesso a queste aree isolate viene controllato e concesso esclusivamente tramite l'hypervisor, che è una parte altamente privilegiata e altamente attendibile della Trusted Compute Base (TCB) del sistema. Poiché l'hypervisor viene eseguito a un livello di privilegi superiore rispetto al software del sistema operativo e ha il controllo esclusivo delle risorse hardware del sistema chiave, ad esempio i controlli delle autorizzazioni di accesso alla memoria nella CPU MMU e IOMMU all'inizio dell'inizializzazione del sistema, l'hypervisor può proteggere queste aree isolate dall'accesso non autorizzato, anche dal software del sistema operativo (ad esempio, kernel del sistema operativo e driver di dispositivo) con accesso in modalità supervisore (ad esempio CPL0, o "Anello 0").

Con questa architettura, anche se il normale software a livello di sistema in esecuzione in modalità supervisore (ad esempio kernel, driver e così via) viene compromesso da software dannoso, gli asset in aree isolate protette dall'hypervisor possono rimanere protetti.

Livello di attendibilità virtuale (VTL)

VSM ottiene e mantiene l'isolamento tramite i livelli di attendibilità virtuale (VTL). I VTL sono abilitati e gestiti sia per partizione che per processore virtuale.

I livelli di attendibilità virtuale sono gerarchici, con livelli più elevati con privilegi più elevati rispetto ai livelli inferiori. VTL0 è il livello con privilegi minimi, con VTL1 con privilegi maggiori rispetto a VTL0, VTL2 con privilegi maggiori rispetto a VTL1 e così via.

Dal punto di vista architettonico, sono supportati fino a 16 livelli di VTL; tuttavia, un hypervisor può scegliere di implementare meno di 16 VTL. Attualmente vengono implementati solo due VTL.

typedef UINT8 HV_VTL, *PHV_VTL;

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

Ogni VTL ha un proprio set di protezioni di accesso alla memoria. Queste protezioni di accesso vengono gestite dall'hypervisor nello spazio indirizzi fisico di una partizione e pertanto non possono essere modificate dal software a livello di sistema in esecuzione nella partizione.

Poiché i VTL con privilegi più elevati possono applicare le proprie protezioni di memoria, i VTL più elevati possono proteggere efficacemente le aree di memoria da vtls inferiori. In pratica, ciò consente a una VTL inferiore di proteggere le aree di memoria isolata proteggendole con una VTL più elevata. Ad esempio, VTL0 potrebbe archiviare un segreto in VTL1, a quel punto solo VTL1 potrebbe accedervi. Anche se VTL0 è compromesso, il segreto sarebbe sicuro.

Protezioni VTL

Esistono più facet per ottenere l'isolamento tra vtl:

  • Protezione dall'accesso alla memoria: ogni VTL gestisce un set di protezioni di accesso alla memoria fisica guest. Il software in esecuzione in una determinata VTL può accedere alla memoria solo in base a queste protezioni.
  • Stato processore virtuale: i processori virtuali mantengono uno stato separato per VTL. Ad esempio, ogni VTL definisce un set di registri VP privati. Il software in esecuzione in una VTL inferiore non può accedere allo stato di registrazione del processore virtuale privato del VTL superiore.
  • Interrupt: insieme a uno stato del processore separato, ogni VTL ha anche un proprio sottosistema di interrupt (APIC locale). In questo modo, i VTL più elevati possono elaborare interruzioni senza rischiare interferenze da una VTL inferiore.
  • Pagine di sovrimpressione: alcune pagine sovrapposte vengono mantenute per VTL, in modo che i VTL più elevati abbiano accesso affidabile. Ad esempio, è presente una pagina di sovrapposizione hypercall separata per VTL.

Rilevamento e stato di VSM

La funzionalità VSM viene annunciata alle partizioni tramite il flag dei privilegi di partizione AccessVsm. Solo le partizioni con tutti i privilegi seguenti possono usare VSM: AccessVsm, AccessVpRegisters e AccessSynicRegs.

Rilevamento funzionalità VSM

Gli utenti guest devono usare il registro specifico del modello seguente per accedere a un report sulle funzionalità di VSM:

Indirizzo MSR Nome registro Descrizione
0x000D0006 HV_X64_REGISTER_VSM_CAPABILITIES Report sulle funzionalità vsm.

Il formato dell'MSR Register VSM Capabilities è il seguente:

BITS Descrizione Attributi
63 Dr6Shared Lettura
62:47 MbecVtlMask Lettura
46 DenyLowerVtlStartup Lettura
45:0 RsvdZ Lettura

Dr6Shared indica al guest se Dr6 è un registro condiviso tra i vtl.

MvecVtlMask indica al guest i VTLs per cui è possibile abilitare Mbec.

DenyLowerVtlStartup indica al guest se un Vtl può negare la reimpostazione di una VPN tramite una VTL inferiore.

Registro stato VSM

Oltre a un flag di privilegio di partizione, è possibile usare due registri virtuali per ottenere informazioni aggiuntive sullo stato di VSM: HvRegisterVsmPartitionStatus e HvRegisterVsmVpStatus.

HvRegisterVsmPartitionStatus

HvRegisterVsmPartitionStatus è un registro di sola lettura per partizione condiviso tra tutti i vtl. Questo registro fornisce informazioni su quali VTLs sono stati abilitati per la partizione, in cui sono abilitati i controlli di esecuzione basati sulla modalità, nonché il numero massimo consentito di VTL.

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

HvRegisterVsmVpStatus

HvRegisterVsmVpStatus è un registro di sola lettura e viene condiviso tra tutti i vtl. Si tratta di un registro per vp, ovvero ogni processore virtuale mantiene la propria istanza. Questo registro fornisce informazioni su quali VTL sono stati abilitati, che sono attivi, nonché la modalità MBEC attiva su 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 è l'ID del contesto VTL attualmente attivo nel processore virtuale.

ActiveMbecEnabled specifica che MBEC è attualmente attivo nel processore virtuale.

EnabledVtlSet è una bitmap delle VTL abilitate nel processore virtuale.

Stato iniziale VTL di partizione

Quando una partizione viene avviata o reimpostata, inizia l'esecuzione in VTL0. Tutti gli altri VTL vengono disabilitati al momento della creazione della partizione.

Abilitazione VTL

Per iniziare a usare una VTL, una VTL inferiore deve avviare quanto segue:

  1. Abilitare la VTL di destinazione per la partizione. In questo modo la durata (VTL) è disponibile a livello generale per la partizione.
  2. Abilitare la VTL di destinazione in uno o più processori virtuali. In questo modo la durata (VTL) è disponibile per una VP e ne imposta il contesto iniziale. È consigliabile che tutti i VP abbiano gli stessi vtl abilitati. La presenza di una libreria virtuale abilitata in alcune macchine virtuali (ma non tutte) può causare un comportamento imprevisto.
  3. Dopo aver abilitato la durata virtuale per una partizione e una VP, è possibile iniziare a impostare le protezioni di accesso dopo aver impostato il flag EnableVtlProtection.

Si noti che i VTL non devono essere consecutivi.

Abilitazione di una VTL di destinazione per una partizione

L'hypercall HvCallEnablePartitionVtl viene usato per abilitare una durata virtuale per una determinata partizione. Si noti che prima che il software possa effettivamente essere eseguito in una determinata VTL, tale durata deve essere abilitata nei processori virtuali nella partizione.

Abilitazione di una VTL di destinazione per processori virtuali

Una volta abilitata una VTL per una partizione, può essere abilitata nei processori virtuali della partizione. L'hypercall HvCallEnableVpVtl può essere usato per abilitare vtls per un processore virtuale, che imposta il contesto iniziale.

I processori virtuali hanno un "contesto" per VTL. Se viene commutata una VTL, viene anche commutato lo stato privato della VTL.

Configurazione VTL

Dopo aver abilitato una VTL, la configurazione può essere modificata da una VP in esecuzione con una VTL uguale o superiore.

Partition Configuration

Gli attributi a livello di partizione possono essere configurati usando il registro HvRegisterVsmPartitionConfig. In ogni partizione è presente un'istanza di questo registro per ogni VTL (maggiore di 0).

Ogni VTL può modificare la propria istanza di HV_REGISTER_VSM_PARTITION_CONFIG, nonché le istanze per i VTL inferiori. I VTL potrebbero non modificare questo registro per i VTL più elevati.

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;

I campi di questo registro sono descritti di seguito.

Abilitare le protezioni VTL

Dopo aver abilitato una VTL, il flag EnableVtlProtection deve essere impostato prima di iniziare a applicare le protezioni di memoria. Questo flag è write-once, ovvero una volta impostato, non può essere modificato.

Maschera protezione predefinita

Per impostazione predefinita, il sistema applica protezioni RWX a tutte le pagine attualmente mappate e a tutte le pagine "aggiunte a caldo" future. Le pagine aggiunte ad accesso frequente fanno riferimento a qualsiasi memoria aggiunta a una partizione durante un'operazione di ridimensionamento.

Una versione VTL superiore può impostare un criterio di protezione della memoria predefinito diverso specificando DefaultVtlProtectionMask in HV_REGISTER_VSM_PARTITION_CONFIG. Questa maschera deve essere impostata al momento dell'abilitazione del VTL. Non può essere modificato una volta impostato e viene cancellato solo da una reimpostazione della partizione.

bit Descrizione
0 Lettura
1 Scrittura
2 Esecuzione in modalità kernel (KMX)
3 Modalità utente Esegui (UMX)

Memoria zero su Reimpostazione

ZeroMemOnReset è un bit che controlla se la memoria è zero prima che venga reimpostata una partizione. Questa configurazione è attiva per impostazione predefinita. Se il bit è impostato, la memoria della partizione è zero al momento della reimpostazione in modo che la memoria VTL superiore non possa essere compromessa da una VTL inferiore. Se questo bit viene cancellato, la memoria della partizione non viene zero sulla reimpostazione.

DenyLowerVtlStartup

Il flag DenyLowerVtlStartup controlla se un processore virtuale può essere avviato o reimpostato da VTLs inferiori. Sono inclusi i modi dell'architettura per reimpostare un processore virtuale (ad esempio SIPI su X64) e l'hypercall HvCallStartVirtualProcessor .

InterceptVpStartup

Se il flag InterceptVpStartup è impostato, l'avvio o la reimpostazione di un processore virtuale genera un'intercetta al VTL superiore.

Configurazione di VTL inferiori

Il registro seguente può essere usato da VTLs superiori per configurare il comportamento di VTLs inferiori:

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

Ogni VTL (superiore a 0) ha un'istanza di questo registro per ogni VTL inferiore a se stesso. Ad esempio, VTL2 avrà due istanze di questo registro, una per VTL1 e una seconda per VTL0.

I campi di questo registro sono descritti di seguito.

MbecEnabled

Questo campo configura se MBEC è abilitato per il VTL inferiore.

TlbLocked

Questo campo blocca il TLB del VTL inferiore. Questa funzionalità può essere usata per impedire agli invalidazioni TLB di causare invalidazioni TLB che potrebbero interferire con una VTL superiore. Quando questo bit è impostato, tutte le richieste di scaricamento dello spazio indirizzi dalla VTL inferiore vengono bloccate finché il blocco non viene sollevato.

Per sbloccare il TLB, il VTL superiore può cancellare questo bit. Inoltre, una volta che una VP torna a una VTL inferiore, rilascia tutti i blocchi TLB che contiene al momento.

Voce VTL

Una VTL viene "immessa" quando un VP passa da una VTL inferiore a una versione superiore. Ciò può verificarsi per i seguenti motivi:

  1. Chiamata VTL: si tratta di quando il software vuole richiamare in modo esplicito il codice in una VTL superiore.
  2. Interruzione sicura: se viene ricevuto un interruzione per una VTL superiore, la VP immetterà il VTL superiore.
  3. Intercetta sicura: alcune azioni attiveranno un interruzione sicuro (ad esempio l'accesso a determinati msr).

Una volta immesso un VTL, deve uscire volontariamente. Una VTL superiore non può essere preceduta da una VTL inferiore.

Identificazione della voce VTL

Per reagire in modo appropriato a una voce, potrebbe essere necessario conoscere il motivo per cui è stato immesso un VTL superiore. Per distinguere tra motivi di ingresso, la voce VTL è inclusa nella struttura HV_VP_VTL_CONTROL .

Chiamata VTL

Una chiamata "VTL" è quando una VTL inferiore avvia una voce in una VTL superiore(ad esempio, per proteggere un'area di memoria con la VTL superiore) tramite l'hypercall HvCallVtlCall .

Le chiamate VTL mantengono lo stato dei registri condivisi tra commutatori VTL. I registri privati vengono mantenuti a livello VTL. L'eccezione a queste restrizioni è i registri richiesti dalla sequenza di chiamate VTL. Per una chiamata VTL sono necessari i registri seguenti:

x64 x86 Descrizione
RCX EDX:EAX Specifica un input del controllo chiamata VTL nell'hypervisor
RAX ECX Riservato

Tutti i bit nell'input del controllo chiamate VTL sono attualmente riservati.

Restrizioni per le chiamate VTL

Le chiamate VTL possono essere avviate solo dalla modalità processore con privilegi più elevati. Ad esempio, nei sistemi x64 una chiamata VTL può venire solo da CPL0. Una chiamata VTL avviata da una modalità processore che è tutto ciò che è il più privilegiato del sistema comporta l'inserimento di un'eccezione #UD nel processore virtuale.

Una chiamata VTL può passare solo al VTL più alto successivo. In altre parole, se sono abilitati più VTL, una chiamata non può "ignorare" un VTL. Le azioni seguenti comportano un'eccezione #UD:

  • Una chiamata VTL avviata da una modalità processore che è tutto ciò che è il più privilegiato sul sistema (architettura specifica).
  • Chiamata VTL dalla modalità reale (x86/x64)
  • Chiamata VTL in un processore virtuale in cui il VTL di destinazione è disabilitato (o non è già stato abilitato).
  • Chiamata VTL con un valore di input di controllo non valido

Uscita VTL

Un passaggio a una VTL inferiore è noto come "return". Una volta completata l'elaborazione, una VTL può avviare una restituzione VTL per passare a una VTL inferiore. L'unico modo in cui un ritorno VTL può verificarsi è se un VTL superiore avvia volontariamente uno. Una VTL inferiore non può mai prefissire una versione più alta.

Restituzione VTL

Un "ritorno VTL" è quando una VTL superiore avvia un commutatore in una VTL inferiore tramite l'hypercall HvCallVtlReturn . Analogamente a una chiamata VTL, lo stato del processore privato viene disattivato e lo stato condiviso rimane sul posto. Se il VTL inferiore ha chiamato in modo esplicito nella VTL superiore, l'hypervisor incrementa il puntatore delle istruzioni VTL superiore prima del completamento della restituzione in modo che possa continuare dopo una chiamata VTL.

Una sequenza di codice restituito VTL richiede l'uso dei registri seguenti:

x64 x86 Descrizione
RCX EDX:EAX Specifica un input del controllo restituito VTL nell'hypervisor
RAX ECX Riservato

L'input del controllo restituito VTL ha il formato seguente:

BITS Campo Descrizione
63:1 RsvdZ
0 Restituzione rapida I registri non vengono ripristinati

Le azioni seguenti genereranno un'eccezione #UD:

  • Tentativo di restituzione VTL quando il VTL più basso è attualmente attivo
  • Tentativo di restituzione VTL con un valore di input di controllo non valido
  • Tentativo di restituzione di una VTL da una modalità processore che è tutto ma il più privilegiato sul sistema (architettura specifica)

Restituzione rapida

Come parte dell'elaborazione di una restituzione, l'hypervisor può ripristinare lo stato del registro VTL inferiore dalla struttura HV_VP_VTL_CONTROL . Ad esempio, dopo l'elaborazione di un interruzione sicuro, una VTL superiore potrebbe voler restituire senza interrompere lo stato della VTL inferiore. Pertanto, l'hypervisor fornisce un meccanismo per ripristinare semplicemente i registri VTL inferiori al valore di pre-chiamata archiviato nella struttura di controllo VTL.

Se questo comportamento non è necessario, una VTL superiore può usare una "restituzione rapida". Una restituzione rapida è quando l'hypervisor non ripristina lo stato del registro dalla struttura di controllo. Questa operazione deve essere usata ogni volta che è possibile per evitare l'elaborazione non necessaria.

Questo campo può essere impostato con bit 0 dell'input restituito VTL. Se è impostato su 0, i registri vengono ripristinati dalla struttura HV_VP_VTL_CONTROL. Se questo bit è impostato su 1, i registri non vengono ripristinati (una restituzione rapida).

Assistenza pagina Hypercall

L'hypervisor fornisce meccanismi per facilitare le chiamate VTL e restituisce tramite la pagina hypercall. Questa pagina astrae la sequenza di codice specifica necessaria per cambiare le VTL.

Le sequenze di codice per eseguire chiamate VTL e restituisce possono essere accessibili eseguendo istruzioni specifiche nella pagina hypercall. I blocchi di chiamata/restituzione si trovano in corrispondenza di un offset nella pagina hypercall determinata dal registro virtuale HvRegisterVsmCodePageOffset. Si tratta di un registro a livello di partizione e di sola lettura, con un'istanza separata per VTL.

Un VTL può eseguire una chiamata/restituzione VTL usando l'istruzione CALL. Una CHIAMATA alla posizione corretta nella pagina hypercall avvierà una chiamata/restituzione VTL.

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

Per riepilogare, i passaggi per chiamare una sequenza di codice usando la pagina hypercall sono i seguenti:

  1. Eseguire il mapping della pagina hypercall nello spazio Criteri di gruppo di una VTL
  2. Determinare l'offset corretto per la sequenza di codice (chiamata VTL o restituzione).
  3. Eseguire la sequenza di codice usando CALL.

Protezioni di accesso alla memoria

Una protezione necessaria fornita da VSM è la possibilità di isolare gli accessi alla memoria.

I VTL più elevati hanno un elevato grado di controllo sul tipo di accesso alla memoria consentito dai VTL inferiori. Esistono tre tipi di protezione di base che possono essere specificati da una VTL superiore per una determinata pagina Criteri di gruppo: lettura, scrittura e eXecute. Questi sono definiti nella tabella seguente:

Nome Descrizione
Lettura Controlla se l'accesso in lettura è consentito a una pagina di memoria
Scrittura Controlla se l'accesso in scrittura consentito a una pagina di memoria
Execute Controlla se i recupero delle istruzioni sono consentiti per una pagina di memoria.

Queste tre combinano per i tipi seguenti di protezione della memoria:

  1. Nessun accesso
  2. Sola lettura, nessuna esecuzione
  3. Sola lettura, eseguire
  4. Lettura/scrittura, nessuna esecuzione
  5. Lettura/scrittura, esecuzione

Se il controllo di esecuzione basato sulla modalità (MBEC) è abilitato, è possibile impostare separatamente le protezioni per l'esecuzione della modalità utente e kernel.

I vtls superiori possono impostare la protezione della memoria per un oggetto Criteri di gruppo tramite l'hypercall HvCallModifyVtlProtectionMask .

Gerarchia di protezione della memoria

Le autorizzazioni di accesso alla memoria possono essere impostate da una serie di origini per una determinata VTL. Le autorizzazioni di ogni VTL possono essere potenzialmente limitate da un numero di altri VTL, oltre che dalla partizione host. L'ordine in cui vengono applicate le protezioni è il seguente:

  1. Protezione della memoria impostata dall'host
  2. Protezioni di memoria impostate da VTLs superiori

In altre parole, la protezione VTL sostituisce le protezioni host. VTLs di livello superiore sosede vtLs di livello inferiore. Si noti che un VTL potrebbe non impostare le autorizzazioni di accesso alla memoria per se stesso.

Si prevede che un'interfaccia conforme non sovrimpressa alcun tipo non RAM su RAM.

Violazioni dell'accesso alla memoria

Se una VP in esecuzione in una VTL inferiore tenta di violare una protezione di memoria impostata da una VTL superiore, viene generata un'intercetta. Questa intercetta viene ricevuta dal VTL superiore che imposta la protezione. Ciò consente ai vtls più elevati di gestire la violazione in base a un caso per caso. Ad esempio, il VTL superiore può scegliere di restituire un errore o emulare l'accesso.

Controllo esecuzione basato sulla modalità (MBEC)

Quando una VTL inserisce una restrizione di memoria in una VTL inferiore, può essere necessario distinguere tra l'utente e la modalità kernel quando si concede un privilegio di "esecuzione". Ad esempio, se i controlli di integrità del codice sarebbero stati eseguiti in una VTL superiore, la possibilità di distinguere tra la modalità utente e la modalità kernel significa che un VTL potrebbe applicare l'integrità del codice solo per le applicazioni in modalità kernel.

Oltre alle tre protezioni di memoria tradizionali (lettura, scrittura, esecuzione), MBEC introduce una distinzione tra la modalità utente e la modalità kernel per le protezioni eseguite. Pertanto, se MBEC è abilitato, una VTL ha la possibilità di impostare quattro tipi di protezione della memoria:

Nome Descrizione
Lettura Controlla se l'accesso in lettura è consentito a una pagina di memoria
Scrittura Controlla se l'accesso in scrittura consentito a una pagina di memoria
Modalità utente Esegui (UMX) Controlla se le istruzioni recuperate in modalità utente sono consentite per una pagina di memoria. NOTA: se MBEC è disabilitato, questa impostazione viene ignorata.
Modalità kernel Execute (UMX) Controlla se le istruzioni recuperate in modalità kernel sono consentite per una pagina di memoria. NOTA: se MBEC è disabilitato, questa impostazione controlla sia l'accesso in modalità utente che in modalità kernel.

La memoria contrassegnata con le protezioni "Esecuzione in modalità utente" sarebbe eseguibile solo quando il processore virtuale è in esecuzione in modalità utente. Analogamente, la memoria "Esecuzione in modalità kernel" sarebbe eseguibile solo quando il processore virtuale è in esecuzione in modalità kernel.

KmX e UMX possono essere impostati in modo indipendente in modo che le autorizzazioni di esecuzione vengano applicate in modo diverso tra l'utente e la modalità kernel. Tutte le combinazioni di UMX e KMX sono supportate, ad eccezione di KMX=1, UMX=0. Il comportamento di questa combinazione non è definito.

MBEC è disabilitato per impostazione predefinita per tutti i processori vtl e virtuali. Quando MBEC è disabilitato, il bit di esecuzione in modalità kernel determina la restrizione di accesso alla memoria. Pertanto, se MBEC è disabilitato, il codice KMX=1 è eseguibile sia in modalità kernel che in modalità utente.

Tabelle descrittori

Qualsiasi codice in modalità utente che accede alle tabelle descrittori deve trovarsi nelle pagine criteri di gruppo contrassegnate come KMX=UMX=1. Il software in modalità utente che accede alle tabelle descrittori da una pagina criteri di gruppo contrassegnata con KMX=0 non è supportato e genera un errore di protezione generale.

Configurazione MBEC

Per usare il controllo di esecuzione basato sulla modalità, è necessario abilitarlo a due livelli:

  1. Quando il VTL è abilitato per una partizione, È necessario abilitare MBEC usando HvCallEnablePartitionVtl
  2. MBEC deve essere configurato in base a VP e per VTL usando HvRegisterVsmVpSecureVtlConfig.

Interazione MBEC con prevenzione dell'esecuzione in modalità supervisore (SMEP)

Supervisor-Mode prevenzione dell'esecuzione (SMEP) è una funzionalità del processore supportata in alcune piattaforme. SMEP può influire sull'operazione di MBEC a causa della restrizione dell'accesso al supervisore alle pagine di memoria. L'hypervisor rispetta i criteri seguenti correlati a SMEP:

  • Se SMEP non è disponibile per il sistema operativo guest (indipendentemente dalle funzionalità hardware o dalla modalità di compatibilità del processore), MBEC opera non interessato.
  • Se SMEP è disponibile ed è abilitato, MBEC opera in modo non interessato.
  • Se SMEP è disponibile ed è disabilitato, tutte le restrizioni di esecuzione vengono regolate dal controllo KMX. Pertanto, solo il codice contrassegnato KMX=1 sarà consentito per l'esecuzione.

Isolamento dello stato del processore virtuale

I processori virtuali mantengono stati separati per ogni VTL attivo. Tuttavia, alcuni di questo stato sono privati di una determinata VTL e lo stato rimanente è condiviso tra tutti i VTL.

Lo stato mantenuto per VTL (a.k.a. stato privato) viene salvato dall'hypervisor tra transizioni VTL. Se viene avviato un commutatore VTL, l'hypervisor salva lo stato privato corrente per il VTL attivo e quindi passa allo stato privato della VTL di destinazione. Lo stato condiviso rimane attivo indipendentemente dai commutatori VTL.

Stato privato

In generale, ogni VTL ha i propri registri di controllo, registrare RIP, registrare RSP e MSR. Di seguito è riportato un elenco di registri e msr specifici privati per ogni VTL.

MsR privati:

  • 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
  • Registri APIC locali (inclusi CR8/TPR)

Registri privati:

  • RIP, RSP
  • RFLAGS
  • CR0, CR3, CR4
  • Ripristino di emergenza
  • IDTR, GDTR
  • CS, DS, ES, FS, GS, SS, TR, LDTR
  • TSC
  • DR6 (*dipendente dal tipo di processore. Leggere registrazione virtuale HvRegisterVsmCapabilities per determinare lo stato condiviso/privato)

Stato condiviso

Lo stato delle condivisioni vtls consente di ridurre il sovraccarico dei contesti di cambio. Lo stato di condivisione consente anche una comunicazione necessaria tra VTL. La maggior parte dei registri a virgola mobile e utilizzo generico è condivisa, come la maggior parte degli MSR architetturali. Di seguito è riportato l'elenco di msr e registri specifici condivisi tra tutti i vtl:

MsR condivisi:

  • 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

Registri condivisi:

  • Rax, Rbx, Rcx, Rdx, Rsi, Rdi, Rbp
  • CR2
  • R8 – R15
  • DR0 - Dr5
  • Stato a virgola mobile X87
  • Stato XMM
  • Stato AVX
  • XCR0 (XFEM)
  • DR6 (*dipendente dal tipo di processore. Leggere registrazione virtuale HvRegisterVsmCapabilities per determinare lo stato condiviso/privato)

Modalità reale

La modalità reale non è supportata per qualsiasi VTL maggiore di 0. I VTL maggiori di 0 possono essere eseguiti in modalità a 32 bit o a 64 bit.

Gestione degli interruzioni VTL

Per ottenere un livello elevato di isolamento tra livelli di attendibilità virtuale, la modalità sicura virtuale fornisce un sottosistema di interruzione separato per ogni VTL abilitato in un processore virtuale. Ciò garantisce che un VTL sia in grado di inviare e ricevere interruzioni senza interferenze da una VTL meno sicura.

Ogni VTL ha il proprio controller di interruzione, che è attivo solo se il processore virtuale è in esecuzione in tale particolare VTL. Se un processore virtuale commuta gli stati VTL, il controller di interruzione attivo nel processore viene anche commutato.

Un interruzione destinata a una VTL superiore alla VTL attiva causerà un commutatore VTL immediato. Il VTL superiore può quindi ricevere l'interruzione. Se il VTL superiore non è in grado di ricevere l'interruzione a causa del relativo valore TPR/CR8, l'interruzione viene mantenuta come "in sospeso" e il VTL non cambia. Se sono presenti più VTLs con interruzioni in sospeso, il VTL più alto ha la precedenza (senza notare il VTL inferiore).

Quando un interruzione è destinata a una VTL inferiore, l'interruzione non viene recapitata fino alla successiva transizione del processore virtuale alla VTL di destinazione. GLI IP DI avvio e INIT destinati a una VTL inferiore vengono eliminati in un processore virtuale con una versione VTL più elevata abilitata. Poiché INIT/SIPI è bloccato, l'hypercall HvCallStartVirtualProcessor deve essere usato per avviare processori.

RFLAGS. SE

Ai fini del cambio di VTLs, RFLAGS. IF non influisce sul fatto che un'interruzione sicura attiva un commutatore VTL. Se RFLAGS. IF viene cancellata per mascherare gli interruzioni, gli interruzioni in VTL superiori causano comunque un commutatore VTL in una VTL superiore. Solo il valore TPR/CR8 del VTL superiore viene preso in considerazione quando si decide se interrompere immediatamente.

Questo comportamento influisce anche sugli interruzioni in sospeso in caso di restituzione VTL. Se RFLAGS. Se il bit IF viene cancellato per mascherare gli interruzioni in una determinata VTL e il VTL restituisce (a una VTL inferiore), l'hypervisor rivaluta eventuali interruzioni in sospeso. In questo modo si verificherà una chiamata immediata al VTL superiore.

Assistenza per la notifica di interruzione virtuale

I VTL più elevati possono registrare per ricevere una notifica se bloccano il recapito immediato di un interruzione a una VTL inferiore dello stesso processore virtuale. I VTLs superiori possono abilitare Virtual Interrupt Notification Assist (VINA) tramite un registro virtuale 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;

Ogni VTL in ogni VP ha una propria istanza VINA, oltre alla propria versione di HvRegisterVsmVina. La struttura VINA genererà un interruzione attivato perimetrale al VTL attualmente attivo quando un interruzione per il VTL inferiore è pronto per il recapito immediato.

Per evitare che si verifichino interruzioni quando questa struttura è abilitata, la struttura VINA include alcuni stati limitati. Quando viene generato un interruzione VINA, lo stato della struttura VINA viene modificato in "Asserted". L'invio di un interruzione finale al SINT associato alla struttura VINA non cancella lo stato "Asserted". Lo stato asseribile può essere cancellato solo in uno dei due modi seguenti:

  1. Lo stato può essere cancellato manualmente scrivendo nel campo VinaAsserted della struttura HV_VP_VTL_CONTROL .
  2. Lo stato viene cancellato automaticamente nella voce successiva al VTL se l'opzione "Reimposta automaticamente nella voce VTL" è abilitata nella registrazione HvRegisterVsmVina.

In questo modo, il codice in esecuzione in una VTL sicura deve ricevere solo una notifica del primo interruzione ricevuto per una VTL inferiore. Se un VTL sicuro vuole ricevere una notifica di interruzioni aggiuntive, può cancellare il campo VinaAsserted della pagina di assist VP e verrà notificata la nuova interruzione successiva.

Intercetta sicura

L'hypervisor consente a una VTL superiore di installare intercettazioni per gli eventi che si verificano nel contesto di una VTL inferiore. In questo modo, i vtls più elevati hanno un livello elevato di controllo sulle risorse VTL inferiori. È possibile usare intercettazioni sicure per proteggere le risorse critiche del sistema e impedire attacchi da vtl inferiori.

Un'intercetta sicura viene accodata alla VTL superiore e che il VTL è reso eseguibile nella VP.

Tipi di intercetta sicura

Tipo di intercetta Intercetta si applica a
Accesso alla memoria Tentativo di accedere alle protezioni criteri di gruppo stabilite da una VTL superiore.
Controllare l'accesso al registro Tentativo di accedere a un set di registri di controllo specificati da una VTL superiore.

Intercettazioni annidate

Più VTL possono installare intercettamenti sicuri per lo stesso evento in una VTL inferiore. Viene quindi stabilita una gerarchia per decidere dove vengono notificate le intercettazioni annidate. L'elenco seguente è l'ordine in cui l'intercetta viene notificata:

  1. VTL inferiore
  2. VTL superiore

Gestione delle intercettazioni sicure

Una volta che una VTL è stata notificata di un'intercetta sicura, deve intervenire in modo che il VTL inferiore possa continuare. La VTL superiore può gestire l'intercetta in diversi modi, tra cui: inserimento di un'eccezione, simulazione dell'accesso o fornitura di un proxy all'accesso. In qualsiasi caso, se è necessario modificare lo stato privato della VP VTL inferiore, è necessario usare HvCallSetVpRegisters .

Intercettazioni di registrazione sicure

Una VTL superiore può intercettare gli accessi a determinati registri di controllo. Ciò viene ottenuto impostando HvX64RegisterCrInterceptControl usando l'hypercall HvCallSetVpRegisters . L'impostazione del bit di controllo in HvX64RegisterCrInterceptControl attiverà un'intercetta per ogni accesso del registro di controllo corrispondente.

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;

Registri maschera

Per consentire un controllo più fine, un subset di registri di controllo ha anche registri maschera corrispondenti. I registri maschera possono essere usati per installare intercettazioni in un subset dei registri di controllo corrispondenti. Se un registro maschera non è definito, qualsiasi accesso (definito da HvX64RegisterCrInterceptControl) attiverà un'intercetta.

L'hypervisor supporta i registri maschera seguenti: HvX64RegisterCrInterceptCr0Mask, HvX64RegisterCrInterceptCr4Mask e HvX64RegisterCrInterceptIa32MiscEnableMask.

DMA e dispositivi

I dispositivi hanno effettivamente lo stesso livello di privilegi di VTL0. Quando VSM è abilitato, tutta la memoria allocata dal dispositivo è contrassegnata come VTL0. Tutti gli accessi DMA hanno gli stessi privilegi di VTL0.