aboutsummaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/kvm/lib/guest_modes.c
blob: 99a575bbbc52a78af1640d2cb7122704b0744908 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2020, Red Hat, Inc.
 */
#include "guest_modes.h"

#ifdef __aarch64__
#include "processor.h"
enum vm_guest_mode vm_mode_default;
#endif

struct guest_mode guest_modes[NUM_VM_MODES];

void guest_modes_append_default(void)
{
#ifndef __aarch64__
	guest_mode_append(VM_MODE_DEFAULT, true, true);
#else
	{
		unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
		bool ps4k, ps16k, ps64k;
		int i;

		aarch64_get_supported_page_sizes(limit, &ps4k, &ps16k, &ps64k);

		vm_mode_default = NUM_VM_MODES;

		if (limit >= 52)
			guest_mode_append(VM_MODE_P52V48_64K, ps64k, ps64k);
		if (limit >= 48) {
			guest_mode_append(VM_MODE_P48V48_4K, ps4k, ps4k);
			guest_mode_append(VM_MODE_P48V48_16K, ps16k, ps16k);
			guest_mode_append(VM_MODE_P48V48_64K, ps64k, ps64k);
		}
		if (limit >= 40) {
			guest_mode_append(VM_MODE_P40V48_4K, ps4k, ps4k);
			guest_mode_append(VM_MODE_P40V48_16K, ps16k, ps16k);
			guest_mode_append(VM_MODE_P40V48_64K, ps64k, ps64k);
			if (ps4k)
				vm_mode_default = VM_MODE_P40V48_4K;
		}
		if (limit >= 36) {
			guest_mode_append(VM_MODE_P36V48_4K, ps4k, ps4k);
			guest_mode_append(VM_MODE_P36V48_16K, ps16k, ps16k);
			guest_mode_append(VM_MODE_P36V48_64K, ps64k, ps64k);
			guest_mode_append(VM_MODE_P36V47_16K, ps16k, ps16k);
		}

		/*
		 * Pick the first supported IPA size if the default
		 * isn't available.
		 */
		for (i = 0; vm_mode_default == NUM_VM_MODES && i < NUM_VM_MODES; i++) {
			if (guest_modes[i].supported && guest_modes[i].enabled)
				vm_mode_default = i;
		}

		TEST_ASSERT(vm_mode_default != NUM_VM_MODES,
			    "No supported mode!");
	}
#endif
#ifdef __s390x__
	{
		int kvm_fd, vm_fd;
		struct kvm_s390_vm_cpu_processor info;

		kvm_fd = open_kvm_dev_path_or_exit();
		vm_fd = __kvm_ioctl(kvm_fd, KVM_CREATE_VM, NULL);
		kvm_device_attr_get(vm_fd, KVM_S390_VM_CPU_MODEL,
				    KVM_S390_VM_CPU_PROCESSOR, &info);
		close(vm_fd);
		close(kvm_fd);
		/* Starting with z13 we have 47bits of physical address */
		if (info.ibc >= 0x30)
			guest_mode_append(VM_MODE_P47V64_4K, true, true);
	}
#endif
#ifdef __riscv
	{
		unsigned int sz = kvm_check_cap(KVM_CAP_VM_GPA_BITS);

		if (sz >= 52)
			guest_mode_append(VM_MODE_P52V48_4K, true, true);
		if (sz >= 48)
			guest_mode_append(VM_MODE_P48V48_4K, true, true);
	}
#endif
}

void for_each_guest_mode(void (*func)(enum vm_guest_mode, void *), void *arg)
{
	int i;

	for (i = 0; i < NUM_VM_MODES; ++i) {
		if (!guest_modes[i].enabled)
			continue;
		TEST_ASSERT(guest_modes[i].supported,
			    "Guest mode ID %d (%s) not supported.",
			    i, vm_guest_mode_string(i));
		func(i, arg);
	}
}

void guest_modes_help(void)
{
	int i;

	printf(" -m: specify the guest mode ID to test\n"
	       "     (default: test all supported modes)\n"
	       "     This option may be used multiple times.\n"
	       "     Guest mode IDs:\n");
	for (i = 0; i < NUM_VM_MODES; ++i) {
		printf("         %d:    %s%s\n", i, vm_guest_mode_string(i),
		       guest_modes[i].supported ? " (supported)" : "");
	}
}

void guest_modes_cmdline(const char *arg)
{
	static bool mode_selected;
	unsigned int mode;
	int i;

	if (!mode_selected) {
		for (i = 0; i < NUM_VM_MODES; ++i)
			guest_modes[i].enabled = false;
		mode_selected = true;
	}

	mode = strtoul(optarg, NULL, 10);
	TEST_ASSERT(mode < NUM_VM_MODES, "Guest mode ID %d too big", mode);
	guest_modes[mode].enabled = true;
}