The VMXON Region is a per logical processor data structure allocated and used by the processor for internal operations related to VMX

  • It’s required to be allocated an initialized prior to entering VMX operation using the vmxon instruction. The vmxon instruction operand is required to be the physical address of the VMXON region, otherwise called the VMXON pointer.
  • A VMM can (should) use different VMXON Regions for each logical processor otherwise the behavior is “undefined”.

Como o layout do VMXON region nao é arquiteturalmente definido (isto é, os acessos de leitura e escrita podem variar para cada processador), não vamos ter informações do que exatamente ocorre na VMXON region. Acredito que a intel nao da mais detalhes disso por justamente depender do processador, sendo o comportamento dado como “indefinido”.


REGION INITIALIZATION:

The VMXON region is supposed to be allocated on a naturally aligned page (4-KByte) allocation aligned on a page boundary. To do this simply and with the guarantee that we are always returned a page aligned allocation, use MmAllocateContiguousMemory.

The VMCS revision identifier (see VMXON region format table above) is needed. It’s necessary to define a structure for the IA32_VMX_BASIC MSR and read the value of this MSR:

union __vmx_basic_msr_t
{
  unsigned __int64 control;
  struct
  {
    unsigned __int64 vmcs_revision_identifier : 31;
    unsigned __int64 always_0 : 1;
    unsigned __int64 vmxon_region_size : 13;
    unsigned __int64 reserved_1 : 3;
    unsigned __int64 vmxon_physical_address_width : 1;
    unsigned __int64 dual_monitor_smi : 1;
    unsigned __int64 memory_type : 4;
    unsigned __int64 io_instruction_reporting : 1;
    unsigned __int64 true_controls : 1;
  } bits;
};
  • The VMXON region has a similar structure to the VMCS, so we’re also going to define a __vmcs_t pointer for the VMXON region allocation. This will also make the writing of the VMCS revision identifier trivial.

  • Following the allocation of our VMXON region, we need to get the physical address of the region and store it in our vCPU context for later use. We’ll use MmGetPhysicalAddress to obtain the physical address of the structure.

VMXON region Initialization code:

int init_vmxon( struct __vcpu_t *vcpu )
{
  union __vmx_basic_msr_t vmx_basic = { 0 };
  struct __vmcs_t *vmxon;
  PHYSICAL_ADDRESS physical_max;
  if( !vcpu ) {
    log_error( "VMXON region could not be initialized. vcpu was null.\n" );
    return FALSE;
  }
  vmx_basic.control = __readmsr( IA32_VMX_BASIC );
  physical_max.QuadPart = ~0ULL;
  if( vmx_basic.bits.vmxon_region_size > PAGE_SIZE ) {
    vcpu->vmxon = MmAllocateContiguousMemory( PAGE_SIZE, physical_max );
  } else {
    vcpu->vmxon = MmAllocateContiguousMemory( vmx_basic.bits.vmxon_region_size, physical_max );
  }
  vcpu->vmxon_physical = MmGetPhysicalAddress( vcpu->vmxon ).QuadPart;
  vmxon = vcpu->vmxon;
  RtlSecureZeroMemory( vmxon, PAGE_SIZE );
  vmxon->header.all = vmx_basic.bits.vmcs_revision_identifier;
  log_debug( "VMXON for vcpu %d initialized:\n\t-> VA: %llX\n\t-> PA: %llX\n\t-> REV: %X\n",
    KeGetCurrentProcessorNumber( ),
    vcpu->vmxon,
    vcpu->vmxon_physical,
    vcpu->vmxon->header.all );
  return TRUE;
}

🌱 Back to Garden