blob: b791f1aca51d2606b331a1407986d4fbcfed9563 [file] [log] [blame]
#include "svm.h"
#include "vm.h"
#include "alloc_page.h"
#include "vmalloc.h"
static void *scratch_page;
static void null_test(struct svm_test *test)
{
}
static void npt_np_prepare(struct svm_test *test)
{
u64 *pte;
scratch_page = alloc_page();
pte = npt_get_pte((u64) scratch_page);
*pte &= ~1ULL;
}
static void npt_np_test(struct svm_test *test)
{
(void)*(volatile u64 *)scratch_page;
}
static bool npt_np_check(struct svm_test *test)
{
u64 *pte = npt_get_pte((u64) scratch_page);
*pte |= 1ULL;
return (vmcb->control.exit_code == SVM_EXIT_NPF)
&& (vmcb->control.exit_info_1 == 0x100000004ULL);
}
static void npt_nx_prepare(struct svm_test *test)
{
u64 *pte;
test->scratch = rdmsr(MSR_EFER);
wrmsr(MSR_EFER, test->scratch | EFER_NX);
/* Clear the guest's EFER.NX, it should not affect NPT behavior. */
vmcb->save.efer &= ~EFER_NX;
pte = npt_get_pte((u64) null_test);
*pte |= PT64_NX_MASK;
}
static bool npt_nx_check(struct svm_test *test)
{
u64 *pte = npt_get_pte((u64) null_test);
wrmsr(MSR_EFER, test->scratch);
*pte &= ~PT64_NX_MASK;
return (vmcb->control.exit_code == SVM_EXIT_NPF)
&& (vmcb->control.exit_info_1 == 0x100000015ULL);
}
static void npt_us_prepare(struct svm_test *test)
{
u64 *pte;
scratch_page = alloc_page();
pte = npt_get_pte((u64) scratch_page);
*pte &= ~(1ULL << 2);
}
static void npt_us_test(struct svm_test *test)
{
(void)*(volatile u64 *)scratch_page;
}
static bool npt_us_check(struct svm_test *test)
{
u64 *pte = npt_get_pte((u64) scratch_page);
*pte |= (1ULL << 2);
return (vmcb->control.exit_code == SVM_EXIT_NPF)
&& (vmcb->control.exit_info_1 == 0x100000005ULL);
}
static void npt_rw_prepare(struct svm_test *test)
{
u64 *pte;
pte = npt_get_pte(0x80000);
*pte &= ~(1ULL << 1);
}
static void npt_rw_test(struct svm_test *test)
{
u64 *data = (void *)(0x80000);
*data = 0;
}
static bool npt_rw_check(struct svm_test *test)
{
u64 *pte = npt_get_pte(0x80000);
*pte |= (1ULL << 1);
return (vmcb->control.exit_code == SVM_EXIT_NPF)
&& (vmcb->control.exit_info_1 == 0x100000007ULL);
}
static void npt_rw_pfwalk_prepare(struct svm_test *test)
{
u64 *pte;
pte = npt_get_pte(read_cr3());
*pte &= ~(1ULL << 1);
}
static bool npt_rw_pfwalk_check(struct svm_test *test)
{
u64 *pte = npt_get_pte(read_cr3());
*pte |= (1ULL << 1);
return (vmcb->control.exit_code == SVM_EXIT_NPF)
&& (vmcb->control.exit_info_1 == 0x200000007ULL)
&& (vmcb->control.exit_info_2 == read_cr3());
}
static void npt_l1mmio_prepare(struct svm_test *test)
{
}
u32 nested_apic_version1;
u32 nested_apic_version2;
static void npt_l1mmio_test(struct svm_test *test)
{
volatile u32 *data = (volatile void *)(0xfee00030UL);
nested_apic_version1 = *data;
nested_apic_version2 = *data;
}
static bool npt_l1mmio_check(struct svm_test *test)
{
volatile u32 *data = (volatile void *)(0xfee00030);
u32 lvr = *data;
return nested_apic_version1 == lvr && nested_apic_version2 == lvr;
}
static void npt_rw_l1mmio_prepare(struct svm_test *test)
{
u64 *pte;
pte = npt_get_pte(0xfee00080);
*pte &= ~(1ULL << 1);
}
static void npt_rw_l1mmio_test(struct svm_test *test)
{
volatile u32 *data = (volatile void *)(0xfee00080);
*data = *data;
}
static bool npt_rw_l1mmio_check(struct svm_test *test)
{
u64 *pte = npt_get_pte(0xfee00080);
*pte |= (1ULL << 1);
return (vmcb->control.exit_code == SVM_EXIT_NPF)
&& (vmcb->control.exit_info_1 == 0x100000007ULL);
}
static void basic_guest_main(struct svm_test *test)
{
}
static void __svm_npt_rsvd_bits_test(u64 * pxe, u64 rsvd_bits, u64 efer,
ulong cr4, u64 guest_efer, ulong guest_cr4)
{
u64 pxe_orig = *pxe;
int exit_reason;
u64 pfec;
wrmsr(MSR_EFER, efer);
write_cr4(cr4);
vmcb->save.efer = guest_efer;
vmcb->save.cr4 = guest_cr4;
*pxe |= rsvd_bits;
exit_reason = svm_vmrun();
report(exit_reason == SVM_EXIT_NPF,
"Wanted #NPF on rsvd bits = 0x%lx, got exit = 0x%x", rsvd_bits,
exit_reason);
if (pxe == npt_get_pdpe((u64) basic_guest_main) || pxe == npt_get_pml4e()) {
/*
* The guest's page tables will blow up on a bad PDPE/PML4E,
* before starting the final walk of the guest page.
*/
pfec = 0x20000000full;
} else {
/* RSVD #NPF on final walk of guest page. */
pfec = 0x10000000dULL;
/* PFEC.FETCH=1 if NX=1 *or* SMEP=1. */
if ((cr4 & X86_CR4_SMEP) || (efer & EFER_NX))
pfec |= 0x10;
}
report(vmcb->control.exit_info_1 == pfec,
"Wanted PFEC = 0x%lx, got PFEC = %lx, PxE = 0x%lx. "
"host.NX = %u, host.SMEP = %u, guest.NX = %u, guest.SMEP = %u",
pfec, vmcb->control.exit_info_1, *pxe,
!!(efer & EFER_NX), !!(cr4 & X86_CR4_SMEP),
!!(guest_efer & EFER_NX), !!(guest_cr4 & X86_CR4_SMEP));
*pxe = pxe_orig;
}
static void _svm_npt_rsvd_bits_test(u64 * pxe, u64 pxe_rsvd_bits, u64 efer,
ulong cr4, u64 guest_efer, ulong guest_cr4)
{
u64 rsvd_bits;
int i;
/*
* RDTSC or RDRAND can sometimes fail to generate a valid reserved bits
*/
if (!pxe_rsvd_bits) {
report_skip
("svm_npt_rsvd_bits_test: Reserved bits are not valid");
return;
}
/*
* Test all combinations of guest/host EFER.NX and CR4.SMEP. If host
* EFER.NX=0, use NX as the reserved bit, otherwise use the passed in
* @pxe_rsvd_bits.
*/
for (i = 0; i < 16; i++) {
if (i & 1) {
rsvd_bits = pxe_rsvd_bits;
efer |= EFER_NX;
} else {
rsvd_bits = PT64_NX_MASK;
efer &= ~EFER_NX;
}
if (i & 2)
cr4 |= X86_CR4_SMEP;
else
cr4 &= ~X86_CR4_SMEP;
if (i & 4)
guest_efer |= EFER_NX;
else
guest_efer &= ~EFER_NX;
if (i & 8)
guest_cr4 |= X86_CR4_SMEP;
else
guest_cr4 &= ~X86_CR4_SMEP;
__svm_npt_rsvd_bits_test(pxe, rsvd_bits, efer, cr4,
guest_efer, guest_cr4);
}
}
static u64 get_random_bits(u64 hi, u64 low)
{
unsigned retry = 5;
u64 rsvd_bits = 0;
if (this_cpu_has(X86_FEATURE_RDRAND)) {
do {
rsvd_bits = (rdrand() << low) & GENMASK_ULL(hi, low);
retry--;
} while (!rsvd_bits && retry);
}
if (!rsvd_bits) {
retry = 5;
do {
rsvd_bits = (rdtsc() << low) & GENMASK_ULL(hi, low);
retry--;
} while (!rsvd_bits && retry);
}
return rsvd_bits;
}
static void svm_npt_rsvd_bits_test(void)
{
u64 saved_efer, host_efer, sg_efer, guest_efer;
ulong saved_cr4, host_cr4, sg_cr4, guest_cr4;
if (!npt_supported()) {
report_skip("NPT not supported");
return;
}
saved_efer = host_efer = rdmsr(MSR_EFER);
saved_cr4 = host_cr4 = read_cr4();
sg_efer = guest_efer = vmcb->save.efer;
sg_cr4 = guest_cr4 = vmcb->save.cr4;
test_set_guest(basic_guest_main);
/*
* 4k PTEs don't have reserved bits if MAXPHYADDR >= 52, just skip the
* sub-test. The NX test is still valid, but the extra bit of coverage
* isn't worth the extra complexity.
*/
if (cpuid_maxphyaddr() >= 52)
goto skip_pte_test;
_svm_npt_rsvd_bits_test(npt_get_pte((u64) basic_guest_main),
get_random_bits(51, cpuid_maxphyaddr()),
host_efer, host_cr4, guest_efer, guest_cr4);
skip_pte_test:
_svm_npt_rsvd_bits_test(npt_get_pde((u64) basic_guest_main),
get_random_bits(20, 13) | PT_PAGE_SIZE_MASK,
host_efer, host_cr4, guest_efer, guest_cr4);
_svm_npt_rsvd_bits_test(npt_get_pdpe((u64) basic_guest_main),
PT_PAGE_SIZE_MASK |
(this_cpu_has(X86_FEATURE_GBPAGES) ?
get_random_bits(29, 13) : 0), host_efer,
host_cr4, guest_efer, guest_cr4);
_svm_npt_rsvd_bits_test(npt_get_pml4e(), BIT_ULL(8),
host_efer, host_cr4, guest_efer, guest_cr4);
wrmsr(MSR_EFER, saved_efer);
write_cr4(saved_cr4);
vmcb->save.efer = sg_efer;
vmcb->save.cr4 = sg_cr4;
}
#define NPT_V1_TEST(name, prepare, guest_code, check) \
{ #name, npt_supported, prepare, default_prepare_gif_clear, guest_code, \
default_finished, check }
#define NPT_V2_TEST(name) { #name, .v2 = name }
static struct svm_test npt_tests[] = {
NPT_V1_TEST(npt_nx, npt_nx_prepare, null_test, npt_nx_check),
NPT_V1_TEST(npt_np, npt_np_prepare, npt_np_test, npt_np_check),
NPT_V1_TEST(npt_us, npt_us_prepare, npt_us_test, npt_us_check),
NPT_V1_TEST(npt_rw, npt_rw_prepare, npt_rw_test, npt_rw_check),
NPT_V1_TEST(npt_rw_pfwalk, npt_rw_pfwalk_prepare, null_test, npt_rw_pfwalk_check),
NPT_V1_TEST(npt_l1mmio, npt_l1mmio_prepare, npt_l1mmio_test, npt_l1mmio_check),
NPT_V1_TEST(npt_rw_l1mmio, npt_rw_l1mmio_prepare, npt_rw_l1mmio_test, npt_rw_l1mmio_check),
NPT_V2_TEST(svm_npt_rsvd_bits_test),
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};
int main(int ac, char **av)
{
pteval_t opt_mask = 0;
__setup_vm(&opt_mask);
return run_svm_tests(ac, av, npt_tests);
}