[2/2,kvm-unit-test] nVMX x86: Check PML and EPT on vmentry of L2 guests
diff mbox series

Message ID 20180926181812.30679-3-krish.sadhukhan@oracle.com
State New
Headers show
Series
  • [1/2] nVMX x86: Add a check for reserved bits [11:0] of PML address
Related show

Commit Message

Krish Sadhukhan Sept. 26, 2018, 6:18 p.m. UTC
According to section "Checks on VMX Controls" in Intel SDM vol 3C, the
following check needs to be enforced on vmentry of L2 guests:

    If the "enable PML" VM-execution control is 1, the "enable EPT"
    VM-execution control must also be 1. In addition, the PML address
    must satisfy the following checks:

      — Bits 11:0 of the address must be 0.
      — The address should not set any bits beyond the processor’s
	physical-address width.

Signed-off-by: Krish Sadhukhan <krish.sadhukhan@oracle.com>
Reviewed-by: Mark Kanda <mark.kanda@oracle.com>
---
 x86/vmx.h       |  3 ++
 x86/vmx_tests.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 91 insertions(+), 11 deletions(-)

Comments

Jim Mattson Sept. 26, 2018, 7:24 p.m. UTC | #1
On Wed, Sep 26, 2018 at 11:18 AM, Krish Sadhukhan
<krish.sadhukhan@oracle.com> wrote:
> According to section "Checks on VMX Controls" in Intel SDM vol 3C, the
> following check needs to be enforced on vmentry of L2 guests:
>
>     If the "enable PML" VM-execution control is 1, the "enable EPT"
>     VM-execution control must also be 1. In addition, the PML address
>     must satisfy the following checks:
>
>       — Bits 11:0 of the address must be 0.
>       — The address should not set any bits beyond the processor’s
>         physical-address width.
>
> Signed-off-by: Krish Sadhukhan <krish.sadhukhan@oracle.com>
> Reviewed-by: Mark Kanda <mark.kanda@oracle.com>
> ---
>  x86/vmx.h       |  3 ++
>  x86/vmx_tests.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++-------
>  2 files changed, 91 insertions(+), 11 deletions(-)
>
> diff --git a/x86/vmx.h b/x86/vmx.h
> index 22b2892..78a786e 100644
> --- a/x86/vmx.h
> +++ b/x86/vmx.h
> @@ -448,6 +448,9 @@ enum Intr_type {
>  #define INTR_TYPE_SOFT_EXCEPTION       (6 << 8) /* software exception */
>  #define INTR_TYPE_OTHER_EVENT           (7 << 8) /* other event */
>
> +
> +#define        PMLADDR_RESV_BITS               0xfff
> +

This is unnecessary, since these are the same bits that are reserved
for all page references.

>  /*
>   * VM-instruction error numbers
>   */
> diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
> index 0e9d900..37db793 100644
> --- a/x86/vmx_tests.c
> +++ b/x86/vmx_tests.c
> @@ -3452,14 +3452,18 @@ static void test_vmcs_page_addr(const char *name,
>  static void test_vmcs_page_values(const char *name,
>                                   enum Encoding encoding,
>                                   bool ignored,
> -                                 bool xfail_beyond_mapped_ram)
> +                                 bool xfail_beyond_mapped_ram,
> +                                 u64 resv_bits)
>  {
>         unsigned i;
>         u64 orig_val = vmcs_read(encoding);
>
> -       for (i = 0; i < 64; i++)
> +       for (i = 0; i < 64; i++) {
> +               if (i & resv_bits)
> +                       continue;
>                 test_vmcs_page_addr(name, encoding, ignored,
>                                     xfail_beyond_mapped_ram, 1ul << i);
> +       }
>
>         test_vmcs_page_addr(name, encoding, ignored,
>                             xfail_beyond_mapped_ram, PAGE_SIZE - 1);
> @@ -3483,7 +3487,7 @@ static void test_vmcs_page_reference(u32 control_bit, enum Encoding field,
>                                      const char *field_name,
>                                      const char *control_name,
>                                      bool xfail_beyond_mapped_ram,
> -                                    bool control_primary)
> +                                    bool control_primary, u64 resv_bits)
>  {
>         u32 primary = vmcs_read(CPU_EXEC_CTRL0);
>         u32 secondary = vmcs_read(CPU_EXEC_CTRL1);
> @@ -3506,7 +3510,8 @@ static void test_vmcs_page_reference(u32 control_bit, enum Encoding field,
>                 vmcs_write(CPU_EXEC_CTRL0, primary | CPU_SECONDARY);
>                 vmcs_write(CPU_EXEC_CTRL1, secondary | control_bit);
>         }
> -       test_vmcs_page_values(field_name, field, false, xfail_beyond_mapped_ram);
> +       test_vmcs_page_values(field_name, field, false,
> +           xfail_beyond_mapped_ram, resv_bits);
>         report_prefix_pop();
>
>         report_prefix_pushf("%s disabled", control_name);
> @@ -3516,7 +3521,7 @@ static void test_vmcs_page_reference(u32 control_bit, enum Encoding field,
>                 vmcs_write(CPU_EXEC_CTRL0, primary & ~CPU_SECONDARY);
>                 vmcs_write(CPU_EXEC_CTRL1, secondary & ~control_bit);
>         }
> -       test_vmcs_page_values(field_name, field, true, false);
> +       test_vmcs_page_values(field_name, field, true, false, resv_bits);
>         report_prefix_pop();
>
>         vmcs_write(field, page_addr);
> @@ -3533,10 +3538,10 @@ static void test_io_bitmaps(void)
>  {
>         test_vmcs_page_reference(CPU_IO_BITMAP, IO_BITMAP_A,
>                                  "I/O bitmap A", "Use I/O bitmaps", false,
> -                                true);
> +                                true, 0);
>         test_vmcs_page_reference(CPU_IO_BITMAP, IO_BITMAP_B,
>                                  "I/O bitmap B", "Use I/O bitmaps", false,
> -                                true);
> +                                true, 0);
>  }
>
>  /*
> @@ -3549,7 +3554,7 @@ static void test_msr_bitmap(void)
>  {
>         test_vmcs_page_reference(CPU_MSR_BITMAP, MSR_BITMAP,
>                                  "MSR bitmap", "Use MSR bitmaps", false,
> -                                true);
> +                                true, 0);
>  }
>
>  /*
> @@ -3564,7 +3569,7 @@ static void test_apic_virt_addr(void)
>  {
>         test_vmcs_page_reference(CPU_TPR_SHADOW, APIC_VIRT_ADDR,
>                                  "virtual-APIC address", "Use TPR shadow",
> -                                true, true);
> +                                true, true, 0);
>  }
>
>  /*
> @@ -3583,7 +3588,7 @@ static void test_apic_access_addr(void)
>
>         test_vmcs_page_reference(CPU_VIRT_APIC_ACCESSES, APIC_ACCS_ADDR,
>                                  "APIC-access address",
> -                                "virtualize APIC-accesses", false, false);
> +                                "virtualize APIC-accesses", false, false, 0);
>  }
>
>  static bool set_bit_pattern(u8 mask, u32 *secondary)
> @@ -3869,7 +3874,8 @@ static void test_posted_intr(void)
>         test_pi_desc_addr(0x00, true);
>         test_pi_desc_addr(0xc000, true);
>
> -       test_vmcs_page_values("process-posted interrupts", POSTED_INTR_DESC_ADDR, false, false);
> +       test_vmcs_page_values("process-posted interrupts",
> +           POSTED_INTR_DESC_ADDR, false, false, 0);
>
>         vmcs_write(CPU_EXEC_CTRL0, saved_primary);
>         vmcs_write(CPU_EXEC_CTRL1, saved_secondary);
> @@ -4408,6 +4414,76 @@ done:
>         vmcs_write(PIN_CONTROLS, pin_ctrls);
>  }
>
> +/*
> + * If the “enable PML” VM-execution control is 1, the “enable EPT”
> + * VM-execution control must also be 1. In addition, the PML address
> + * must satisfy the following checks:
> + *
> + *    — Bits 11:0 of the address must be 0.
> + *    — The address should not set any bits beyond the processor’s
> + *     physical-address width.
> +
> + *  [Intel SDM]
> + */
> +static void test_pml(void)
> +{
> +       u32 primary_saved = vmcs_read(CPU_EXEC_CTRL0);
> +       u32 secondary_saved = vmcs_read(CPU_EXEC_CTRL1);
> +       u32 primary = primary_saved;
> +       u32 secondary = secondary_saved;
> +       u64 pml_addr;
> +       int i;
> +
> +       if (!((ctrl_cpu_rev[0].clr & CPU_SECONDARY) &&
> +           (ctrl_cpu_rev[1].clr & CPU_EPT) && (ctrl_cpu_rev[1].clr & CPU_PML))) {
> +               test_skip("\"Secondary execution\" control or \"enable EPT\" control or \"enable PML\" control is not supported !");
> +               return;
> +       }
> +
> +       primary |= CPU_SECONDARY;
> +       vmcs_write(CPU_EXEC_CTRL0, primary);
> +       secondary &= ~(CPU_PML | CPU_EPT);
> +       vmcs_write(CPU_EXEC_CTRL1, secondary);
> +       report_prefix_pushf("enable-PML disabled, enable-EPT disabled");
> +       test_vmx_controls(true, false);
> +       report_prefix_pop();
> +
> +       secondary |= CPU_PML;
> +       vmcs_write(CPU_EXEC_CTRL1, secondary);
> +       report_prefix_pushf("enable-PML enabled, enable-EPT disabled");
> +       test_vmx_controls(false, false);
> +       report_prefix_pop();
> +
> +       secondary |= CPU_EPT;
> +       vmcs_write(CPU_EXEC_CTRL1, secondary);
> +       report_prefix_pushf("enable-PML enabled, enable-EPT enabled");
> +       test_vmx_controls(true, false);
> +       report_prefix_pop();
> +
> +       secondary &= ~CPU_PML;
> +       vmcs_write(CPU_EXEC_CTRL1, secondary);
> +       report_prefix_pushf("enable-PML disabled, enable EPT enabled");
> +       test_vmx_controls(true, false);
> +       report_prefix_pop();
> +
> +       secondary |= CPU_PML;
> +       vmcs_write(CPU_EXEC_CTRL1, secondary);
> +
> +       pml_addr = (u64) phys_to_virt(vmcs_read(PMLADDR));
> +       for (i = 0; i < 12; i++) {
> +               pml_addr |= 1ull << i;
> +               vmcs_write(PMLADDR, virt_to_phys((void *) pml_addr));
> +               report_prefix_pushf("PML address 0x%lx", pml_addr);
> +               test_vmx_controls(false, false);
> +               report_prefix_pop();
> +       }
> +
> +       test_vmcs_page_reference(CPU_PML, PMLADDR, "PML address",
> +           "PML", false, false, PMLADDR_RESV_BITS);
> +
> +       vmcs_write(CPU_EXEC_CTRL0, primary_saved);
> +       vmcs_write(CPU_EXEC_CTRL1, secondary_saved);
> +}

Except for the "enable EPT" requirement, this can all be done with a
variant of test_vmcs_page_reference that takes a secondary
processor-based control bit rather than a primary processor-based
control-bit.
Krish Sadhukhan Sept. 26, 2018, 10:47 p.m. UTC | #2
On 09/26/2018 12:24 PM, Jim Mattson wrote:
> On Wed, Sep 26, 2018 at 11:18 AM, Krish Sadhukhan
> <krish.sadhukhan@oracle.com> wrote:
>> According to section "Checks on VMX Controls" in Intel SDM vol 3C, the
>> following check needs to be enforced on vmentry of L2 guests:
>>
>>      If the "enable PML" VM-execution control is 1, the "enable EPT"
>>      VM-execution control must also be 1. In addition, the PML address
>>      must satisfy the following checks:
>>
>>        — Bits 11:0 of the address must be 0.
>>        — The address should not set any bits beyond the processor’s
>>          physical-address width.
>>
>> Signed-off-by: Krish Sadhukhan <krish.sadhukhan@oracle.com>
>> Reviewed-by: Mark Kanda <mark.kanda@oracle.com>
>> ---
>>   x86/vmx.h       |  3 ++
>>   x86/vmx_tests.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++-------
>>   2 files changed, 91 insertions(+), 11 deletions(-)
>>
>> diff --git a/x86/vmx.h b/x86/vmx.h
>> index 22b2892..78a786e 100644
>> --- a/x86/vmx.h
>> +++ b/x86/vmx.h
>> @@ -448,6 +448,9 @@ enum Intr_type {
>>   #define INTR_TYPE_SOFT_EXCEPTION       (6 << 8) /* software exception */
>>   #define INTR_TYPE_OTHER_EVENT           (7 << 8) /* other event */
>>
>> +
>> +#define        PMLADDR_RESV_BITS               0xfff
>> +
> This is unnecessary, since these are the same bits that are reserved
> for all page references.

Agreed.

>
>>   /*
>>    * VM-instruction error numbers
>>    */
>> diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
>> index 0e9d900..37db793 100644
>> --- a/x86/vmx_tests.c
>> +++ b/x86/vmx_tests.c
>> @@ -3452,14 +3452,18 @@ static void test_vmcs_page_addr(const char *name,
>>   static void test_vmcs_page_values(const char *name,
>>                                    enum Encoding encoding,
>>                                    bool ignored,
>> -                                 bool xfail_beyond_mapped_ram)
>> +                                 bool xfail_beyond_mapped_ram,
>> +                                 u64 resv_bits)
>>   {
>>          unsigned i;
>>          u64 orig_val = vmcs_read(encoding);
>>
>> -       for (i = 0; i < 64; i++)
>> +       for (i = 0; i < 64; i++) {
>> +               if (i & resv_bits)
>> +                       continue;
>>                  test_vmcs_page_addr(name, encoding, ignored,
>>                                      xfail_beyond_mapped_ram, 1ul << i);
>> +       }
>>
>>          test_vmcs_page_addr(name, encoding, ignored,
>>                              xfail_beyond_mapped_ram, PAGE_SIZE - 1);
>> @@ -3483,7 +3487,7 @@ static void test_vmcs_page_reference(u32 control_bit, enum Encoding field,
>>                                       const char *field_name,
>>                                       const char *control_name,
>>                                       bool xfail_beyond_mapped_ram,
>> -                                    bool control_primary)
>> +                                    bool control_primary, u64 resv_bits)
>>   {
>>          u32 primary = vmcs_read(CPU_EXEC_CTRL0);
>>          u32 secondary = vmcs_read(CPU_EXEC_CTRL1);
>> @@ -3506,7 +3510,8 @@ static void test_vmcs_page_reference(u32 control_bit, enum Encoding field,
>>                  vmcs_write(CPU_EXEC_CTRL0, primary | CPU_SECONDARY);
>>                  vmcs_write(CPU_EXEC_CTRL1, secondary | control_bit);
>>          }
>> -       test_vmcs_page_values(field_name, field, false, xfail_beyond_mapped_ram);
>> +       test_vmcs_page_values(field_name, field, false,
>> +           xfail_beyond_mapped_ram, resv_bits);
>>          report_prefix_pop();
>>
>>          report_prefix_pushf("%s disabled", control_name);
>> @@ -3516,7 +3521,7 @@ static void test_vmcs_page_reference(u32 control_bit, enum Encoding field,
>>                  vmcs_write(CPU_EXEC_CTRL0, primary & ~CPU_SECONDARY);
>>                  vmcs_write(CPU_EXEC_CTRL1, secondary & ~control_bit);
>>          }
>> -       test_vmcs_page_values(field_name, field, true, false);
>> +       test_vmcs_page_values(field_name, field, true, false, resv_bits);
>>          report_prefix_pop();
>>
>>          vmcs_write(field, page_addr);
>> @@ -3533,10 +3538,10 @@ static void test_io_bitmaps(void)
>>   {
>>          test_vmcs_page_reference(CPU_IO_BITMAP, IO_BITMAP_A,
>>                                   "I/O bitmap A", "Use I/O bitmaps", false,
>> -                                true);
>> +                                true, 0);
>>          test_vmcs_page_reference(CPU_IO_BITMAP, IO_BITMAP_B,
>>                                   "I/O bitmap B", "Use I/O bitmaps", false,
>> -                                true);
>> +                                true, 0);
>>   }
>>
>>   /*
>> @@ -3549,7 +3554,7 @@ static void test_msr_bitmap(void)
>>   {
>>          test_vmcs_page_reference(CPU_MSR_BITMAP, MSR_BITMAP,
>>                                   "MSR bitmap", "Use MSR bitmaps", false,
>> -                                true);
>> +                                true, 0);
>>   }
>>
>>   /*
>> @@ -3564,7 +3569,7 @@ static void test_apic_virt_addr(void)
>>   {
>>          test_vmcs_page_reference(CPU_TPR_SHADOW, APIC_VIRT_ADDR,
>>                                   "virtual-APIC address", "Use TPR shadow",
>> -                                true, true);
>> +                                true, true, 0);
>>   }
>>
>>   /*
>> @@ -3583,7 +3588,7 @@ static void test_apic_access_addr(void)
>>
>>          test_vmcs_page_reference(CPU_VIRT_APIC_ACCESSES, APIC_ACCS_ADDR,
>>                                   "APIC-access address",
>> -                                "virtualize APIC-accesses", false, false);
>> +                                "virtualize APIC-accesses", false, false, 0);
>>   }
>>
>>   static bool set_bit_pattern(u8 mask, u32 *secondary)
>> @@ -3869,7 +3874,8 @@ static void test_posted_intr(void)
>>          test_pi_desc_addr(0x00, true);
>>          test_pi_desc_addr(0xc000, true);
>>
>> -       test_vmcs_page_values("process-posted interrupts", POSTED_INTR_DESC_ADDR, false, false);
>> +       test_vmcs_page_values("process-posted interrupts",
>> +           POSTED_INTR_DESC_ADDR, false, false, 0);
>>
>>          vmcs_write(CPU_EXEC_CTRL0, saved_primary);
>>          vmcs_write(CPU_EXEC_CTRL1, saved_secondary);
>> @@ -4408,6 +4414,76 @@ done:
>>          vmcs_write(PIN_CONTROLS, pin_ctrls);
>>   }
>>
>> +/*
>> + * If the “enable PML” VM-execution control is 1, the “enable EPT”
>> + * VM-execution control must also be 1. In addition, the PML address
>> + * must satisfy the following checks:
>> + *
>> + *    — Bits 11:0 of the address must be 0.
>> + *    — The address should not set any bits beyond the processor’s
>> + *     physical-address width.
>> +
>> + *  [Intel SDM]
>> + */
>> +static void test_pml(void)
>> +{
>> +       u32 primary_saved = vmcs_read(CPU_EXEC_CTRL0);
>> +       u32 secondary_saved = vmcs_read(CPU_EXEC_CTRL1);
>> +       u32 primary = primary_saved;
>> +       u32 secondary = secondary_saved;
>> +       u64 pml_addr;
>> +       int i;
>> +
>> +       if (!((ctrl_cpu_rev[0].clr & CPU_SECONDARY) &&
>> +           (ctrl_cpu_rev[1].clr & CPU_EPT) && (ctrl_cpu_rev[1].clr & CPU_PML))) {
>> +               test_skip("\"Secondary execution\" control or \"enable EPT\" control or \"enable PML\" control is not supported !");
>> +               return;
>> +       }
>> +
>> +       primary |= CPU_SECONDARY;
>> +       vmcs_write(CPU_EXEC_CTRL0, primary);
>> +       secondary &= ~(CPU_PML | CPU_EPT);
>> +       vmcs_write(CPU_EXEC_CTRL1, secondary);
>> +       report_prefix_pushf("enable-PML disabled, enable-EPT disabled");
>> +       test_vmx_controls(true, false);
>> +       report_prefix_pop();
>> +
>> +       secondary |= CPU_PML;
>> +       vmcs_write(CPU_EXEC_CTRL1, secondary);
>> +       report_prefix_pushf("enable-PML enabled, enable-EPT disabled");
>> +       test_vmx_controls(false, false);
>> +       report_prefix_pop();
>> +
>> +       secondary |= CPU_EPT;
>> +       vmcs_write(CPU_EXEC_CTRL1, secondary);
>> +       report_prefix_pushf("enable-PML enabled, enable-EPT enabled");
>> +       test_vmx_controls(true, false);
>> +       report_prefix_pop();
>> +
>> +       secondary &= ~CPU_PML;
>> +       vmcs_write(CPU_EXEC_CTRL1, secondary);
>> +       report_prefix_pushf("enable-PML disabled, enable EPT enabled");
>> +       test_vmx_controls(true, false);
>> +       report_prefix_pop();
>> +
>> +       secondary |= CPU_PML;
>> +       vmcs_write(CPU_EXEC_CTRL1, secondary);
>> +
>> +       pml_addr = (u64) phys_to_virt(vmcs_read(PMLADDR));
>> +       for (i = 0; i < 12; i++) {
>> +               pml_addr |= 1ull << i;
>> +               vmcs_write(PMLADDR, virt_to_phys((void *) pml_addr));
>> +               report_prefix_pushf("PML address 0x%lx", pml_addr);
>> +               test_vmx_controls(false, false);
>> +               report_prefix_pop();
>> +       }
>> +
>> +       test_vmcs_page_reference(CPU_PML, PMLADDR, "PML address",
>> +           "PML", false, false, PMLADDR_RESV_BITS);
>> +
>> +       vmcs_write(CPU_EXEC_CTRL0, primary_saved);
>> +       vmcs_write(CPU_EXEC_CTRL1, secondary_saved);
>> +}
> Except for the "enable EPT" requirement, this can all be done with a
> variant of test_vmcs_page_reference that takes a secondary
> processor-based control bit rather than a primary processor-based
> control-bit.

It seems that for this patch, the existing test_vmcs_page_reference() is 
sufficient. Also, test_vmcs_page_addr() already covers the cases for 
reserved bits (sorry, I missed it earlier). So all I need to do is just 
call test_vmcs_page_reference() after the "enable EPT" part.

Patch
diff mbox series

diff --git a/x86/vmx.h b/x86/vmx.h
index 22b2892..78a786e 100644
--- a/x86/vmx.h
+++ b/x86/vmx.h
@@ -448,6 +448,9 @@  enum Intr_type {
 #define INTR_TYPE_SOFT_EXCEPTION	(6 << 8) /* software exception */
 #define INTR_TYPE_OTHER_EVENT           (7 << 8) /* other event */
 
+
+#define	PMLADDR_RESV_BITS		0xfff
+
 /*
  * VM-instruction error numbers
  */
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index 0e9d900..37db793 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -3452,14 +3452,18 @@  static void test_vmcs_page_addr(const char *name,
 static void test_vmcs_page_values(const char *name,
 				  enum Encoding encoding,
 				  bool ignored,
-				  bool xfail_beyond_mapped_ram)
+				  bool xfail_beyond_mapped_ram,
+				  u64 resv_bits)
 {
 	unsigned i;
 	u64 orig_val = vmcs_read(encoding);
 
-	for (i = 0; i < 64; i++)
+	for (i = 0; i < 64; i++) {
+		if (i & resv_bits)
+			continue;
 		test_vmcs_page_addr(name, encoding, ignored,
 				    xfail_beyond_mapped_ram, 1ul << i);
+	}
 
 	test_vmcs_page_addr(name, encoding, ignored,
 			    xfail_beyond_mapped_ram, PAGE_SIZE - 1);
@@ -3483,7 +3487,7 @@  static void test_vmcs_page_reference(u32 control_bit, enum Encoding field,
 				     const char *field_name,
 				     const char *control_name,
 				     bool xfail_beyond_mapped_ram,
-				     bool control_primary)
+				     bool control_primary, u64 resv_bits)
 {
 	u32 primary = vmcs_read(CPU_EXEC_CTRL0);
 	u32 secondary = vmcs_read(CPU_EXEC_CTRL1);
@@ -3506,7 +3510,8 @@  static void test_vmcs_page_reference(u32 control_bit, enum Encoding field,
 		vmcs_write(CPU_EXEC_CTRL0, primary | CPU_SECONDARY);
 		vmcs_write(CPU_EXEC_CTRL1, secondary | control_bit);
 	}
-	test_vmcs_page_values(field_name, field, false, xfail_beyond_mapped_ram);
+	test_vmcs_page_values(field_name, field, false,
+	    xfail_beyond_mapped_ram, resv_bits);
 	report_prefix_pop();
 
 	report_prefix_pushf("%s disabled", control_name);
@@ -3516,7 +3521,7 @@  static void test_vmcs_page_reference(u32 control_bit, enum Encoding field,
 		vmcs_write(CPU_EXEC_CTRL0, primary & ~CPU_SECONDARY);
 		vmcs_write(CPU_EXEC_CTRL1, secondary & ~control_bit);
 	}
-	test_vmcs_page_values(field_name, field, true, false);
+	test_vmcs_page_values(field_name, field, true, false, resv_bits);
 	report_prefix_pop();
 
 	vmcs_write(field, page_addr);
@@ -3533,10 +3538,10 @@  static void test_io_bitmaps(void)
 {
 	test_vmcs_page_reference(CPU_IO_BITMAP, IO_BITMAP_A,
 				 "I/O bitmap A", "Use I/O bitmaps", false,
-				 true);
+				 true, 0);
 	test_vmcs_page_reference(CPU_IO_BITMAP, IO_BITMAP_B,
 				 "I/O bitmap B", "Use I/O bitmaps", false,
-				 true);
+				 true, 0);
 }
 
 /*
@@ -3549,7 +3554,7 @@  static void test_msr_bitmap(void)
 {
 	test_vmcs_page_reference(CPU_MSR_BITMAP, MSR_BITMAP,
 				 "MSR bitmap", "Use MSR bitmaps", false,
-				 true);
+				 true, 0);
 }
 
 /*
@@ -3564,7 +3569,7 @@  static void test_apic_virt_addr(void)
 {
 	test_vmcs_page_reference(CPU_TPR_SHADOW, APIC_VIRT_ADDR,
 				 "virtual-APIC address", "Use TPR shadow",
-				 true, true);
+				 true, true, 0);
 }
 
 /*
@@ -3583,7 +3588,7 @@  static void test_apic_access_addr(void)
 
 	test_vmcs_page_reference(CPU_VIRT_APIC_ACCESSES, APIC_ACCS_ADDR,
 				 "APIC-access address",
-				 "virtualize APIC-accesses", false, false);
+				 "virtualize APIC-accesses", false, false, 0);
 }
 
 static bool set_bit_pattern(u8 mask, u32 *secondary)
@@ -3869,7 +3874,8 @@  static void test_posted_intr(void)
 	test_pi_desc_addr(0x00, true);
 	test_pi_desc_addr(0xc000, true);
 
-	test_vmcs_page_values("process-posted interrupts", POSTED_INTR_DESC_ADDR, false, false);
+	test_vmcs_page_values("process-posted interrupts",
+	    POSTED_INTR_DESC_ADDR, false, false, 0);
 
 	vmcs_write(CPU_EXEC_CTRL0, saved_primary);
 	vmcs_write(CPU_EXEC_CTRL1, saved_secondary);
@@ -4408,6 +4414,76 @@  done:
 	vmcs_write(PIN_CONTROLS, pin_ctrls);
 }
 
+/*
+ * If the “enable PML” VM-execution control is 1, the “enable EPT”
+ * VM-execution control must also be 1. In addition, the PML address
+ * must satisfy the following checks:
+ *
+ *    — Bits 11:0 of the address must be 0.
+ *    — The address should not set any bits beyond the processor’s
+ *	physical-address width.
+
+ *  [Intel SDM]
+ */
+static void test_pml(void)
+{
+	u32 primary_saved = vmcs_read(CPU_EXEC_CTRL0);
+	u32 secondary_saved = vmcs_read(CPU_EXEC_CTRL1);
+	u32 primary = primary_saved;
+	u32 secondary = secondary_saved;
+	u64 pml_addr;
+	int i;
+
+	if (!((ctrl_cpu_rev[0].clr & CPU_SECONDARY) &&
+	    (ctrl_cpu_rev[1].clr & CPU_EPT) && (ctrl_cpu_rev[1].clr & CPU_PML))) {
+		test_skip("\"Secondary execution\" control or \"enable EPT\" control or \"enable PML\" control is not supported !");
+		return;
+	}
+
+	primary |= CPU_SECONDARY;
+	vmcs_write(CPU_EXEC_CTRL0, primary);
+	secondary &= ~(CPU_PML | CPU_EPT);
+	vmcs_write(CPU_EXEC_CTRL1, secondary);
+	report_prefix_pushf("enable-PML disabled, enable-EPT disabled");
+	test_vmx_controls(true, false);
+	report_prefix_pop();
+
+	secondary |= CPU_PML;
+	vmcs_write(CPU_EXEC_CTRL1, secondary);
+	report_prefix_pushf("enable-PML enabled, enable-EPT disabled");
+	test_vmx_controls(false, false);
+	report_prefix_pop();
+
+	secondary |= CPU_EPT;
+	vmcs_write(CPU_EXEC_CTRL1, secondary);
+	report_prefix_pushf("enable-PML enabled, enable-EPT enabled");
+	test_vmx_controls(true, false);
+	report_prefix_pop();
+
+	secondary &= ~CPU_PML;
+	vmcs_write(CPU_EXEC_CTRL1, secondary);
+	report_prefix_pushf("enable-PML disabled, enable EPT enabled");
+	test_vmx_controls(true, false);
+	report_prefix_pop();
+
+	secondary |= CPU_PML;
+	vmcs_write(CPU_EXEC_CTRL1, secondary);
+
+	pml_addr = (u64) phys_to_virt(vmcs_read(PMLADDR));
+	for (i = 0; i < 12; i++) {
+		pml_addr |= 1ull << i;
+		vmcs_write(PMLADDR, virt_to_phys((void *) pml_addr));
+		report_prefix_pushf("PML address 0x%lx", pml_addr);
+		test_vmx_controls(false, false);
+		report_prefix_pop();
+	}
+
+	test_vmcs_page_reference(CPU_PML, PMLADDR, "PML address",
+	    "PML", false, false, PMLADDR_RESV_BITS);
+
+	vmcs_write(CPU_EXEC_CTRL0, primary_saved);
+	vmcs_write(CPU_EXEC_CTRL1, secondary_saved);
+}
 
 /*
  * Check that the virtual CPU checks all of the VMX controls as
@@ -4431,6 +4507,7 @@  static void vmx_controls_test(void)
 	test_apic_ctls();
 	test_tpr_threshold();
 	test_nmi_ctrls();
+	test_pml();
 	test_invalid_event_injection();
 }