333 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Test code for the s390x kvm ucontrol interface4 *5 * Copyright IBM Corp. 20246 *7 * Authors:8 * Christoph Schlameuss <schlameuss@linux.ibm.com>9 */10#include "debug_print.h"11#include "kselftest_harness.h"12#include "kvm_util.h"13#include "processor.h"14#include "sie.h"15 16#include <linux/capability.h>17#include <linux/sizes.h>18 19#define VM_MEM_SIZE (4 * SZ_1M)20 21/* so directly declare capget to check caps without libcap */22int capget(cap_user_header_t header, cap_user_data_t data);23 24/**25 * In order to create user controlled virtual machines on S390,26 * check KVM_CAP_S390_UCONTROL and use the flag KVM_VM_S390_UCONTROL27 * as privileged user (SYS_ADMIN).28 */29void require_ucontrol_admin(void)30{31 struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3];32 struct __user_cap_header_struct hdr = {33 .version = _LINUX_CAPABILITY_VERSION_3,34 };35 int rc;36 37 rc = capget(&hdr, data);38 TEST_ASSERT_EQ(0, rc);39 TEST_REQUIRE((data->effective & CAP_TO_MASK(CAP_SYS_ADMIN)) > 0);40 41 TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_UCONTROL));42}43 44/* Test program setting some registers and looping */45extern char test_gprs_asm[];46asm("test_gprs_asm:\n"47 "xgr %r0, %r0\n"48 "lgfi %r1,1\n"49 "lgfi %r2,2\n"50 "lgfi %r3,3\n"51 "lgfi %r4,4\n"52 "lgfi %r5,5\n"53 "lgfi %r6,6\n"54 "lgfi %r7,7\n"55 "0:\n"56 " diag 0,0,0x44\n"57 " ahi %r0,1\n"58 " j 0b\n"59);60 61FIXTURE(uc_kvm)62{63 struct kvm_s390_sie_block *sie_block;64 struct kvm_run *run;65 uintptr_t base_gpa;66 uintptr_t code_gpa;67 uintptr_t base_hva;68 uintptr_t code_hva;69 int kvm_run_size;70 void *vm_mem;71 int vcpu_fd;72 int kvm_fd;73 int vm_fd;74};75 76/**77 * create VM with single vcpu, map kvm_run and SIE control block for easy access78 */79FIXTURE_SETUP(uc_kvm)80{81 struct kvm_s390_vm_cpu_processor info;82 int rc;83 84 require_ucontrol_admin();85 86 self->kvm_fd = open_kvm_dev_path_or_exit();87 self->vm_fd = ioctl(self->kvm_fd, KVM_CREATE_VM, KVM_VM_S390_UCONTROL);88 ASSERT_GE(self->vm_fd, 0);89 90 kvm_device_attr_get(self->vm_fd, KVM_S390_VM_CPU_MODEL,91 KVM_S390_VM_CPU_PROCESSOR, &info);92 TH_LOG("create VM 0x%llx", info.cpuid);93 94 self->vcpu_fd = ioctl(self->vm_fd, KVM_CREATE_VCPU, 0);95 ASSERT_GE(self->vcpu_fd, 0);96 97 self->kvm_run_size = ioctl(self->kvm_fd, KVM_GET_VCPU_MMAP_SIZE, NULL);98 ASSERT_GE(self->kvm_run_size, sizeof(struct kvm_run))99 TH_LOG(KVM_IOCTL_ERROR(KVM_GET_VCPU_MMAP_SIZE, self->kvm_run_size));100 self->run = (struct kvm_run *)mmap(NULL, self->kvm_run_size,101 PROT_READ | PROT_WRITE, MAP_SHARED, self->vcpu_fd, 0);102 ASSERT_NE(self->run, MAP_FAILED);103 /**104 * For virtual cpus that have been created with S390 user controlled105 * virtual machines, the resulting vcpu fd can be memory mapped at page106 * offset KVM_S390_SIE_PAGE_OFFSET in order to obtain a memory map of107 * the virtual cpu's hardware control block.108 */109 self->sie_block = (struct kvm_s390_sie_block *)mmap(NULL, PAGE_SIZE,110 PROT_READ | PROT_WRITE, MAP_SHARED,111 self->vcpu_fd, KVM_S390_SIE_PAGE_OFFSET << PAGE_SHIFT);112 ASSERT_NE(self->sie_block, MAP_FAILED);113 114 TH_LOG("VM created %p %p", self->run, self->sie_block);115 116 self->base_gpa = 0;117 self->code_gpa = self->base_gpa + (3 * SZ_1M);118 119 self->vm_mem = aligned_alloc(SZ_1M, VM_MEM_SIZE);120 ASSERT_NE(NULL, self->vm_mem) TH_LOG("malloc failed %u", errno);121 self->base_hva = (uintptr_t)self->vm_mem;122 self->code_hva = self->base_hva - self->base_gpa + self->code_gpa;123 struct kvm_s390_ucas_mapping map = {124 .user_addr = self->base_hva,125 .vcpu_addr = self->base_gpa,126 .length = VM_MEM_SIZE,127 };128 TH_LOG("ucas map %p %p 0x%llx",129 (void *)map.user_addr, (void *)map.vcpu_addr, map.length);130 rc = ioctl(self->vcpu_fd, KVM_S390_UCAS_MAP, &map);131 ASSERT_EQ(0, rc) TH_LOG("ucas map result %d not expected, %s",132 rc, strerror(errno));133 134 TH_LOG("page in %p", (void *)self->base_gpa);135 rc = ioctl(self->vcpu_fd, KVM_S390_VCPU_FAULT, self->base_gpa);136 ASSERT_EQ(0, rc) TH_LOG("vcpu fault (%p) result %d not expected, %s",137 (void *)self->base_hva, rc, strerror(errno));138 139 self->sie_block->cpuflags &= ~CPUSTAT_STOPPED;140}141 142FIXTURE_TEARDOWN(uc_kvm)143{144 munmap(self->sie_block, PAGE_SIZE);145 munmap(self->run, self->kvm_run_size);146 close(self->vcpu_fd);147 close(self->vm_fd);148 close(self->kvm_fd);149 free(self->vm_mem);150}151 152TEST_F(uc_kvm, uc_sie_assertions)153{154 /* assert interception of Code 08 (Program Interruption) is set */155 EXPECT_EQ(0, self->sie_block->ecb & ECB_SPECI);156}157 158TEST_F(uc_kvm, uc_attr_mem_limit)159{160 u64 limit;161 struct kvm_device_attr attr = {162 .group = KVM_S390_VM_MEM_CTRL,163 .attr = KVM_S390_VM_MEM_LIMIT_SIZE,164 .addr = (unsigned long)&limit,165 };166 int rc;167 168 rc = ioctl(self->vm_fd, KVM_GET_DEVICE_ATTR, &attr);169 EXPECT_EQ(0, rc);170 EXPECT_EQ(~0UL, limit);171 172 /* assert set not supported */173 rc = ioctl(self->vm_fd, KVM_SET_DEVICE_ATTR, &attr);174 EXPECT_EQ(-1, rc);175 EXPECT_EQ(EINVAL, errno);176}177 178TEST_F(uc_kvm, uc_no_dirty_log)179{180 struct kvm_dirty_log dlog;181 int rc;182 183 rc = ioctl(self->vm_fd, KVM_GET_DIRTY_LOG, &dlog);184 EXPECT_EQ(-1, rc);185 EXPECT_EQ(EINVAL, errno);186}187 188/**189 * Assert HPAGE CAP cannot be enabled on UCONTROL VM190 */191TEST(uc_cap_hpage)192{193 int rc, kvm_fd, vm_fd, vcpu_fd;194 struct kvm_enable_cap cap = {195 .cap = KVM_CAP_S390_HPAGE_1M,196 };197 198 require_ucontrol_admin();199 200 kvm_fd = open_kvm_dev_path_or_exit();201 vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, KVM_VM_S390_UCONTROL);202 ASSERT_GE(vm_fd, 0);203 204 /* assert hpages are not supported on ucontrol vm */205 rc = ioctl(vm_fd, KVM_CHECK_EXTENSION, KVM_CAP_S390_HPAGE_1M);206 EXPECT_EQ(0, rc);207 208 /* Test that KVM_CAP_S390_HPAGE_1M can't be enabled for a ucontrol vm */209 rc = ioctl(vm_fd, KVM_ENABLE_CAP, cap);210 EXPECT_EQ(-1, rc);211 EXPECT_EQ(EINVAL, errno);212 213 /* assert HPAGE CAP is rejected after vCPU creation */214 vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0);215 ASSERT_GE(vcpu_fd, 0);216 rc = ioctl(vm_fd, KVM_ENABLE_CAP, cap);217 EXPECT_EQ(-1, rc);218 EXPECT_EQ(EBUSY, errno);219 220 close(vcpu_fd);221 close(vm_fd);222 close(kvm_fd);223}224 225/* verify SIEIC exit226 * * fail on codes not expected in the test cases227 */228static bool uc_handle_sieic(FIXTURE_DATA(uc_kvm) * self)229{230 struct kvm_s390_sie_block *sie_block = self->sie_block;231 struct kvm_run *run = self->run;232 233 /* check SIE interception code */234 pr_info("sieic: 0x%.2x 0x%.4x 0x%.4x\n",235 run->s390_sieic.icptcode,236 run->s390_sieic.ipa,237 run->s390_sieic.ipb);238 switch (run->s390_sieic.icptcode) {239 case ICPT_INST:240 /* end execution in caller on intercepted instruction */241 pr_info("sie instruction interception\n");242 return false;243 case ICPT_OPEREXC:244 /* operation exception */245 TEST_FAIL("sie exception on %.4x%.8x", sie_block->ipa, sie_block->ipb);246 default:247 TEST_FAIL("UNEXPECTED SIEIC CODE %d", run->s390_sieic.icptcode);248 }249 return true;250}251 252/* verify VM state on exit */253static bool uc_handle_exit(FIXTURE_DATA(uc_kvm) * self)254{255 struct kvm_run *run = self->run;256 257 switch (run->exit_reason) {258 case KVM_EXIT_S390_SIEIC:259 return uc_handle_sieic(self);260 default:261 pr_info("exit_reason %2d not handled\n", run->exit_reason);262 }263 return true;264}265 266/* run the VM until interrupted */267static int uc_run_once(FIXTURE_DATA(uc_kvm) * self)268{269 int rc;270 271 rc = ioctl(self->vcpu_fd, KVM_RUN, NULL);272 print_run(self->run, self->sie_block);273 print_regs(self->run);274 pr_debug("run %d / %d %s\n", rc, errno, strerror(errno));275 return rc;276}277 278static void uc_assert_diag44(FIXTURE_DATA(uc_kvm) * self)279{280 struct kvm_s390_sie_block *sie_block = self->sie_block;281 282 /* assert vm was interrupted by diag 0x0044 */283 TEST_ASSERT_EQ(KVM_EXIT_S390_SIEIC, self->run->exit_reason);284 TEST_ASSERT_EQ(ICPT_INST, sie_block->icptcode);285 TEST_ASSERT_EQ(0x8300, sie_block->ipa);286 TEST_ASSERT_EQ(0x440000, sie_block->ipb);287}288 289TEST_F(uc_kvm, uc_gprs)290{291 struct kvm_sync_regs *sync_regs = &self->run->s.regs;292 struct kvm_run *run = self->run;293 struct kvm_regs regs = {};294 295 /* Set registers to values that are different from the ones that we expect below */296 for (int i = 0; i < 8; i++)297 sync_regs->gprs[i] = 8;298 run->kvm_dirty_regs |= KVM_SYNC_GPRS;299 300 /* copy test_gprs_asm to code_hva / code_gpa */301 TH_LOG("copy code %p to vm mapped memory %p / %p",302 &test_gprs_asm, (void *)self->code_hva, (void *)self->code_gpa);303 memcpy((void *)self->code_hva, &test_gprs_asm, PAGE_SIZE);304 305 /* DAT disabled + 64 bit mode */306 run->psw_mask = 0x0000000180000000ULL;307 run->psw_addr = self->code_gpa;308 309 /* run and expect interception of diag 44 */310 ASSERT_EQ(0, uc_run_once(self));311 ASSERT_EQ(false, uc_handle_exit(self));312 uc_assert_diag44(self);313 314 /* Retrieve and check guest register values */315 ASSERT_EQ(0, ioctl(self->vcpu_fd, KVM_GET_REGS, ®s));316 for (int i = 0; i < 8; i++) {317 ASSERT_EQ(i, regs.gprs[i]);318 ASSERT_EQ(i, sync_regs->gprs[i]);319 }320 321 /* run and expect interception of diag 44 again */322 ASSERT_EQ(0, uc_run_once(self));323 ASSERT_EQ(false, uc_handle_exit(self));324 uc_assert_diag44(self);325 326 /* check continued increment of register 0 value */327 ASSERT_EQ(0, ioctl(self->vcpu_fd, KVM_GET_REGS, ®s));328 ASSERT_EQ(1, regs.gprs[0]);329 ASSERT_EQ(1, sync_regs->gprs[0]);330}331 332TEST_HARNESS_MAIN333