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
raised directly at the corresponding VCPU if the flag
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
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.
KVM_S390_MEMOP_F_SKEY_PROTECTION is available if KVM_CAP_S390_MEM_OP_EXTENSION
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:
^^^^^^^^^^^^^^^^^^^^

View File

@ -10785,9 +10785,12 @@ F: Documentation/virt/kvm/s390*
F: arch/s390/include/asm/gmap.h
F: arch/s390/include/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/kvm/
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/

View File

@ -2,7 +2,7 @@
/*
* Ultravisor Interfaces
*
* Copyright IBM Corp. 2019
* Copyright IBM Corp. 2019, 2022
*
* Author(s):
* Vasily Gorbik <gor@linux.ibm.com>
@ -52,6 +52,7 @@
#define UVC_CMD_UNPIN_PAGE_SHARED 0x0342
#define UVC_CMD_SET_SHARED_ACCESS 0x1000
#define UVC_CMD_REMOVE_SHARED_ACCESS 0x1001
#define UVC_CMD_RETR_ATTEST 0x1020
/* Bits in installed uv calls */
enum uv_cmds_inst {
@ -76,6 +77,7 @@ enum uv_cmds_inst {
BIT_UVC_CMD_UNSHARE_ALL = 20,
BIT_UVC_CMD_PIN_PAGE_SHARED = 21,
BIT_UVC_CMD_UNPIN_PAGE_SHARED = 22,
BIT_UVC_CMD_RETR_ATTEST = 28,
};
enum uv_feat_ind {
@ -219,6 +221,25 @@ struct uv_cb_share {
u64 reserved28;
} __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)
{
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,
};
static int trans_exc(struct kvm_vcpu *vcpu, int code, unsigned long gva,
u8 ar, enum gacc_mode mode, enum prot_type prot)
static int trans_exc_ending(struct kvm_vcpu *vcpu, int code, unsigned long gva, u8 ar,
enum gacc_mode mode, enum prot_type prot, bool terminate)
{
struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
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;
break;
}
if (terminate) {
tec->b56 = 0;
tec->b60 = 0;
tec->b61 = 0;
}
fallthrough;
case PGM_ASCE_TYPE:
case PGM_PAGE_TRANSLATION:
@ -552,6 +557,12 @@ static int trans_exc(struct kvm_vcpu *vcpu, int code, unsigned long gva,
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,
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;
ga = kvm_s390_logical_to_effective(vcpu, ga + fragment_len);
}
if (rc > 0)
rc = trans_exc(vcpu, rc, ga, ar, mode, prot);
if (rc > 0) {
bool terminate = (mode == GACC_STORE) && (idx > 0);
rc = trans_exc_ending(vcpu, rc, ga, ar, mode, prot, terminate);
}
out_unlock:
if (need_ipte_lock)
ipte_unlock(vcpu);

View File

@ -100,6 +100,16 @@ config SCLP_OFB
This option enables the Open-for-Business interface to the s390
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
def_tristate m
prompt "S/390 tape device support"

View File

@ -48,6 +48,7 @@ obj-$(CONFIG_MONREADER) += monreader.o
obj-$(CONFIG_MONWRITER) += monwriter.o
obj-$(CONFIG_S390_VMUR) += vmur.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
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 += cpu-hotplug
TARGETS += drivers/dma-buf
TARGETS += drivers/s390x/uvdevice
TARGETS += efivarfs
TARGETS += exec
TARGETS += filesystems

View File

@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
/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 <sys/ioctl.h>
#include <linux/bits.h>
#include "test_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 AR(a) ._ar = 1, .ar = (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__); })
@ -430,9 +433,18 @@ static void test_copy_key_fetch_prot(void)
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)
{
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_SKEYS_SET);
@ -446,6 +458,37 @@ static void test_errors_key(void)
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)
{
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_override();
test_errors_key();
test_termination();
test_errors_key_storage_prot_override();
test_errors_key_fetch_prot_override_not_enabled();
test_errors_key_fetch_prot_override_enabled();