KVM: s390: Fix and feature for 5.19

- ultravisor communication device driver
 - fix TEID on terminating storage key ops
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+SKTgaM0CPnbq/vKEXu8gLWmHHwFAmKLWW0ACgkQEXu8gLWm
 HHyhmBAApRObtkLtQjctGs4lzGPvE019EFFdBlK5ayYrgFE0gcaX0adstnLWyzJ+
 J7L6UbxUzKKfev0BCDyPCTH+FUW5LHanpS0pBASLrl4VMcloWa7GZh5Ahbiq797x
 9QnMC72qUggg4FYj4X4WxYJhxqgqi2lmYrcz7QjCbW6X0RWilryPuzZcL326ghzz
 gH11gup0cy9HSpe6zr7efNT8UVahUr06ky1VnUBnDRR3ecuMQOUBET/McOXLYUQP
 Q0eFtdRXvrnDKbCXinORCCp6dbreibBpLAF5PWh5WxlTNluZQtfzYjBUHyMpoeEB
 akEc/gb/MY7bbR5V7aTG3Joi1soFFmZQ93P9Bn8c39wWfouOkm8gqioCd6erjczW
 5seFUNR72uWwUfNxBFvPbDFq7eS6qEVoIx14jLjGhUcTwE9xQhNYCgc5qmmNSqB/
 OMUKyKpaqkvPs+mx+/efFhVSScWs+AMimUYzYb2fdTJ7MXxnCRIF0BUkIlFjJVpc
 3y1tOi0mD+CEKDyVfTPmigagFgW79FK6rnScSorSRWXqE3xcSpJjv3Afo6II20mQ
 YJZKviciknzxZ8/uZbJl98DpHvW17oh08SBs9kLweHjLo3SPZtHWa2qzilGTBZMY
 75jPiwNMLdZdf/SRYdrIn6nlSNrvUt+16YcN8vqUwcqW+9Of0IQ=
 =93zj
 -----END PGP SIGNATURE-----

Merge tag 'kvm-s390-next-5.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into HEAD

KVM: s390: Fix and feature for 5.19

- ultravisor communication device driver
- fix TEID on terminating storage key ops
This commit is contained in:
Paolo Bonzini 2022-05-25 05:11:21 -04:00
commit 1644e27059
14 changed files with 714 additions and 6 deletions

View File

@ -3814,12 +3814,18 @@ in case of KVM_S390_MEMOP_F_CHECK_ONLY), the ioctl returns a positive
error number indicating the type of exception. This exception is also error number indicating the type of exception. This exception is also
raised directly at the corresponding VCPU if the flag raised directly at the corresponding VCPU if the flag
KVM_S390_MEMOP_F_INJECT_EXCEPTION is set. KVM_S390_MEMOP_F_INJECT_EXCEPTION is set.
On protection exceptions, unless specified otherwise, the injected
translation-exception identifier (TEID) indicates suppression.
If the KVM_S390_MEMOP_F_SKEY_PROTECTION flag is set, storage key If the KVM_S390_MEMOP_F_SKEY_PROTECTION flag is set, storage key
protection is also in effect and may cause exceptions if accesses are protection is also in effect and may cause exceptions if accesses are
prohibited given the access key designated by "key"; the valid range is 0..15. prohibited given the access key designated by "key"; the valid range is 0..15.
KVM_S390_MEMOP_F_SKEY_PROTECTION is available if KVM_CAP_S390_MEM_OP_EXTENSION KVM_S390_MEMOP_F_SKEY_PROTECTION is available if KVM_CAP_S390_MEM_OP_EXTENSION
is > 0. is > 0.
Since the accessed memory may span multiple pages and those pages might have
different storage keys, it is possible that a protection exception occurs
after memory has been modified. In this case, if the exception is injected,
the TEID does not indicate suppression.
Absolute read/write: Absolute read/write:
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^

View File

@ -10785,9 +10785,12 @@ F: Documentation/virt/kvm/s390*
F: arch/s390/include/asm/gmap.h F: arch/s390/include/asm/gmap.h
F: arch/s390/include/asm/kvm* F: arch/s390/include/asm/kvm*
F: arch/s390/include/uapi/asm/kvm* F: arch/s390/include/uapi/asm/kvm*
F: arch/s390/include/uapi/asm/uvdevice.h
F: arch/s390/kernel/uv.c F: arch/s390/kernel/uv.c
F: arch/s390/kvm/ F: arch/s390/kvm/
F: arch/s390/mm/gmap.c F: arch/s390/mm/gmap.c
F: drivers/s390/char/uvdevice.c
F: tools/testing/selftests/drivers/s390x/uvdevice/
F: tools/testing/selftests/kvm/*/s390x/ F: tools/testing/selftests/kvm/*/s390x/
F: tools/testing/selftests/kvm/s390x/ F: tools/testing/selftests/kvm/s390x/

View File

@ -2,7 +2,7 @@
/* /*
* Ultravisor Interfaces * Ultravisor Interfaces
* *
* Copyright IBM Corp. 2019 * Copyright IBM Corp. 2019, 2022
* *
* Author(s): * Author(s):
* Vasily Gorbik <gor@linux.ibm.com> * Vasily Gorbik <gor@linux.ibm.com>
@ -52,6 +52,7 @@
#define UVC_CMD_UNPIN_PAGE_SHARED 0x0342 #define UVC_CMD_UNPIN_PAGE_SHARED 0x0342
#define UVC_CMD_SET_SHARED_ACCESS 0x1000 #define UVC_CMD_SET_SHARED_ACCESS 0x1000
#define UVC_CMD_REMOVE_SHARED_ACCESS 0x1001 #define UVC_CMD_REMOVE_SHARED_ACCESS 0x1001
#define UVC_CMD_RETR_ATTEST 0x1020
/* Bits in installed uv calls */ /* Bits in installed uv calls */
enum uv_cmds_inst { enum uv_cmds_inst {
@ -76,6 +77,7 @@ enum uv_cmds_inst {
BIT_UVC_CMD_UNSHARE_ALL = 20, BIT_UVC_CMD_UNSHARE_ALL = 20,
BIT_UVC_CMD_PIN_PAGE_SHARED = 21, BIT_UVC_CMD_PIN_PAGE_SHARED = 21,
BIT_UVC_CMD_UNPIN_PAGE_SHARED = 22, BIT_UVC_CMD_UNPIN_PAGE_SHARED = 22,
BIT_UVC_CMD_RETR_ATTEST = 28,
}; };
enum uv_feat_ind { enum uv_feat_ind {
@ -219,6 +221,25 @@ struct uv_cb_share {
u64 reserved28; u64 reserved28;
} __packed __aligned(8); } __packed __aligned(8);
/* Retrieve Attestation Measurement */
struct uv_cb_attest {
struct uv_cb_header header; /* 0x0000 */
u64 reserved08[2]; /* 0x0008 */
u64 arcb_addr; /* 0x0018 */
u64 cont_token; /* 0x0020 */
u8 reserved28[6]; /* 0x0028 */
u16 user_data_len; /* 0x002e */
u8 user_data[256]; /* 0x0030 */
u32 reserved130[3]; /* 0x0130 */
u32 meas_len; /* 0x013c */
u64 meas_addr; /* 0x0140 */
u8 config_uid[16]; /* 0x0148 */
u32 reserved158; /* 0x0158 */
u32 add_data_len; /* 0x015c */
u64 add_data_addr; /* 0x0160 */
u64 reserved168[4]; /* 0x0168 */
} __packed __aligned(8);
static inline int __uv_call(unsigned long r1, unsigned long r2) static inline int __uv_call(unsigned long r1, unsigned long r2)
{ {
int cc; int cc;

View File

@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* Copyright IBM Corp. 2022
* Author(s): Steffen Eiden <seiden@linux.ibm.com>
*/
#ifndef __S390_ASM_UVDEVICE_H
#define __S390_ASM_UVDEVICE_H
#include <linux/types.h>
struct uvio_ioctl_cb {
__u32 flags;
__u16 uv_rc; /* UV header rc value */
__u16 uv_rrc; /* UV header rrc value */
__u64 argument_addr; /* Userspace address of uvio argument */
__u32 argument_len;
__u8 reserved14[0x40 - 0x14]; /* must be zero */
};
#define UVIO_ATT_USER_DATA_LEN 0x100
#define UVIO_ATT_UID_LEN 0x10
struct uvio_attest {
__u64 arcb_addr; /* 0x0000 */
__u64 meas_addr; /* 0x0008 */
__u64 add_data_addr; /* 0x0010 */
__u8 user_data[UVIO_ATT_USER_DATA_LEN]; /* 0x0018 */
__u8 config_uid[UVIO_ATT_UID_LEN]; /* 0x0118 */
__u32 arcb_len; /* 0x0128 */
__u32 meas_len; /* 0x012c */
__u32 add_data_len; /* 0x0130 */
__u16 user_data_len; /* 0x0134 */
__u16 reserved136; /* 0x0136 */
};
/*
* The following max values define an upper length for the IOCTL in/out buffers.
* However, they do not represent the maximum the Ultravisor allows which is
* often way smaller. By allowing larger buffer sizes we hopefully do not need
* to update the code with every machine update. It is therefore possible for
* userspace to request more memory than actually used by kernel/UV.
*/
#define UVIO_ATT_ARCB_MAX_LEN 0x100000
#define UVIO_ATT_MEASUREMENT_MAX_LEN 0x8000
#define UVIO_ATT_ADDITIONAL_MAX_LEN 0x8000
#define UVIO_DEVICE_NAME "uv"
#define UVIO_TYPE_UVC 'u'
#define UVIO_IOCTL_ATT _IOWR(UVIO_TYPE_UVC, 0x01, struct uvio_ioctl_cb)
#endif /* __S390_ASM_UVDEVICE_H */

View File

@ -491,8 +491,8 @@ enum prot_type {
PROT_TYPE_IEP = 4, PROT_TYPE_IEP = 4,
}; };
static int trans_exc(struct kvm_vcpu *vcpu, int code, unsigned long gva, static int trans_exc_ending(struct kvm_vcpu *vcpu, int code, unsigned long gva, u8 ar,
u8 ar, enum gacc_mode mode, enum prot_type prot) enum gacc_mode mode, enum prot_type prot, bool terminate)
{ {
struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm; struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
struct trans_exc_code_bits *tec; struct trans_exc_code_bits *tec;
@ -520,6 +520,11 @@ static int trans_exc(struct kvm_vcpu *vcpu, int code, unsigned long gva,
tec->b61 = 1; tec->b61 = 1;
break; break;
} }
if (terminate) {
tec->b56 = 0;
tec->b60 = 0;
tec->b61 = 0;
}
fallthrough; fallthrough;
case PGM_ASCE_TYPE: case PGM_ASCE_TYPE:
case PGM_PAGE_TRANSLATION: case PGM_PAGE_TRANSLATION:
@ -552,6 +557,12 @@ static int trans_exc(struct kvm_vcpu *vcpu, int code, unsigned long gva,
return code; return code;
} }
static int trans_exc(struct kvm_vcpu *vcpu, int code, unsigned long gva, u8 ar,
enum gacc_mode mode, enum prot_type prot)
{
return trans_exc_ending(vcpu, code, gva, ar, mode, prot, false);
}
static int get_vcpu_asce(struct kvm_vcpu *vcpu, union asce *asce, static int get_vcpu_asce(struct kvm_vcpu *vcpu, union asce *asce,
unsigned long ga, u8 ar, enum gacc_mode mode) unsigned long ga, u8 ar, enum gacc_mode mode)
{ {
@ -1109,8 +1120,11 @@ int access_guest_with_key(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
data += fragment_len; data += fragment_len;
ga = kvm_s390_logical_to_effective(vcpu, ga + fragment_len); ga = kvm_s390_logical_to_effective(vcpu, ga + fragment_len);
} }
if (rc > 0) if (rc > 0) {
rc = trans_exc(vcpu, rc, ga, ar, mode, prot); bool terminate = (mode == GACC_STORE) && (idx > 0);
rc = trans_exc_ending(vcpu, rc, ga, ar, mode, prot, terminate);
}
out_unlock: out_unlock:
if (need_ipte_lock) if (need_ipte_lock)
ipte_unlock(vcpu); ipte_unlock(vcpu);

View File

@ -100,6 +100,16 @@ config SCLP_OFB
This option enables the Open-for-Business interface to the s390 This option enables the Open-for-Business interface to the s390
Service Element. Service Element.
config S390_UV_UAPI
def_tristate m
prompt "Ultravisor userspace API"
help
Selecting exposes parts of the UV interface to userspace
by providing a misc character device at /dev/uv.
Using IOCTLs one can interact with the UV.
The device is only available if the Ultravisor
Facility (158) is present.
config S390_TAPE config S390_TAPE
def_tristate m def_tristate m
prompt "S/390 tape device support" prompt "S/390 tape device support"

View File

@ -48,6 +48,7 @@ obj-$(CONFIG_MONREADER) += monreader.o
obj-$(CONFIG_MONWRITER) += monwriter.o obj-$(CONFIG_MONWRITER) += monwriter.o
obj-$(CONFIG_S390_VMUR) += vmur.o obj-$(CONFIG_S390_VMUR) += vmur.o
obj-$(CONFIG_CRASH_DUMP) += sclp_sdias.o zcore.o obj-$(CONFIG_CRASH_DUMP) += sclp_sdias.o zcore.o
obj-$(CONFIG_S390_UV_UAPI) += uvdevice.o
hmcdrv-objs := hmcdrv_mod.o hmcdrv_dev.o hmcdrv_ftp.o hmcdrv_cache.o diag_ftp.o sclp_ftp.o hmcdrv-objs := hmcdrv_mod.o hmcdrv_dev.o hmcdrv_ftp.o hmcdrv_cache.o diag_ftp.o sclp_ftp.o
obj-$(CONFIG_HMC_DRV) += hmcdrv.o obj-$(CONFIG_HMC_DRV) += hmcdrv.o

View File

@ -0,0 +1,257 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright IBM Corp. 2022
* Author(s): Steffen Eiden <seiden@linux.ibm.com>
*
* This file provides a Linux misc device to give userspace access to some
* Ultravisor (UV) functions. The device only accepts IOCTLs and will only
* be present if the Ultravisor facility (158) is present.
*
* When userspace sends a valid IOCTL uvdevice will copy the input data to
* kernel space, do some basic validity checks to avoid kernel/system
* corruption. Any other check that the Ultravisor does will not be done by
* the uvdevice to keep changes minimal when adding new functionalities
* to existing UV-calls.
* After the checks uvdevice builds a corresponding
* Ultravisor Call Control Block, and sends the request to the Ultravisor.
* Then, it copies the response, including the return codes, back to userspace.
* It is the responsibility of the userspace to check for any error issued
* by UV and to interpret the UV response. The uvdevice acts as a communication
* channel for userspace to the Ultravisor.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <asm/uvdevice.h>
#include <asm/uv.h>
static int uvio_build_uvcb_attest(struct uv_cb_attest *uvcb_attest, u8 *arcb,
u8 *meas, u8 *add_data, struct uvio_attest *uvio_attest)
{
void __user *user_buf_arcb = (void __user *)uvio_attest->arcb_addr;
if (copy_from_user(arcb, user_buf_arcb, uvio_attest->arcb_len))
return -EFAULT;
uvcb_attest->header.len = sizeof(*uvcb_attest);
uvcb_attest->header.cmd = UVC_CMD_RETR_ATTEST;
uvcb_attest->arcb_addr = (u64)arcb;
uvcb_attest->cont_token = 0;
uvcb_attest->user_data_len = uvio_attest->user_data_len;
memcpy(uvcb_attest->user_data, uvio_attest->user_data, sizeof(uvcb_attest->user_data));
uvcb_attest->meas_len = uvio_attest->meas_len;
uvcb_attest->meas_addr = (u64)meas;
uvcb_attest->add_data_len = uvio_attest->add_data_len;
uvcb_attest->add_data_addr = (u64)add_data;
return 0;
}
static int uvio_copy_attest_result_to_user(struct uv_cb_attest *uvcb_attest,
struct uvio_ioctl_cb *uv_ioctl,
u8 *measurement, u8 *add_data,
struct uvio_attest *uvio_attest)
{
struct uvio_attest __user *user_uvio_attest = (void __user *)uv_ioctl->argument_addr;
void __user *user_buf_add = (void __user *)uvio_attest->add_data_addr;
void __user *user_buf_meas = (void __user *)uvio_attest->meas_addr;
void __user *user_buf_uid = &user_uvio_attest->config_uid;
if (copy_to_user(user_buf_meas, measurement, uvio_attest->meas_len))
return -EFAULT;
if (add_data && copy_to_user(user_buf_add, add_data, uvio_attest->add_data_len))
return -EFAULT;
if (copy_to_user(user_buf_uid, uvcb_attest->config_uid, sizeof(uvcb_attest->config_uid)))
return -EFAULT;
return 0;
}
static int get_uvio_attest(struct uvio_ioctl_cb *uv_ioctl, struct uvio_attest *uvio_attest)
{
u8 __user *user_arg_buf = (u8 __user *)uv_ioctl->argument_addr;
if (copy_from_user(uvio_attest, user_arg_buf, sizeof(*uvio_attest)))
return -EFAULT;
if (uvio_attest->arcb_len > UVIO_ATT_ARCB_MAX_LEN)
return -EINVAL;
if (uvio_attest->arcb_len == 0)
return -EINVAL;
if (uvio_attest->meas_len > UVIO_ATT_MEASUREMENT_MAX_LEN)
return -EINVAL;
if (uvio_attest->meas_len == 0)
return -EINVAL;
if (uvio_attest->add_data_len > UVIO_ATT_ADDITIONAL_MAX_LEN)
return -EINVAL;
if (uvio_attest->reserved136)
return -EINVAL;
return 0;
}
/**
* uvio_attestation() - Perform a Retrieve Attestation Measurement UVC.
*
* @uv_ioctl: ioctl control block
*
* uvio_attestation() does a Retrieve Attestation Measurement Ultravisor Call.
* It verifies that the given userspace addresses are valid and request sizes
* are sane. Every other check is made by the Ultravisor (UV) and won't result
* in a negative return value. It copies the input to kernelspace, builds the
* request, sends the UV-call, and copies the result to userspace.
*
* The Attestation Request has two input and two outputs.
* ARCB and User Data are inputs for the UV generated by userspace.
* Measurement and Additional Data are outputs for userspace generated by UV.
*
* The Attestation Request Control Block (ARCB) is a cryptographically verified
* and secured request to UV and User Data is some plaintext data which is
* going to be included in the Attestation Measurement calculation.
*
* Measurement is a cryptographic measurement of the callers properties,
* optional data configured by the ARCB and the user data. If specified by the
* ARCB, UV will add some Additional Data to the measurement calculation.
* This Additional Data is then returned as well.
*
* If the Retrieve Attestation Measurement UV facility is not present,
* UV will return invalid command rc. This won't be fenced in the driver
* and does not result in a negative return value.
*
* Context: might sleep
*
* Return: 0 on success or a negative error code on error.
*/
static int uvio_attestation(struct uvio_ioctl_cb *uv_ioctl)
{
struct uv_cb_attest *uvcb_attest = NULL;
struct uvio_attest *uvio_attest = NULL;
u8 *measurement = NULL;
u8 *add_data = NULL;
u8 *arcb = NULL;
int ret;
ret = -EINVAL;
if (uv_ioctl->argument_len != sizeof(*uvio_attest))
goto out;
ret = -ENOMEM;
uvio_attest = kzalloc(sizeof(*uvio_attest), GFP_KERNEL);
if (!uvio_attest)
goto out;
ret = get_uvio_attest(uv_ioctl, uvio_attest);
if (ret)
goto out;
ret = -ENOMEM;
arcb = kvzalloc(uvio_attest->arcb_len, GFP_KERNEL);
measurement = kvzalloc(uvio_attest->meas_len, GFP_KERNEL);
if (!arcb || !measurement)
goto out;
if (uvio_attest->add_data_len) {
add_data = kvzalloc(uvio_attest->add_data_len, GFP_KERNEL);
if (!add_data)
goto out;
}
uvcb_attest = kzalloc(sizeof(*uvcb_attest), GFP_KERNEL);
if (!uvcb_attest)
goto out;
ret = uvio_build_uvcb_attest(uvcb_attest, arcb, measurement, add_data, uvio_attest);
if (ret)
goto out;
uv_call_sched(0, (u64)uvcb_attest);
uv_ioctl->uv_rc = uvcb_attest->header.rc;
uv_ioctl->uv_rrc = uvcb_attest->header.rrc;
ret = uvio_copy_attest_result_to_user(uvcb_attest, uv_ioctl, measurement, add_data,
uvio_attest);
out:
kvfree(arcb);
kvfree(measurement);
kvfree(add_data);
kfree(uvio_attest);
kfree(uvcb_attest);
return ret;
}
static int uvio_copy_and_check_ioctl(struct uvio_ioctl_cb *ioctl, void __user *argp)
{
if (copy_from_user(ioctl, argp, sizeof(*ioctl)))
return -EFAULT;
if (ioctl->flags != 0)
return -EINVAL;
if (memchr_inv(ioctl->reserved14, 0, sizeof(ioctl->reserved14)))
return -EINVAL;
return 0;
}
/*
* IOCTL entry point for the Ultravisor device.
*/
static long uvio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
struct uvio_ioctl_cb uv_ioctl = { };
long ret;
switch (cmd) {
case UVIO_IOCTL_ATT:
ret = uvio_copy_and_check_ioctl(&uv_ioctl, argp);
if (ret)
return ret;
ret = uvio_attestation(&uv_ioctl);
break;
default:
ret = -ENOIOCTLCMD;
break;
}
if (ret)
return ret;
if (copy_to_user(argp, &uv_ioctl, sizeof(uv_ioctl)))
ret = -EFAULT;
return ret;
}
static const struct file_operations uvio_dev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = uvio_ioctl,
.llseek = no_llseek,
};
static struct miscdevice uvio_dev_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = UVIO_DEVICE_NAME,
.fops = &uvio_dev_fops,
};
static void __exit uvio_dev_exit(void)
{
misc_deregister(&uvio_dev_miscdev);
}
static int __init uvio_dev_init(void)
{
if (!test_facility(158))
return -ENXIO;
return misc_register(&uvio_dev_miscdev);
}
module_init(uvio_dev_init);
module_exit(uvio_dev_exit);
MODULE_AUTHOR("IBM Corporation");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Ultravisor UAPI driver");

View File

@ -10,6 +10,7 @@ TARGETS += core
TARGETS += cpufreq TARGETS += cpufreq
TARGETS += cpu-hotplug TARGETS += cpu-hotplug
TARGETS += drivers/dma-buf TARGETS += drivers/dma-buf
TARGETS += drivers/s390x/uvdevice
TARGETS += efivarfs TARGETS += efivarfs
TARGETS += exec TARGETS += exec
TARGETS += filesystems TARGETS += filesystems

View File

@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
/dma-buf/udmabuf /dma-buf/udmabuf
/s390x/uvdevice/test_uvdevice

View File

@ -0,0 +1,22 @@
include ../../../../../build/Build.include
UNAME_M := $(shell uname -m)
ifneq ($(UNAME_M),s390x)
nothing:
.PHONY: all clean run_tests install
.SILENT:
else
TEST_GEN_PROGS := test_uvdevice
top_srcdir ?= ../../../../../..
KSFT_KHDR_INSTALL := 1
khdr_dir = $(top_srcdir)/usr/include
LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include
CFLAGS += -Wall -Werror -static -I$(khdr_dir) -I$(LINUX_TOOL_ARCH_INCLUDE)
include ../../../lib.mk
endif

View File

@ -0,0 +1 @@
CONFIG_S390_UV_UAPI=y

View File

@ -0,0 +1,276 @@
// SPDX-License-Identifier: GPL-2.0
/*
* selftest for the Ultravisor UAPI device
*
* Copyright IBM Corp. 2022
* Author(s): Steffen Eiden <seiden@linux.ibm.com>
*/
#include <stdint.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <asm/uvdevice.h>
#include "../../../kselftest_harness.h"
#define UV_PATH "/dev/uv"
#define BUFFER_SIZE 0x200
FIXTURE(uvio_fixture) {
int uv_fd;
struct uvio_ioctl_cb uvio_ioctl;
uint8_t buffer[BUFFER_SIZE];
__u64 fault_page;
};
FIXTURE_VARIANT(uvio_fixture) {
unsigned long ioctl_cmd;
uint32_t arg_size;
};
FIXTURE_VARIANT_ADD(uvio_fixture, att) {
.ioctl_cmd = UVIO_IOCTL_ATT,
.arg_size = sizeof(struct uvio_attest),
};
FIXTURE_SETUP(uvio_fixture)
{
self->uv_fd = open(UV_PATH, O_ACCMODE);
self->uvio_ioctl.argument_addr = (__u64)self->buffer;
self->uvio_ioctl.argument_len = variant->arg_size;
self->fault_page =
(__u64)mmap(NULL, (size_t)getpagesize(), PROT_NONE, MAP_ANONYMOUS, -1, 0);
}
FIXTURE_TEARDOWN(uvio_fixture)
{
if (self->uv_fd)
close(self->uv_fd);
munmap((void *)self->fault_page, (size_t)getpagesize());
}
TEST_F(uvio_fixture, fault_ioctl_arg)
{
int rc, errno_cache;
rc = ioctl(self->uv_fd, variant->ioctl_cmd, NULL);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EFAULT);
rc = ioctl(self->uv_fd, variant->ioctl_cmd, self->fault_page);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EFAULT);
}
TEST_F(uvio_fixture, fault_uvio_arg)
{
int rc, errno_cache;
self->uvio_ioctl.argument_addr = 0;
rc = ioctl(self->uv_fd, variant->ioctl_cmd, &self->uvio_ioctl);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EFAULT);
self->uvio_ioctl.argument_addr = self->fault_page;
rc = ioctl(self->uv_fd, variant->ioctl_cmd, &self->uvio_ioctl);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EFAULT);
}
/*
* Test to verify that IOCTLs with invalid values in the ioctl_control block
* are rejected.
*/
TEST_F(uvio_fixture, inval_ioctl_cb)
{
int rc, errno_cache;
self->uvio_ioctl.argument_len = 0;
rc = ioctl(self->uv_fd, variant->ioctl_cmd, &self->uvio_ioctl);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EINVAL);
self->uvio_ioctl.argument_len = (uint32_t)-1;
rc = ioctl(self->uv_fd, variant->ioctl_cmd, &self->uvio_ioctl);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EINVAL);
self->uvio_ioctl.argument_len = variant->arg_size;
self->uvio_ioctl.flags = (uint32_t)-1;
rc = ioctl(self->uv_fd, variant->ioctl_cmd, &self->uvio_ioctl);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EINVAL);
self->uvio_ioctl.flags = 0;
memset(self->uvio_ioctl.reserved14, 0xff, sizeof(self->uvio_ioctl.reserved14));
rc = ioctl(self->uv_fd, variant->ioctl_cmd, &self->uvio_ioctl);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EINVAL);
memset(&self->uvio_ioctl, 0x11, sizeof(self->uvio_ioctl));
rc = ioctl(self->uv_fd, variant->ioctl_cmd, &self->uvio_ioctl);
ASSERT_EQ(rc, -1);
}
TEST_F(uvio_fixture, inval_ioctl_cmd)
{
int rc, errno_cache;
uint8_t nr = _IOC_NR(variant->ioctl_cmd);
unsigned long cmds[] = {
_IOWR('a', nr, struct uvio_ioctl_cb),
_IOWR(UVIO_TYPE_UVC, nr, int),
_IO(UVIO_TYPE_UVC, nr),
_IOR(UVIO_TYPE_UVC, nr, struct uvio_ioctl_cb),
_IOW(UVIO_TYPE_UVC, nr, struct uvio_ioctl_cb),
};
for (size_t i = 0; i < ARRAY_SIZE(cmds); i++) {
rc = ioctl(self->uv_fd, cmds[i], &self->uvio_ioctl);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, ENOTTY);
}
}
struct test_attest_buffer {
uint8_t arcb[0x180];
uint8_t meas[64];
uint8_t add[32];
};
FIXTURE(attest_fixture) {
int uv_fd;
struct uvio_ioctl_cb uvio_ioctl;
struct uvio_attest uvio_attest;
struct test_attest_buffer attest_buffer;
__u64 fault_page;
};
FIXTURE_SETUP(attest_fixture)
{
self->uv_fd = open(UV_PATH, O_ACCMODE);
self->uvio_ioctl.argument_addr = (__u64)&self->uvio_attest;
self->uvio_ioctl.argument_len = sizeof(self->uvio_attest);
self->uvio_attest.arcb_addr = (__u64)&self->attest_buffer.arcb;
self->uvio_attest.arcb_len = sizeof(self->attest_buffer.arcb);
self->uvio_attest.meas_addr = (__u64)&self->attest_buffer.meas;
self->uvio_attest.meas_len = sizeof(self->attest_buffer.meas);
self->uvio_attest.add_data_addr = (__u64)&self->attest_buffer.add;
self->uvio_attest.add_data_len = sizeof(self->attest_buffer.add);
self->fault_page =
(__u64)mmap(NULL, (size_t)getpagesize(), PROT_NONE, MAP_ANONYMOUS, -1, 0);
}
FIXTURE_TEARDOWN(attest_fixture)
{
if (self->uv_fd)
close(self->uv_fd);
munmap((void *)self->fault_page, (size_t)getpagesize());
}
static void att_inval_sizes_test(uint32_t *size, uint32_t max_size, bool test_zero,
struct __test_metadata *_metadata,
FIXTURE_DATA(attest_fixture) *self)
{
int rc, errno_cache;
uint32_t tmp = *size;
if (test_zero) {
*size = 0;
rc = ioctl(self->uv_fd, UVIO_IOCTL_ATT, &self->uvio_ioctl);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EINVAL);
}
*size = max_size + 1;
rc = ioctl(self->uv_fd, UVIO_IOCTL_ATT, &self->uvio_ioctl);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EINVAL);
*size = tmp;
}
/*
* Test to verify that attestation IOCTLs with invalid values in the UVIO
* attestation control block are rejected.
*/
TEST_F(attest_fixture, att_inval_request)
{
int rc, errno_cache;
att_inval_sizes_test(&self->uvio_attest.add_data_len, UVIO_ATT_ADDITIONAL_MAX_LEN,
false, _metadata, self);
att_inval_sizes_test(&self->uvio_attest.meas_len, UVIO_ATT_MEASUREMENT_MAX_LEN,
true, _metadata, self);
att_inval_sizes_test(&self->uvio_attest.arcb_len, UVIO_ATT_ARCB_MAX_LEN,
true, _metadata, self);
self->uvio_attest.reserved136 = (uint16_t)-1;
rc = ioctl(self->uv_fd, UVIO_IOCTL_ATT, &self->uvio_ioctl);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EINVAL);
memset(&self->uvio_attest, 0x11, sizeof(self->uvio_attest));
rc = ioctl(self->uv_fd, UVIO_IOCTL_ATT, &self->uvio_ioctl);
ASSERT_EQ(rc, -1);
}
static void att_inval_addr_test(__u64 *addr, struct __test_metadata *_metadata,
FIXTURE_DATA(attest_fixture) *self)
{
int rc, errno_cache;
__u64 tmp = *addr;
*addr = 0;
rc = ioctl(self->uv_fd, UVIO_IOCTL_ATT, &self->uvio_ioctl);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EFAULT);
*addr = self->fault_page;
rc = ioctl(self->uv_fd, UVIO_IOCTL_ATT, &self->uvio_ioctl);
errno_cache = errno;
ASSERT_EQ(rc, -1);
ASSERT_EQ(errno_cache, EFAULT);
*addr = tmp;
}
TEST_F(attest_fixture, att_inval_addr)
{
att_inval_addr_test(&self->uvio_attest.arcb_addr, _metadata, self);
att_inval_addr_test(&self->uvio_attest.add_data_addr, _metadata, self);
att_inval_addr_test(&self->uvio_attest.meas_addr, _metadata, self);
}
static void __attribute__((constructor)) __constructor_order_last(void)
{
if (!__constructor_order)
__constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
}
int main(int argc, char **argv)
{
int fd = open(UV_PATH, O_ACCMODE);
if (fd < 0)
ksft_exit_skip("No uv-device or cannot access " UV_PATH "\n"
"Enable CONFIG_S390_UV_UAPI and check the access rights on "
UV_PATH ".\n");
close(fd);
return test_harness_run(argc, argv);
}

View File

@ -10,6 +10,8 @@
#include <string.h> #include <string.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <linux/bits.h>
#include "test_util.h" #include "test_util.h"
#include "kvm_util.h" #include "kvm_util.h"
@ -194,6 +196,7 @@ static int err_memop_ioctl(struct test_vcpu vcpu, struct kvm_s390_mem_op *ksmo)
#define SIDA_OFFSET(o) ._sida_offset = 1, .sida_offset = (o) #define SIDA_OFFSET(o) ._sida_offset = 1, .sida_offset = (o)
#define AR(a) ._ar = 1, .ar = (a) #define AR(a) ._ar = 1, .ar = (a)
#define KEY(a) .f_key = 1, .key = (a) #define KEY(a) .f_key = 1, .key = (a)
#define INJECT .f_inject = 1
#define CHECK_N_DO(f, ...) ({ f(__VA_ARGS__, CHECK_ONLY); f(__VA_ARGS__); }) #define CHECK_N_DO(f, ...) ({ f(__VA_ARGS__, CHECK_ONLY); f(__VA_ARGS__); })
@ -430,9 +433,18 @@ static void test_copy_key_fetch_prot(void)
TEST_ASSERT(rv == 4, "Should result in protection exception"); \ TEST_ASSERT(rv == 4, "Should result in protection exception"); \
}) })
static void guest_error_key(void)
{
GUEST_SYNC(STAGE_INITED);
set_storage_key_range(mem1, PAGE_SIZE, 0x18);
set_storage_key_range(mem1 + PAGE_SIZE, sizeof(mem1) - PAGE_SIZE, 0x98);
GUEST_SYNC(STAGE_SKEYS_SET);
GUEST_SYNC(STAGE_IDLED);
}
static void test_errors_key(void) static void test_errors_key(void)
{ {
struct test_default t = test_default_init(guest_copy_key_fetch_prot); struct test_default t = test_default_init(guest_error_key);
HOST_SYNC(t.vcpu, STAGE_INITED); HOST_SYNC(t.vcpu, STAGE_INITED);
HOST_SYNC(t.vcpu, STAGE_SKEYS_SET); HOST_SYNC(t.vcpu, STAGE_SKEYS_SET);
@ -446,6 +458,37 @@ static void test_errors_key(void)
kvm_vm_free(t.kvm_vm); kvm_vm_free(t.kvm_vm);
} }
static void test_termination(void)
{
struct test_default t = test_default_init(guest_error_key);
uint64_t prefix;
uint64_t teid;
uint64_t teid_mask = BIT(63 - 56) | BIT(63 - 60) | BIT(63 - 61);
uint64_t psw[2];
HOST_SYNC(t.vcpu, STAGE_INITED);
HOST_SYNC(t.vcpu, STAGE_SKEYS_SET);
/* vcpu, mismatching keys after first page */
ERR_PROT_MOP(t.vcpu, LOGICAL, WRITE, mem1, t.size, GADDR_V(mem1), KEY(1), INJECT);
/*
* The memop injected a program exception and the test needs to check the
* Translation-Exception Identification (TEID). It is necessary to run
* the guest in order to be able to read the TEID from guest memory.
* Set the guest program new PSW, so the guest state is not clobbered.
*/
prefix = t.run->s.regs.prefix;
psw[0] = t.run->psw_mask;
psw[1] = t.run->psw_addr;
MOP(t.vm, ABSOLUTE, WRITE, psw, sizeof(psw), GADDR(prefix + 464));
HOST_SYNC(t.vcpu, STAGE_IDLED);
MOP(t.vm, ABSOLUTE, READ, &teid, sizeof(teid), GADDR(prefix + 168));
/* Bits 56, 60, 61 form a code, 0 being the only one allowing for termination */
ASSERT_EQ(teid & teid_mask, 0);
kvm_vm_free(t.kvm_vm);
}
static void test_errors_key_storage_prot_override(void) static void test_errors_key_storage_prot_override(void)
{ {
struct test_default t = test_default_init(guest_copy_key_fetch_prot); struct test_default t = test_default_init(guest_copy_key_fetch_prot);
@ -668,6 +711,7 @@ int main(int argc, char *argv[])
test_copy_key_fetch_prot(); test_copy_key_fetch_prot();
test_copy_key_fetch_prot_override(); test_copy_key_fetch_prot_override();
test_errors_key(); test_errors_key();
test_termination();
test_errors_key_storage_prot_override(); test_errors_key_storage_prot_override();
test_errors_key_fetch_prot_override_not_enabled(); test_errors_key_fetch_prot_override_not_enabled();
test_errors_key_fetch_prot_override_enabled(); test_errors_key_fetch_prot_override_enabled();