~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/kvm/aarch64/hypercalls.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 
  3 /* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
  4  *
  5  * The test validates the basic hypercall functionalities that are exposed
  6  * via the psuedo-firmware bitmap register. This includes the registers'
  7  * read/write behavior before and after the VM has started, and if the
  8  * hypercalls are properly masked or unmasked to the guest when disabled or
  9  * enabled from the KVM userspace, respectively.
 10  */
 11 #include <errno.h>
 12 #include <linux/arm-smccc.h>
 13 #include <asm/kvm.h>
 14 #include <kvm_util.h>
 15 
 16 #include "processor.h"
 17 
 18 #define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK(max_feat_bit, 0))
 19 
 20 /* Last valid bits of the bitmapped firmware registers */
 21 #define KVM_REG_ARM_STD_BMAP_BIT_MAX            0
 22 #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX        0
 23 #define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX     1
 24 
 25 struct kvm_fw_reg_info {
 26         uint64_t reg;           /* Register definition */
 27         uint64_t max_feat_bit;  /* Bit that represents the upper limit of the feature-map */
 28 };
 29 
 30 #define FW_REG_INFO(r)                  \
 31         {                                       \
 32                 .reg = r,                       \
 33                 .max_feat_bit = r##_BIT_MAX,    \
 34         }
 35 
 36 static const struct kvm_fw_reg_info fw_reg_info[] = {
 37         FW_REG_INFO(KVM_REG_ARM_STD_BMAP),
 38         FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP),
 39         FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP),
 40 };
 41 
 42 enum test_stage {
 43         TEST_STAGE_REG_IFACE,
 44         TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
 45         TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
 46         TEST_STAGE_HVC_IFACE_FALSE_INFO,
 47         TEST_STAGE_END,
 48 };
 49 
 50 static int stage = TEST_STAGE_REG_IFACE;
 51 
 52 struct test_hvc_info {
 53         uint32_t func_id;
 54         uint64_t arg1;
 55 };
 56 
 57 #define TEST_HVC_INFO(f, a1)    \
 58         {                       \
 59                 .func_id = f,   \
 60                 .arg1 = a1,     \
 61         }
 62 
 63 static const struct test_hvc_info hvc_info[] = {
 64         /* KVM_REG_ARM_STD_BMAP */
 65         TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
 66         TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
 67         TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
 68         TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
 69         TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
 70 
 71         /* KVM_REG_ARM_STD_HYP_BMAP */
 72         TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
 73         TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
 74         TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
 75 
 76         /* KVM_REG_ARM_VENDOR_HYP_BMAP */
 77         TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
 78                         ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
 79         TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, 0),
 80         TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
 81 };
 82 
 83 /* Feed false hypercall info to test the KVM behavior */
 84 static const struct test_hvc_info false_hvc_info[] = {
 85         /* Feature support check against a different family of hypercalls */
 86         TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
 87         TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
 88         TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
 89 };
 90 
 91 static void guest_test_hvc(const struct test_hvc_info *hc_info)
 92 {
 93         unsigned int i;
 94         struct arm_smccc_res res;
 95         unsigned int hvc_info_arr_sz;
 96 
 97         hvc_info_arr_sz =
 98         hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
 99 
100         for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
101                 memset(&res, 0, sizeof(res));
102                 smccc_hvc(hc_info->func_id, hc_info->arg1, 0, 0, 0, 0, 0, 0, &res);
103 
104                 switch (stage) {
105                 case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
106                 case TEST_STAGE_HVC_IFACE_FALSE_INFO:
107                         __GUEST_ASSERT(res.a0 == SMCCC_RET_NOT_SUPPORTED,
108                                        "a0 = 0x%lx, func_id = 0x%x, arg1 = 0x%lx, stage = %u",
109                                         res.a0, hc_info->func_id, hc_info->arg1, stage);
110                         break;
111                 case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
112                         __GUEST_ASSERT(res.a0 != SMCCC_RET_NOT_SUPPORTED,
113                                        "a0 = 0x%lx, func_id = 0x%x, arg1 = 0x%lx, stage = %u",
114                                         res.a0, hc_info->func_id, hc_info->arg1, stage);
115                         break;
116                 default:
117                         GUEST_FAIL("Unexpected stage = %u", stage);
118                 }
119         }
120 }
121 
122 static void guest_code(void)
123 {
124         while (stage != TEST_STAGE_END) {
125                 switch (stage) {
126                 case TEST_STAGE_REG_IFACE:
127                         break;
128                 case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
129                 case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
130                         guest_test_hvc(hvc_info);
131                         break;
132                 case TEST_STAGE_HVC_IFACE_FALSE_INFO:
133                         guest_test_hvc(false_hvc_info);
134                         break;
135                 default:
136                         GUEST_FAIL("Unexpected stage = %u", stage);
137                 }
138 
139                 GUEST_SYNC(stage);
140         }
141 
142         GUEST_DONE();
143 }
144 
145 struct st_time {
146         uint32_t rev;
147         uint32_t attr;
148         uint64_t st_time;
149 };
150 
151 #define STEAL_TIME_SIZE         ((sizeof(struct st_time) + 63) & ~63)
152 #define ST_GPA_BASE             (1 << 30)
153 
154 static void steal_time_init(struct kvm_vcpu *vcpu)
155 {
156         uint64_t st_ipa = (ulong)ST_GPA_BASE;
157         unsigned int gpages;
158 
159         gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
160         vm_userspace_mem_region_add(vcpu->vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
161 
162         vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PVTIME_CTRL,
163                              KVM_ARM_VCPU_PVTIME_IPA, &st_ipa);
164 }
165 
166 static void test_fw_regs_before_vm_start(struct kvm_vcpu *vcpu)
167 {
168         uint64_t val;
169         unsigned int i;
170         int ret;
171 
172         for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
173                 const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
174 
175                 /* First 'read' should be an upper limit of the features supported */
176                 vcpu_get_reg(vcpu, reg_info->reg, &val);
177                 TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
178                         "Expected all the features to be set for reg: 0x%lx; expected: 0x%lx; read: 0x%lx",
179                         reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit), val);
180 
181                 /* Test a 'write' by disabling all the features of the register map */
182                 ret = __vcpu_set_reg(vcpu, reg_info->reg, 0);
183                 TEST_ASSERT(ret == 0,
184                         "Failed to clear all the features of reg: 0x%lx; ret: %d",
185                         reg_info->reg, errno);
186 
187                 vcpu_get_reg(vcpu, reg_info->reg, &val);
188                 TEST_ASSERT(val == 0,
189                         "Expected all the features to be cleared for reg: 0x%lx", reg_info->reg);
190 
191                 /*
192                  * Test enabling a feature that's not supported.
193                  * Avoid this check if all the bits are occupied.
194                  */
195                 if (reg_info->max_feat_bit < 63) {
196                         ret = __vcpu_set_reg(vcpu, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
197                         TEST_ASSERT(ret != 0 && errno == EINVAL,
198                         "Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx",
199                         errno, reg_info->reg);
200                 }
201         }
202 }
203 
204 static void test_fw_regs_after_vm_start(struct kvm_vcpu *vcpu)
205 {
206         uint64_t val;
207         unsigned int i;
208         int ret;
209 
210         for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
211                 const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
212 
213                 /*
214                  * Before starting the VM, the test clears all the bits.
215                  * Check if that's still the case.
216                  */
217                 vcpu_get_reg(vcpu, reg_info->reg, &val);
218                 TEST_ASSERT(val == 0,
219                         "Expected all the features to be cleared for reg: 0x%lx",
220                         reg_info->reg);
221 
222                 /*
223                  * Since the VM has run at least once, KVM shouldn't allow modification of
224                  * the registers and should return EBUSY. Set the registers and check for
225                  * the expected errno.
226                  */
227                 ret = __vcpu_set_reg(vcpu, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
228                 TEST_ASSERT(ret != 0 && errno == EBUSY,
229                 "Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx",
230                 errno, reg_info->reg);
231         }
232 }
233 
234 static struct kvm_vm *test_vm_create(struct kvm_vcpu **vcpu)
235 {
236         struct kvm_vm *vm;
237 
238         vm = vm_create_with_one_vcpu(vcpu, guest_code);
239 
240         steal_time_init(*vcpu);
241 
242         return vm;
243 }
244 
245 static void test_guest_stage(struct kvm_vm **vm, struct kvm_vcpu **vcpu)
246 {
247         int prev_stage = stage;
248 
249         pr_debug("Stage: %d\n", prev_stage);
250 
251         /* Sync the stage early, the VM might be freed below. */
252         stage++;
253         sync_global_to_guest(*vm, stage);
254 
255         switch (prev_stage) {
256         case TEST_STAGE_REG_IFACE:
257                 test_fw_regs_after_vm_start(*vcpu);
258                 break;
259         case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
260                 /* Start a new VM so that all the features are now enabled by default */
261                 kvm_vm_free(*vm);
262                 *vm = test_vm_create(vcpu);
263                 break;
264         case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
265         case TEST_STAGE_HVC_IFACE_FALSE_INFO:
266                 break;
267         default:
268                 TEST_FAIL("Unknown test stage: %d", prev_stage);
269         }
270 }
271 
272 static void test_run(void)
273 {
274         struct kvm_vcpu *vcpu;
275         struct kvm_vm *vm;
276         struct ucall uc;
277         bool guest_done = false;
278 
279         vm = test_vm_create(&vcpu);
280 
281         test_fw_regs_before_vm_start(vcpu);
282 
283         while (!guest_done) {
284                 vcpu_run(vcpu);
285 
286                 switch (get_ucall(vcpu, &uc)) {
287                 case UCALL_SYNC:
288                         test_guest_stage(&vm, &vcpu);
289                         break;
290                 case UCALL_DONE:
291                         guest_done = true;
292                         break;
293                 case UCALL_ABORT:
294                         REPORT_GUEST_ASSERT(uc);
295                         break;
296                 default:
297                         TEST_FAIL("Unexpected guest exit");
298                 }
299         }
300 
301         kvm_vm_free(vm);
302 }
303 
304 int main(void)
305 {
306         test_run();
307         return 0;
308 }
309 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php