File Index
Files and directories
Sign in
FILE

set_sregs_test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/kvm/x86/set_sregs_test.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * KVM_SET_SREGS tests
  4 *
  5 * Copyright (C) 2018, Google LLC.
  6 *
  7 * This is a regression test for the bug fixed by the following commit:
  8 * d3802286fa0f ("kvm: x86: Disallow illegal IA32_APIC_BASE MSR values")
  9 *
 10 * That bug allowed a user-mode program that called the KVM_SET_SREGS
 11 * ioctl to put a VCPU's local APIC into an invalid state.
 12 */
 13#include <fcntl.h>
 14#include <stdio.h>
 15#include <stdlib.h>
 16#include <string.h>
 17#include <sys/ioctl.h>
 18
 19#include "test_util.h"
 20
 21#include "kvm_util.h"
 22#include "processor.h"
 23
 24#define TEST_INVALID_CR_BIT(vcpu, cr, orig, bit)				\
 25do {										\
 26	struct kvm_sregs new;							\
 27	int rc;									\
 28										\
 29	/* Skip the sub-test, the feature/bit is supported. */			\
 30	if (orig.cr & bit)							\
 31		break;								\
 32										\
 33	memcpy(&new, &orig, sizeof(sregs));					\
 34	new.cr |= bit;								\
 35										\
 36	rc = _vcpu_sregs_set(vcpu, &new);					\
 37	TEST_ASSERT(rc, "KVM allowed invalid " #cr " bit (0x%lx)", bit);	\
 38										\
 39	/* Sanity check that KVM didn't change anything. */			\
 40	vcpu_sregs_get(vcpu, &new);						\
 41	TEST_ASSERT(!memcmp(&new, &orig, sizeof(new)), "KVM modified sregs");	\
 42} while (0)
 43
 44#define KVM_ALWAYS_ALLOWED_CR4 (X86_CR4_VME | X86_CR4_PVI | X86_CR4_TSD |	\
 45				X86_CR4_DE | X86_CR4_PSE | X86_CR4_PAE |	\
 46				X86_CR4_MCE | X86_CR4_PGE | X86_CR4_PCE |	\
 47				X86_CR4_OSFXSR | X86_CR4_OSXMMEXCPT)
 48
 49static uint64_t calc_supported_cr4_feature_bits(void)
 50{
 51	uint64_t cr4 = KVM_ALWAYS_ALLOWED_CR4;
 52
 53	if (kvm_cpu_has(X86_FEATURE_UMIP))
 54		cr4 |= X86_CR4_UMIP;
 55	if (kvm_cpu_has(X86_FEATURE_LA57))
 56		cr4 |= X86_CR4_LA57;
 57	if (kvm_cpu_has(X86_FEATURE_VMX))
 58		cr4 |= X86_CR4_VMXE;
 59	if (kvm_cpu_has(X86_FEATURE_SMX))
 60		cr4 |= X86_CR4_SMXE;
 61	if (kvm_cpu_has(X86_FEATURE_FSGSBASE))
 62		cr4 |= X86_CR4_FSGSBASE;
 63	if (kvm_cpu_has(X86_FEATURE_PCID))
 64		cr4 |= X86_CR4_PCIDE;
 65	if (kvm_cpu_has(X86_FEATURE_XSAVE))
 66		cr4 |= X86_CR4_OSXSAVE;
 67	if (kvm_cpu_has(X86_FEATURE_SMEP))
 68		cr4 |= X86_CR4_SMEP;
 69	if (kvm_cpu_has(X86_FEATURE_SMAP))
 70		cr4 |= X86_CR4_SMAP;
 71	if (kvm_cpu_has(X86_FEATURE_PKU))
 72		cr4 |= X86_CR4_PKE;
 73
 74	return cr4;
 75}
 76
 77static void test_cr_bits(struct kvm_vcpu *vcpu, uint64_t cr4)
 78{
 79	struct kvm_sregs sregs;
 80	int rc, i;
 81
 82	vcpu_sregs_get(vcpu, &sregs);
 83	sregs.cr0 &= ~(X86_CR0_CD | X86_CR0_NW);
 84	sregs.cr4 |= cr4;
 85	rc = _vcpu_sregs_set(vcpu, &sregs);
 86	TEST_ASSERT(!rc, "Failed to set supported CR4 bits (0x%lx)", cr4);
 87
 88	TEST_ASSERT(!!(sregs.cr4 & X86_CR4_OSXSAVE) ==
 89		    (vcpu->cpuid && vcpu_cpuid_has(vcpu, X86_FEATURE_OSXSAVE)),
 90		    "KVM didn't %s OSXSAVE in CPUID as expected",
 91		    (sregs.cr4 & X86_CR4_OSXSAVE) ? "set" : "clear");
 92
 93	TEST_ASSERT(!!(sregs.cr4 & X86_CR4_PKE) ==
 94		    (vcpu->cpuid && vcpu_cpuid_has(vcpu, X86_FEATURE_OSPKE)),
 95		    "KVM didn't %s OSPKE in CPUID as expected",
 96		    (sregs.cr4 & X86_CR4_PKE) ? "set" : "clear");
 97
 98	vcpu_sregs_get(vcpu, &sregs);
 99	TEST_ASSERT(sregs.cr4 == cr4, "sregs.CR4 (0x%llx) != CR4 (0x%lx)",
100		    sregs.cr4, cr4);
101
102	TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_UMIP);
103	TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_LA57);
104	TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_VMXE);
105	TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_SMXE);
106	TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_FSGSBASE);
107	TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_PCIDE);
108	TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_OSXSAVE);
109	TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_SMEP);
110	TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_SMAP);
111	TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_PKE);
112
113	for (i = 32; i < 64; i++)
114		TEST_INVALID_CR_BIT(vcpu, cr0, sregs, BIT(i));
115
116	/* NW without CD is illegal, as is PG without PE. */
117	TEST_INVALID_CR_BIT(vcpu, cr0, sregs, X86_CR0_NW);
118	TEST_INVALID_CR_BIT(vcpu, cr0, sregs, X86_CR0_PG);
119}
120
121int main(int argc, char *argv[])
122{
123	struct kvm_sregs sregs;
124	struct kvm_vcpu *vcpu;
125	struct kvm_vm *vm;
126	int rc;
127
128	/*
129	 * Create a dummy VM, specifically to avoid doing KVM_SET_CPUID2, and
130	 * use it to verify KVM enforces guest CPUID even if *userspace* never
131	 * sets CPUID.
132	 */
133	vm = vm_create_barebones();
134	vcpu = __vm_vcpu_add(vm, 0);
135	test_cr_bits(vcpu, KVM_ALWAYS_ALLOWED_CR4);
136	kvm_vm_free(vm);
137
138	/* Create a "real" VM with a fully populated guest CPUID and verify
139	 * APIC_BASE and all supported CR4 can be set.
140	 */
141	vm = vm_create_with_one_vcpu(&vcpu, NULL);
142
143	vcpu_sregs_get(vcpu, &sregs);
144	sregs.apic_base = 1 << 10;
145	rc = _vcpu_sregs_set(vcpu, &sregs);
146	TEST_ASSERT(rc, "Set IA32_APIC_BASE to %llx (invalid)",
147		    sregs.apic_base);
148	sregs.apic_base = 1 << 11;
149	rc = _vcpu_sregs_set(vcpu, &sregs);
150	TEST_ASSERT(!rc, "Couldn't set IA32_APIC_BASE to %llx (valid)",
151		    sregs.apic_base);
152
153	test_cr_bits(vcpu, calc_supported_cr4_feature_bits());
154
155	kvm_vm_free(vm);
156
157	return 0;
158}
~/ Read-only file preview.