Commit 8acc3518 authored by David Woodhouse's avatar David Woodhouse Committed by Paolo Bonzini
Browse files

KVM: x86/xen: Add runstate tests for 32-bit mode and crossing page boundary



Torture test the cases where the runstate crosses a page boundary, and
and especially the case where it's configured in 32-bit mode and doesn't,
but then switching to 64-bit mode makes it go onto the second page.

To simplify this, make the KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST ioctl
also update the guest runstate area. It already did so if the actual
runstate changed, as a side-effect of kvm_xen_update_runstate(). So
doing it in the plain adjustment case is making it more consistent, as
well as giving us a nice way to trigger the update without actually
running the vCPU again and changing the values.

Signed-off-by: default avatarDavid Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: default avatarPaul Durrant <paul@xen.org>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent d8ba8ba4
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -884,6 +884,8 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)

		if (data->u.runstate.state <= RUNSTATE_offline)
			kvm_xen_update_runstate(vcpu, data->u.runstate.state);
		else if (vcpu->arch.xen.runstate_cache.active)
			kvm_xen_update_runstate_guest(vcpu, false);
		r = 0;
		break;

+95 −20
Original line number Diff line number Diff line
@@ -90,9 +90,15 @@ struct pvclock_wall_clock {
struct vcpu_runstate_info {
	uint32_t state;
	uint64_t state_entry_time;
    uint64_t time[4];
	uint64_t time[5]; /* Extra field for overrun check */
};

struct compat_vcpu_runstate_info {
	uint32_t state;
	uint64_t state_entry_time;
	uint64_t time[5];
} __attribute__((__packed__));;

struct arch_vcpu_info {
	unsigned long cr2;
	unsigned long pad; /* sizeof(vcpu_info_t) == 64 */
@@ -1013,6 +1019,71 @@ int main(int argc, char *argv[])
				       runstate_names[i], rs->time[i]);
			}
		}

		/*
		 * Exercise runstate info at all points across the page boundary, in
		 * 32-bit and 64-bit mode. In particular, test the case where it is
		 * configured in 32-bit mode and then switched to 64-bit mode while
		 * active, which takes it onto the second page.
		 */
		unsigned long runstate_addr;
		struct compat_vcpu_runstate_info *crs;
		for (runstate_addr = SHINFO_REGION_GPA + PAGE_SIZE + PAGE_SIZE - sizeof(*rs) - 4;
		     runstate_addr < SHINFO_REGION_GPA + PAGE_SIZE + PAGE_SIZE + 4; runstate_addr++) {

			rs = addr_gpa2hva(vm, runstate_addr);
			crs = (void *)rs;

			memset(rs, 0xa5, sizeof(*rs));

			/* Set to compatibility mode */
			lm.u.long_mode = 0;
			vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &lm);

			/* Set runstate to new address (kernel will write it) */
			struct kvm_xen_vcpu_attr st = {
				.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR,
				.u.gpa = runstate_addr,
			};
			vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &st);

			if (verbose)
				printf("Compatibility runstate at %08lx\n", runstate_addr);

			TEST_ASSERT(crs->state == rst.u.runstate.state, "Runstate mismatch");
			TEST_ASSERT(crs->state_entry_time == rst.u.runstate.state_entry_time,
				    "State entry time mismatch");
			TEST_ASSERT(crs->time[RUNSTATE_running] == rst.u.runstate.time_running,
				    "Running time mismatch");
			TEST_ASSERT(crs->time[RUNSTATE_runnable] == rst.u.runstate.time_runnable,
				    "Runnable time mismatch");
			TEST_ASSERT(crs->time[RUNSTATE_blocked] == rst.u.runstate.time_blocked,
				    "Blocked time mismatch");
			TEST_ASSERT(crs->time[RUNSTATE_offline] == rst.u.runstate.time_offline,
				    "Offline time mismatch");
			TEST_ASSERT(crs->time[RUNSTATE_offline + 1] == 0xa5a5a5a5a5a5a5a5ULL,
				    "Structure overrun");
			TEST_ASSERT(crs->state_entry_time == crs->time[0] +
				    crs->time[1] + crs->time[2] + crs->time[3],
				    "runstate times don't add up");


			/* Now switch to 64-bit mode */
			lm.u.long_mode = 1;
			vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &lm);

			memset(rs, 0xa5, sizeof(*rs));

			/* Don't change the address, just trigger a write */
			struct kvm_xen_vcpu_attr adj = {
				.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST,
				.u.runstate.state = (uint64_t)-1
			};
			vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &adj);

			if (verbose)
				printf("64-bit runstate at %08lx\n", runstate_addr);

			TEST_ASSERT(rs->state == rst.u.runstate.state, "Runstate mismatch");
			TEST_ASSERT(rs->state_entry_time == rst.u.runstate.state_entry_time,
				    "State entry time mismatch");
@@ -1024,11 +1095,15 @@ int main(int argc, char *argv[])
				    "Blocked time mismatch");
			TEST_ASSERT(rs->time[RUNSTATE_offline] == rst.u.runstate.time_offline,
				    "Offline time mismatch");
			TEST_ASSERT(rs->time[RUNSTATE_offline + 1] == 0xa5a5a5a5a5a5a5a5ULL,
				    "Structure overrun");

			TEST_ASSERT(rs->state_entry_time == rs->time[0] +
				    rs->time[1] + rs->time[2] + rs->time[3],
				    "runstate times don't add up");
		}
	}

	kvm_vm_free(vm);
	return 0;
}