linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] s390: Ultravisor device
@ 2022-02-17 11:37 Steffen Eiden
  2022-02-17 11:37 ` [PATCH 1/3] drivers/s390/char: Add Ultravisor io device Steffen Eiden
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Steffen Eiden @ 2022-02-17 11:37 UTC (permalink / raw)
  To: Heiko Carstens, Vasily Gorbik, Christian Borntraeger,
	Alexander Gordeev, Janosch Frank, David Hildenbrand,
	Claudio Imbrenda, Shuah Khan, linux-kernel, linux-s390, kvm,
	linux-kselftest

This series adds an Ultravisor(UV) device letting the userspace send some
Ultravisor calls to the UV. Currently two calls are supported.
Query Ultravisor Information (QUI) and
Receive Attestation Measurement (Attest[ation]).

The UV device is implemented as a miscdevice accepting only IOCTLs.
The IOCTL cmd specifies the UV call and the IOCTL arg the request
and response data depending on the UV call.
The device driver writes the UV response in the ioctl argument data.

The 'uvdevice' does no checks on the request beside faulty userspace
addresses, if sizes are in a sane range before allocating in kernel space,
and other tests that prevent the system from corruption.
Especially, no checks are made, that will be performed by the UV anyway
(E.g. 'invalid command' in case of attestation on unsupported hardware).
These errors are reported back to Userspace using the UV return code
field.

Steffen Eiden (3):
  drivers/s390/char: Add Ultravisor io device
  drivers/s390/char: Add Ultravisor attestation to uvdevice
  selftests: drivers/s390x: Add uvdevice tests

 MAINTAINERS                                   |   3 +
 arch/s390/include/asm/uv.h                    |  23 +-
 arch/s390/include/uapi/asm/uvdevice.h         |  46 +++
 drivers/s390/char/Kconfig                     |   9 +
 drivers/s390/char/Makefile                    |   1 +
 drivers/s390/char/uvdevice.c                  | 325 ++++++++++++++++++
 tools/testing/selftests/Makefile              |   1 +
 tools/testing/selftests/drivers/.gitignore    |   1 +
 .../selftests/drivers/s390x/uvdevice/Makefile |  22 ++
 .../selftests/drivers/s390x/uvdevice/config   |   1 +
 .../drivers/s390x/uvdevice/test_uvdevice.c    | 280 +++++++++++++++
 11 files changed, 711 insertions(+), 1 deletion(-)
 create mode 100644 arch/s390/include/uapi/asm/uvdevice.h
 create mode 100644 drivers/s390/char/uvdevice.c
 create mode 100644 tools/testing/selftests/drivers/s390x/uvdevice/Makefile
 create mode 100644 tools/testing/selftests/drivers/s390x/uvdevice/config
 create mode 100644 tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c

-- 
2.25.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/3] drivers/s390/char: Add Ultravisor io device
  2022-02-17 11:37 [PATCH 0/3] s390: Ultravisor device Steffen Eiden
@ 2022-02-17 11:37 ` Steffen Eiden
  2022-02-17 12:33   ` Greg KH
  2022-02-17 11:37 ` [PATCH 2/3] drivers/s390/char: Add Ultravisor attestation to uvdevice Steffen Eiden
  2022-02-17 11:37 ` [PATCH 3/3] selftests: drivers/s390x: Add uvdevice tests Steffen Eiden
  2 siblings, 1 reply; 8+ messages in thread
From: Steffen Eiden @ 2022-02-17 11:37 UTC (permalink / raw)
  To: Heiko Carstens, Vasily Gorbik, Christian Borntraeger,
	Alexander Gordeev, Janosch Frank, David Hildenbrand,
	Claudio Imbrenda, Shuah Khan, linux-kernel, linux-s390, kvm,
	linux-kselftest

This patch adds a new miscdevice to expose some Ultravisor functions
to userspace. Userspace can send IOCTLis to the uvdevice that will then
emit a corresponding Ultravisor Call and hands the result over to
userspace. The uvdevice is available if the Ultravisor Call facility is
present.

Userspace is now able to call the Query Ultravisor Information
Ultravisor Command through the uvdevice.

Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
---
 MAINTAINERS                           |   2 +
 arch/s390/include/uapi/asm/uvdevice.h |  27 +++++
 drivers/s390/char/Kconfig             |   9 ++
 drivers/s390/char/Makefile            |   1 +
 drivers/s390/char/uvdevice.c          | 162 ++++++++++++++++++++++++++
 5 files changed, 201 insertions(+)
 create mode 100644 arch/s390/include/uapi/asm/uvdevice.h
 create mode 100644 drivers/s390/char/uvdevice.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 5250298d2817..c7d8d0fe48cf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10457,9 +10457,11 @@ 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/kvm/*/s390x/
 F:	tools/testing/selftests/kvm/s390x/
 
diff --git a/arch/s390/include/uapi/asm/uvdevice.h b/arch/s390/include/uapi/asm/uvdevice.h
new file mode 100644
index 000000000000..f2e4984a6e2e
--- /dev/null
+++ b/arch/s390/include/uapi/asm/uvdevice.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ *  Copyright IBM Corp. 2022
+ *  Author(s): Steffen Eiden <seiden@linux.ibm.com>
+ */
+#ifndef __S390X_ASM_UVDEVICE_H
+#define __S390X_ASM_UVDEVICE_H
+
+#include <linux/types.h>
+
+struct uvio_ioctl_cb {
+	__u32 flags;			/* Currently no flags defined, must be zero */
+	__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_QUI_MAX_LEN		0x8000
+
+#define UVIO_DEVICE_NAME "uv"
+#define UVIO_TYPE_UVC 'u'
+
+#define UVIO_IOCTL_QUI _IOWR(UVIO_TYPE_UVC, 0x01, struct uvio_ioctl_cb)
+
+#endif  /* __S390X_ASM_UVDEVICE_H */
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig
index 6cc4b19acf85..933c0d0062d6 100644
--- a/drivers/s390/char/Kconfig
+++ b/drivers/s390/char/Kconfig
@@ -184,3 +184,12 @@ config S390_VMUR
 	depends on S390
 	help
 	  Character device driver for z/VM reader, puncher and printer.
+
+config UV_UAPI
+	def_tristate m
+	prompt "Ultravisor userspace API"
+	depends on PROTECTED_VIRTUALIZATION_GUEST
+	help
+	  Selecting exposes parts of the UV interface to userspace
+	  by providing a misc character device. Using IOCTLs one
+	  can interact with the UV.
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index c6fdb81a068a..b5c83092210e 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -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_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
diff --git a/drivers/s390/char/uvdevice.c b/drivers/s390/char/uvdevice.c
new file mode 100644
index 000000000000..e8efcbf0e7ab
--- /dev/null
+++ b/drivers/s390/char/uvdevice.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright IBM Corp. 2022
+ *  Author(s): Steffen Eiden <seiden@linux.ibm.com>
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ".\n"
+
+#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>
+
+/**
+ * uvio_qui() - Perform a Query Ultravisor Information UVC.
+ *
+ * uv_ioctl: ioctl control block
+ *
+ * uvio_qui() does a Query Ultravisor Information (QUI) Ultravisor Call.
+ * It creates the uvc qui request and sends it to the Ultravisor. After that
+ * it copies the response to userspace and fills the rc and rrc of uv_ioctl
+ * uv_call with the response values of the Ultravisor.
+ *
+ * Create the UVC structure, send the UVC to UV and write the response in the ioctl struct.
+ *
+ * Return: 0 on success or a negative error code on error.
+ */
+static int uvio_qui(struct uvio_ioctl_cb *uv_ioctl)
+{
+	u8 __user *user_buf_addr = (__user u8 *)uv_ioctl->argument_addr;
+	size_t user_buf_len = uv_ioctl->argument_len;
+	struct uv_cb_header *uvcb_qui = NULL;
+	int ret;
+
+	/*
+	 * Do not check for a too small buffer. If userspace provides a buffer
+	 * that is too small the Ultravisor will complain.
+	 */
+	ret = -EINVAL;
+	if (!user_buf_len || user_buf_len > UVIO_QUI_MAX_LEN)
+		goto out;
+	ret = -ENOMEM;
+	uvcb_qui = kvzalloc(user_buf_len, GFP_KERNEL);
+	if (!uvcb_qui)
+		goto out;
+	uvcb_qui->len = user_buf_len;
+	uvcb_qui->cmd = UVC_CMD_QUI;
+
+	uv_call(0, (u64)uvcb_qui);
+
+	ret = -EFAULT;
+	if (copy_to_user(user_buf_addr, uvcb_qui, uvcb_qui->len))
+		goto out;
+	uv_ioctl->uv_rc = uvcb_qui->rc;
+	uv_ioctl->uv_rrc = uvcb_qui->rrc;
+
+	ret = 0;
+out:
+	kvfree(uvcb_qui);
+	return ret;
+}
+
+static int uvio_copy_and_check_ioctl(struct uvio_ioctl_cb *ioctl, void __user *argp)
+{
+	u64 sum = 0;
+	u64 i;
+
+	if (copy_from_user(ioctl, argp, sizeof(*ioctl)))
+		return -EFAULT;
+	if (ioctl->flags != 0)
+		return -EINVAL;
+	for (i = 0; i < ARRAY_SIZE(ioctl->reserved14); i++)
+		sum += ioctl->reserved14[i];
+	if (sum)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int uvio_dev_open(struct inode *inode, struct file *filp)
+{
+	return 0;
+}
+
+static int uvio_dev_close(struct inode *inodep, struct file *filp)
+{
+	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;
+
+	ret = -ENOMEM;
+	uv_ioctl = vzalloc(sizeof(*uv_ioctl));
+	if (!uv_ioctl)
+		goto out;
+
+	switch (cmd) {
+	case UVIO_IOCTL_QUI:
+		ret = uvio_copy_and_check_ioctl(uv_ioctl, argp);
+		if (ret)
+			goto out;
+		ret = uvio_qui(uv_ioctl);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	if (ret)
+		goto out;
+
+	if (copy_to_user(argp, uv_ioctl, sizeof(*uv_ioctl)))
+		ret = -EFAULT;
+
+ out:
+	vfree(uv_ioctl);
+	return ret;
+}
+
+static const struct file_operations uvio_dev_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = uvio_ioctl,
+	.open = uvio_dev_open,
+	.release = uvio_dev_close,
+	.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");
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/3] drivers/s390/char: Add Ultravisor attestation to uvdevice
  2022-02-17 11:37 [PATCH 0/3] s390: Ultravisor device Steffen Eiden
  2022-02-17 11:37 ` [PATCH 1/3] drivers/s390/char: Add Ultravisor io device Steffen Eiden
@ 2022-02-17 11:37 ` Steffen Eiden
  2022-02-17 11:37 ` [PATCH 3/3] selftests: drivers/s390x: Add uvdevice tests Steffen Eiden
  2 siblings, 0 replies; 8+ messages in thread
From: Steffen Eiden @ 2022-02-17 11:37 UTC (permalink / raw)
  To: Heiko Carstens, Vasily Gorbik, Christian Borntraeger,
	Alexander Gordeev, Janosch Frank, David Hildenbrand,
	Claudio Imbrenda, Shuah Khan, linux-kernel, linux-s390, kvm,
	linux-kselftest

This patch enables userspace to call the Retrieve Attestation Measurement
Ultravisor Call using IOCTLs on the uvdevice.

The uvdevice will do some sanity checks first.
Then, copy the request data to kernel space, build the uvcb,
perform the UV call, and copy the result back to userspace.

Userspace is now able to call the Retrieve Attestation Measurement
Ultravisor Call through the uvdevice.

Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
---
 arch/s390/include/asm/uv.h            |  23 +++-
 arch/s390/include/uapi/asm/uvdevice.h |  19 +++
 drivers/s390/char/uvdevice.c          | 163 ++++++++++++++++++++++++++
 3 files changed, 204 insertions(+), 1 deletion(-)

diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h
index 72d3e49c2860..648375750259 100644
--- a/arch/s390/include/asm/uv.h
+++ b/arch/s390/include/asm/uv.h
@@ -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 {
@@ -218,6 +220,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;
diff --git a/arch/s390/include/uapi/asm/uvdevice.h b/arch/s390/include/uapi/asm/uvdevice.h
index f2e4984a6e2e..7359f77583e0 100644
--- a/arch/s390/include/uapi/asm/uvdevice.h
+++ b/arch/s390/include/uapi/asm/uvdevice.h
@@ -17,11 +17,30 @@ struct uvio_ioctl_cb {
 	__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 */
+};
+
 #define UVIO_QUI_MAX_LEN		0x8000
+#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_QUI _IOWR(UVIO_TYPE_UVC, 0x01, struct uvio_ioctl_cb)
+#define UVIO_IOCTL_ATT _IOWR(UVIO_TYPE_UVC, 0x02, struct uvio_ioctl_cb)
 
 #endif  /* __S390X_ASM_UVDEVICE_H */
diff --git a/drivers/s390/char/uvdevice.c b/drivers/s390/char/uvdevice.c
index e8efcbf0e7ab..3a40a1ee150a 100644
--- a/drivers/s390/char/uvdevice.c
+++ b/drivers/s390/char/uvdevice.c
@@ -65,6 +65,163 @@ static int uvio_qui(struct uvio_ioctl_cb *uv_ioctl)
 	return ret;
 }
 
+/* Create Attestation Measurement functions */
+
+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(struct uv_cb_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 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) {
+		ret = -ENOMEM;
+		add_data = kvzalloc(uvio_attest->add_data_len, GFP_KERNEL);
+		if (!add_data)
+			goto out;
+	}
+
+	ret = -ENOMEM;
+	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)
 {
 	u64 sum = 0;
@@ -113,6 +270,12 @@ static long uvio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 			goto out;
 		ret = uvio_qui(uv_ioctl);
 		break;
+	case UVIO_IOCTL_ATT:
+		ret = uvio_copy_and_check_ioctl(uv_ioctl, argp);
+		if (ret)
+			goto out;
+		ret = uvio_attestation(uv_ioctl);
+		break;
 	default:
 		ret = -EINVAL;
 		break;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/3] selftests: drivers/s390x: Add uvdevice tests
  2022-02-17 11:37 [PATCH 0/3] s390: Ultravisor device Steffen Eiden
  2022-02-17 11:37 ` [PATCH 1/3] drivers/s390/char: Add Ultravisor io device Steffen Eiden
  2022-02-17 11:37 ` [PATCH 2/3] drivers/s390/char: Add Ultravisor attestation to uvdevice Steffen Eiden
@ 2022-02-17 11:37 ` Steffen Eiden
  2022-02-18 23:28   ` Shuah Khan
  2 siblings, 1 reply; 8+ messages in thread
From: Steffen Eiden @ 2022-02-17 11:37 UTC (permalink / raw)
  To: Heiko Carstens, Vasily Gorbik, Christian Borntraeger,
	Alexander Gordeev, Janosch Frank, David Hildenbrand,
	Claudio Imbrenda, Shuah Khan, linux-kernel, linux-s390, kvm,
	linux-kselftest

Adds some selftests to test ioctl error paths of the uv-uapi.

Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
---
 MAINTAINERS                                   |   1 +
 tools/testing/selftests/Makefile              |   1 +
 tools/testing/selftests/drivers/.gitignore    |   1 +
 .../selftests/drivers/s390x/uvdevice/Makefile |  22 ++
 .../selftests/drivers/s390x/uvdevice/config   |   1 +
 .../drivers/s390x/uvdevice/test_uvdevice.c    | 280 ++++++++++++++++++
 6 files changed, 306 insertions(+)
 create mode 100644 tools/testing/selftests/drivers/s390x/uvdevice/Makefile
 create mode 100644 tools/testing/selftests/drivers/s390x/uvdevice/config
 create mode 100644 tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c

diff --git a/MAINTAINERS b/MAINTAINERS
index c7d8d0fe48cf..c6a0311c3fa8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10462,6 +10462,7 @@ 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/
 
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index c852eb40c4f7..3b8abaee9271 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -9,6 +9,7 @@ TARGETS += core
 TARGETS += cpufreq
 TARGETS += cpu-hotplug
 TARGETS += drivers/dma-buf
+TARGETS += drivers/s390x/uvdevice
 TARGETS += efivarfs
 TARGETS += exec
 TARGETS += filesystems
diff --git a/tools/testing/selftests/drivers/.gitignore b/tools/testing/selftests/drivers/.gitignore
index ca74f2e1c719..09e23b5afa96 100644
--- a/tools/testing/selftests/drivers/.gitignore
+++ b/tools/testing/selftests/drivers/.gitignore
@@ -1,2 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
 /dma-buf/udmabuf
+/s390x/uvdevice/test_uvdevice
diff --git a/tools/testing/selftests/drivers/s390x/uvdevice/Makefile b/tools/testing/selftests/drivers/s390x/uvdevice/Makefile
new file mode 100644
index 000000000000..5e701d2708d4
--- /dev/null
+++ b/tools/testing/selftests/drivers/s390x/uvdevice/Makefile
@@ -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
diff --git a/tools/testing/selftests/drivers/s390x/uvdevice/config b/tools/testing/selftests/drivers/s390x/uvdevice/config
new file mode 100644
index 000000000000..f28a04b99eff
--- /dev/null
+++ b/tools/testing/selftests/drivers/s390x/uvdevice/config
@@ -0,0 +1 @@
+CONFIG_S390_UV_UAPI=y
diff --git a/tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c b/tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c
new file mode 100644
index 000000000000..f23663bcab03
--- /dev/null
+++ b/tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c
@@ -0,0 +1,280 @@
+// 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 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, qui) {
+	.ioctl_cmd = UVIO_IOCTL_QUI,
+	.arg_size = BUFFER_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("/dev/uv", O_RDWR);
+
+	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, EINVAL);
+	}
+}
+
+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("/dev/uv", O_RDWR);
+
+	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("/dev/uv", O_RDWR);
+
+	if (fd < 0) {
+		ksft_exit_skip("No uv-device.\n");
+		ksft_exit(KSFT_SKIP);
+	}
+	close(fd);
+	return test_harness_run(argc, argv);
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/3] drivers/s390/char: Add Ultravisor io device
  2022-02-17 11:37 ` [PATCH 1/3] drivers/s390/char: Add Ultravisor io device Steffen Eiden
@ 2022-02-17 12:33   ` Greg KH
  2022-02-21 11:08     ` Steffen Eiden
  0 siblings, 1 reply; 8+ messages in thread
From: Greg KH @ 2022-02-17 12:33 UTC (permalink / raw)
  To: Steffen Eiden
  Cc: Heiko Carstens, Vasily Gorbik, Christian Borntraeger,
	Alexander Gordeev, Janosch Frank, David Hildenbrand,
	Claudio Imbrenda, Shuah Khan, linux-kernel, linux-s390, kvm,
	linux-kselftest

On Thu, Feb 17, 2022 at 06:37:15AM -0500, Steffen Eiden wrote:
> This patch adds a new miscdevice to expose some Ultravisor functions
> to userspace. Userspace can send IOCTLis to the uvdevice that will then
> emit a corresponding Ultravisor Call and hands the result over to
> userspace. The uvdevice is available if the Ultravisor Call facility is
> present.
> 
> Userspace is now able to call the Query Ultravisor Information
> Ultravisor Command through the uvdevice.
> 
> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
> ---
>  MAINTAINERS                           |   2 +
>  arch/s390/include/uapi/asm/uvdevice.h |  27 +++++
>  drivers/s390/char/Kconfig             |   9 ++
>  drivers/s390/char/Makefile            |   1 +
>  drivers/s390/char/uvdevice.c          | 162 ++++++++++++++++++++++++++
>  5 files changed, 201 insertions(+)
>  create mode 100644 arch/s390/include/uapi/asm/uvdevice.h
>  create mode 100644 drivers/s390/char/uvdevice.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5250298d2817..c7d8d0fe48cf 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10457,9 +10457,11 @@ 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/kvm/*/s390x/
>  F:	tools/testing/selftests/kvm/s390x/
>  
> diff --git a/arch/s390/include/uapi/asm/uvdevice.h b/arch/s390/include/uapi/asm/uvdevice.h
> new file mode 100644
> index 000000000000..f2e4984a6e2e
> --- /dev/null
> +++ b/arch/s390/include/uapi/asm/uvdevice.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + *  Copyright IBM Corp. 2022
> + *  Author(s): Steffen Eiden <seiden@linux.ibm.com>
> + */
> +#ifndef __S390X_ASM_UVDEVICE_H
> +#define __S390X_ASM_UVDEVICE_H
> +
> +#include <linux/types.h>
> +
> +struct uvio_ioctl_cb {
> +	__u32 flags;			/* Currently no flags defined, must be zero */
> +	__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_QUI_MAX_LEN		0x8000
> +
> +#define UVIO_DEVICE_NAME "uv"
> +#define UVIO_TYPE_UVC 'u'
> +
> +#define UVIO_IOCTL_QUI _IOWR(UVIO_TYPE_UVC, 0x01, struct uvio_ioctl_cb)
> +
> +#endif  /* __S390X_ASM_UVDEVICE_H */
> diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig
> index 6cc4b19acf85..933c0d0062d6 100644
> --- a/drivers/s390/char/Kconfig
> +++ b/drivers/s390/char/Kconfig
> @@ -184,3 +184,12 @@ config S390_VMUR
>  	depends on S390
>  	help
>  	  Character device driver for z/VM reader, puncher and printer.
> +
> +config UV_UAPI
> +	def_tristate m
> +	prompt "Ultravisor userspace API"
> +	depends on PROTECTED_VIRTUALIZATION_GUEST
> +	help
> +	  Selecting exposes parts of the UV interface to userspace
> +	  by providing a misc character device. Using IOCTLs one
> +	  can interact with the UV.
> diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
> index c6fdb81a068a..b5c83092210e 100644
> --- a/drivers/s390/char/Makefile
> +++ b/drivers/s390/char/Makefile
> @@ -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_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
> diff --git a/drivers/s390/char/uvdevice.c b/drivers/s390/char/uvdevice.c
> new file mode 100644
> index 000000000000..e8efcbf0e7ab
> --- /dev/null
> +++ b/drivers/s390/char/uvdevice.c
> @@ -0,0 +1,162 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright IBM Corp. 2022
> + *  Author(s): Steffen Eiden <seiden@linux.ibm.com>
> + */
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ".\n"
> +
> +#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>
> +
> +/**
> + * uvio_qui() - Perform a Query Ultravisor Information UVC.
> + *
> + * uv_ioctl: ioctl control block
> + *
> + * uvio_qui() does a Query Ultravisor Information (QUI) Ultravisor Call.
> + * It creates the uvc qui request and sends it to the Ultravisor. After that
> + * it copies the response to userspace and fills the rc and rrc of uv_ioctl
> + * uv_call with the response values of the Ultravisor.
> + *
> + * Create the UVC structure, send the UVC to UV and write the response in the ioctl struct.
> + *
> + * Return: 0 on success or a negative error code on error.
> + */
> +static int uvio_qui(struct uvio_ioctl_cb *uv_ioctl)
> +{
> +	u8 __user *user_buf_addr = (__user u8 *)uv_ioctl->argument_addr;
> +	size_t user_buf_len = uv_ioctl->argument_len;
> +	struct uv_cb_header *uvcb_qui = NULL;
> +	int ret;
> +
> +	/*
> +	 * Do not check for a too small buffer. If userspace provides a buffer
> +	 * that is too small the Ultravisor will complain.
> +	 */
> +	ret = -EINVAL;
> +	if (!user_buf_len || user_buf_len > UVIO_QUI_MAX_LEN)
> +		goto out;
> +	ret = -ENOMEM;
> +	uvcb_qui = kvzalloc(user_buf_len, GFP_KERNEL);
> +	if (!uvcb_qui)
> +		goto out;
> +	uvcb_qui->len = user_buf_len;
> +	uvcb_qui->cmd = UVC_CMD_QUI;
> +
> +	uv_call(0, (u64)uvcb_qui);
> +
> +	ret = -EFAULT;
> +	if (copy_to_user(user_buf_addr, uvcb_qui, uvcb_qui->len))
> +		goto out;
> +	uv_ioctl->uv_rc = uvcb_qui->rc;
> +	uv_ioctl->uv_rrc = uvcb_qui->rrc;
> +
> +	ret = 0;
> +out:
> +	kvfree(uvcb_qui);
> +	return ret;
> +}
> +
> +static int uvio_copy_and_check_ioctl(struct uvio_ioctl_cb *ioctl, void __user *argp)
> +{
> +	u64 sum = 0;
> +	u64 i;
> +
> +	if (copy_from_user(ioctl, argp, sizeof(*ioctl)))
> +		return -EFAULT;
> +	if (ioctl->flags != 0)
> +		return -EINVAL;
> +	for (i = 0; i < ARRAY_SIZE(ioctl->reserved14); i++)
> +		sum += ioctl->reserved14[i];
> +	if (sum)
> +		return -EINVAL;

So you can have -1, 1, -1, 1, and so on and cause this to be an
incorrect check.  Just test for 0 and bail out early please.



> +
> +	return 0;
> +}
> +
> +static int uvio_dev_open(struct inode *inode, struct file *filp)
> +{
> +	return 0;
> +}
> +
> +static int uvio_dev_close(struct inode *inodep, struct file *filp)
> +{
> +	return 0;
> +}

If open/close do nothing, no need to provide it at all, just drop them.

> +
> +/*
> + * 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;
> +
> +	ret = -ENOMEM;
> +	uv_ioctl = vzalloc(sizeof(*uv_ioctl));
> +	if (!uv_ioctl)
> +		goto out;
> +
> +	switch (cmd) {
> +	case UVIO_IOCTL_QUI:
> +		ret = uvio_copy_and_check_ioctl(uv_ioctl, argp);
> +		if (ret)
> +			goto out;
> +		ret = uvio_qui(uv_ioctl);
> +		break;
> +	default:
> +		ret = -EINVAL;

Wrong error value :(

> +		break;
> +	}
> +	if (ret)
> +		goto out;
> +
> +	if (copy_to_user(argp, uv_ioctl, sizeof(*uv_ioctl)))
> +		ret = -EFAULT;
> +
> + out:
> +	vfree(uv_ioctl);
> +	return ret;
> +}
> +
> +static const struct file_operations uvio_dev_fops = {
> +	.owner = THIS_MODULE,
> +	.unlocked_ioctl = uvio_ioctl,
> +	.open = uvio_dev_open,
> +	.release = uvio_dev_close,
> +	.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");

Nothing to cause this to automatically be loaded when the "hardware" is
present?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/3] selftests: drivers/s390x: Add uvdevice tests
  2022-02-17 11:37 ` [PATCH 3/3] selftests: drivers/s390x: Add uvdevice tests Steffen Eiden
@ 2022-02-18 23:28   ` Shuah Khan
  2022-02-21 11:09     ` Steffen Eiden
  0 siblings, 1 reply; 8+ messages in thread
From: Shuah Khan @ 2022-02-18 23:28 UTC (permalink / raw)
  To: Steffen Eiden, Heiko Carstens, Vasily Gorbik,
	Christian Borntraeger, Alexander Gordeev, Janosch Frank,
	David Hildenbrand, Claudio Imbrenda, Shuah Khan, linux-kernel,
	linux-s390, kvm, linux-kselftest, Shuah Khan

On 2/17/22 4:37 AM, Steffen Eiden wrote:
> Adds some selftests to test ioctl error paths of the uv-uapi.
> 

Please add information on how to run this test and example output.

> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
> ---
>   MAINTAINERS                                   |   1 +
>   tools/testing/selftests/Makefile              |   1 +
>   tools/testing/selftests/drivers/.gitignore    |   1 +
>   .../selftests/drivers/s390x/uvdevice/Makefile |  22 ++
>   .../selftests/drivers/s390x/uvdevice/config   |   1 +
>   .../drivers/s390x/uvdevice/test_uvdevice.c    | 280 ++++++++++++++++++
>   6 files changed, 306 insertions(+)
>   create mode 100644 tools/testing/selftests/drivers/s390x/uvdevice/Makefile
>   create mode 100644 tools/testing/selftests/drivers/s390x/uvdevice/config
>   create mode 100644 tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c7d8d0fe48cf..c6a0311c3fa8 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10462,6 +10462,7 @@ 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/
>   
> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> index c852eb40c4f7..3b8abaee9271 100644
> --- a/tools/testing/selftests/Makefile
> +++ b/tools/testing/selftests/Makefile
> @@ -9,6 +9,7 @@ TARGETS += core
>   TARGETS += cpufreq
>   TARGETS += cpu-hotplug
>   TARGETS += drivers/dma-buf
> +TARGETS += drivers/s390x/uvdevice
>   TARGETS += efivarfs
>   TARGETS += exec
>   TARGETS += filesystems
> diff --git a/tools/testing/selftests/drivers/.gitignore b/tools/testing/selftests/drivers/.gitignore
> index ca74f2e1c719..09e23b5afa96 100644
> --- a/tools/testing/selftests/drivers/.gitignore
> +++ b/tools/testing/selftests/drivers/.gitignore
> @@ -1,2 +1,3 @@
>   # SPDX-License-Identifier: GPL-2.0-only
>   /dma-buf/udmabuf
> +/s390x/uvdevice/test_uvdevice
> diff --git a/tools/testing/selftests/drivers/s390x/uvdevice/Makefile b/tools/testing/selftests/drivers/s390x/uvdevice/Makefile
> new file mode 100644
> index 000000000000..5e701d2708d4
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/s390x/uvdevice/Makefile
> @@ -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
> diff --git a/tools/testing/selftests/drivers/s390x/uvdevice/config b/tools/testing/selftests/drivers/s390x/uvdevice/config
> new file mode 100644
> index 000000000000..f28a04b99eff
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/s390x/uvdevice/config
> @@ -0,0 +1 @@
> +CONFIG_S390_UV_UAPI=y
> diff --git a/tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c b/tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c
> new file mode 100644
> index 000000000000..f23663bcab03
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c
> @@ -0,0 +1,280 @@
> +// 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 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, qui) {
> +	.ioctl_cmd = UVIO_IOCTL_QUI,
> +	.arg_size = BUFFER_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("/dev/uv", O_RDWR);
> +
> +	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, EINVAL);
> +	}
> +}
> +
> +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("/dev/uv", O_RDWR);

So each test opne and closes the devuce. Can this file stay open
for the duraction of the test run and close it in main() after
test exits?

> +
> +	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("/dev/uv", O_RDWR);
> +
> +	if (fd < 0) {
> +		ksft_exit_skip("No uv-device.\n");

Please add information on which confg options need to be enabled
to run this test - CONFIG_S390_UV_UAPI?

Also add a check and skip when run by non-root if this test requires
root privileges.

> +		ksft_exit(KSFT_SKIP);

You don't need this - ksft_exit_skip() calls exit(KSFT_SKIP)


> +	}
> +	close(fd);

Closing the file before test inocation?

> +	return test_harness_run(argc, argv);
> +}
> 

thanks,
-- Shuah

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/3] drivers/s390/char: Add Ultravisor io device
  2022-02-17 12:33   ` Greg KH
@ 2022-02-21 11:08     ` Steffen Eiden
  0 siblings, 0 replies; 8+ messages in thread
From: Steffen Eiden @ 2022-02-21 11:08 UTC (permalink / raw)
  To: Greg KH
  Cc: Heiko Carstens, Vasily Gorbik, Christian Borntraeger,
	Alexander Gordeev, Janosch Frank, David Hildenbrand,
	Claudio Imbrenda, Shuah Khan, linux-kernel, linux-s390, kvm,
	linux-kselftest

Hey Greg,
thanks for your review.

On 2/17/22 13:33, Greg KH wrote:
> On Thu, Feb 17, 2022 at 06:37:15AM -0500, Steffen Eiden wrote:
>> This patch adds a new miscdevice to expose some Ultravisor functions
>> to userspace. Userspace can send IOCTLis to the uvdevice that will then
>> emit a corresponding Ultravisor Call and hands the result over to
>> userspace. The uvdevice is available if the Ultravisor Call facility is
>> present.
>>
>> Userspace is now able to call the Query Ultravisor Information
>> Ultravisor Command through the uvdevice.
>>
>> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
>> ---
>>   MAINTAINERS                           |   2 +
>>   arch/s390/include/uapi/asm/uvdevice.h |  27 +++++
>>   drivers/s390/char/Kconfig             |   9 ++
>>   drivers/s390/char/Makefile            |   1 +
>>   drivers/s390/char/uvdevice.c          | 162 ++++++++++++++++++++++++++
>>   5 files changed, 201 insertions(+)
>>   create mode 100644 arch/s390/include/uapi/asm/uvdevice.h
>>   create mode 100644 drivers/s390/char/uvdevice.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 5250298d2817..c7d8d0fe48cf 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -10457,9 +10457,11 @@ 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/kvm/*/s390x/
>>   F:	tools/testing/selftests/kvm/s390x/
>>   
>> diff --git a/arch/s390/include/uapi/asm/uvdevice.h b/arch/s390/include/uapi/asm/uvdevice.h
>> new file mode 100644
>> index 000000000000..f2e4984a6e2e
>> --- /dev/null
>> +++ b/arch/s390/include/uapi/asm/uvdevice.h
>> @@ -0,0 +1,27 @@
>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>> +/*
>> + *  Copyright IBM Corp. 2022
>> + *  Author(s): Steffen Eiden <seiden@linux.ibm.com>
>> + */
>> +#ifndef __S390X_ASM_UVDEVICE_H
>> +#define __S390X_ASM_UVDEVICE_H
>> +
>> +#include <linux/types.h>
>> +
>> +struct uvio_ioctl_cb {
>> +	__u32 flags;			/* Currently no flags defined, must be zero */
>> +	__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_QUI_MAX_LEN		0x8000
>> +
>> +#define UVIO_DEVICE_NAME "uv"
>> +#define UVIO_TYPE_UVC 'u'
>> +
>> +#define UVIO_IOCTL_QUI _IOWR(UVIO_TYPE_UVC, 0x01, struct uvio_ioctl_cb)
>> +
>> +#endif  /* __S390X_ASM_UVDEVICE_H */
>> diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig
>> index 6cc4b19acf85..933c0d0062d6 100644
>> --- a/drivers/s390/char/Kconfig
>> +++ b/drivers/s390/char/Kconfig
>> @@ -184,3 +184,12 @@ config S390_VMUR
>>   	depends on S390
>>   	help
>>   	  Character device driver for z/VM reader, puncher and printer.
>> +
>> +config UV_UAPI
>> +	def_tristate m
>> +	prompt "Ultravisor userspace API"
>> +	depends on PROTECTED_VIRTUALIZATION_GUEST
>> +	help
>> +	  Selecting exposes parts of the UV interface to userspace
>> +	  by providing a misc character device. Using IOCTLs one
>> +	  can interact with the UV.
>> diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
>> index c6fdb81a068a..b5c83092210e 100644
>> --- a/drivers/s390/char/Makefile
>> +++ b/drivers/s390/char/Makefile
>> @@ -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_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
>> diff --git a/drivers/s390/char/uvdevice.c b/drivers/s390/char/uvdevice.c
>> new file mode 100644
>> index 000000000000..e8efcbf0e7ab
>> --- /dev/null
>> +++ b/drivers/s390/char/uvdevice.c
>> @@ -0,0 +1,162 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + *  Copyright IBM Corp. 2022
>> + *  Author(s): Steffen Eiden <seiden@linux.ibm.com>
>> + */
>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ".\n"
>> +
>> +#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>
>> +
>> +/**
>> + * uvio_qui() - Perform a Query Ultravisor Information UVC.
>> + *
>> + * uv_ioctl: ioctl control block
>> + *
>> + * uvio_qui() does a Query Ultravisor Information (QUI) Ultravisor Call.
>> + * It creates the uvc qui request and sends it to the Ultravisor. After that
>> + * it copies the response to userspace and fills the rc and rrc of uv_ioctl
>> + * uv_call with the response values of the Ultravisor.
>> + *
>> + * Create the UVC structure, send the UVC to UV and write the response in the ioctl struct.
>> + *
>> + * Return: 0 on success or a negative error code on error.
>> + */
>> +static int uvio_qui(struct uvio_ioctl_cb *uv_ioctl)
>> +{
>> +	u8 __user *user_buf_addr = (__user u8 *)uv_ioctl->argument_addr;
>> +	size_t user_buf_len = uv_ioctl->argument_len;
>> +	struct uv_cb_header *uvcb_qui = NULL;
>> +	int ret;
>> +
>> +	/*
>> +	 * Do not check for a too small buffer. If userspace provides a buffer
>> +	 * that is too small the Ultravisor will complain.
>> +	 */
>> +	ret = -EINVAL;
>> +	if (!user_buf_len || user_buf_len > UVIO_QUI_MAX_LEN)
>> +		goto out;
>> +	ret = -ENOMEM;
>> +	uvcb_qui = kvzalloc(user_buf_len, GFP_KERNEL);
>> +	if (!uvcb_qui)
>> +		goto out;
>> +	uvcb_qui->len = user_buf_len;
>> +	uvcb_qui->cmd = UVC_CMD_QUI;
>> +
>> +	uv_call(0, (u64)uvcb_qui);
>> +
>> +	ret = -EFAULT;
>> +	if (copy_to_user(user_buf_addr, uvcb_qui, uvcb_qui->len))
>> +		goto out;
>> +	uv_ioctl->uv_rc = uvcb_qui->rc;
>> +	uv_ioctl->uv_rrc = uvcb_qui->rrc;
>> +
>> +	ret = 0;
>> +out:
>> +	kvfree(uvcb_qui);
>> +	return ret;
>> +}
>> +
>> +static int uvio_copy_and_check_ioctl(struct uvio_ioctl_cb *ioctl, void __user *argp)
>> +{
>> +	u64 sum = 0;
>> +	u64 i;
>> +
>> +	if (copy_from_user(ioctl, argp, sizeof(*ioctl)))
>> +		return -EFAULT;
>> +	if (ioctl->flags != 0)
>> +		return -EINVAL;
>> +	for (i = 0; i < ARRAY_SIZE(ioctl->reserved14); i++)
>> +		sum += ioctl->reserved14[i];
>> +	if (sum)
>> +		return -EINVAL;
> 
> So you can have -1, 1, -1, 1, and so on and cause this to be an
> incorrect check.  Just test for 0 and bail out early please.
These ints are unsigned, your szenario cannot happen. However, I changed 
it to bailout early anyway.
> 
> 
> 
>> +
>> +	return 0;
>> +}
>> +
>> +static int uvio_dev_open(struct inode *inode, struct file *filp)
>> +{
>> +	return 0;
>> +}
>> +
>> +static int uvio_dev_close(struct inode *inodep, struct file *filp)
>> +{
>> +	return 0;
>> +}
> 
> If open/close do nothing, no need to provide it at all, just drop them.
Makes sense.
> 
>> +
>> +/*
>> + * 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;
>> +
>> +	ret = -ENOMEM;
>> +	uv_ioctl = vzalloc(sizeof(*uv_ioctl));
>> +	if (!uv_ioctl)
>> +		goto out;
>> +
>> +	switch (cmd) {
>> +	case UVIO_IOCTL_QUI:
>> +		ret = uvio_copy_and_check_ioctl(uv_ioctl, argp);
>> +		if (ret)
>> +			goto out;
>> +		ret = uvio_qui(uv_ioctl);
>> +		break;
>> +	default:
>> +		ret = -EINVAL;
> 
> Wrong error value :(
changed
> 
>> +		break;
>> +	}
>> +	if (ret)
>> +		goto out;
>> +
>> +	if (copy_to_user(argp, uv_ioctl, sizeof(*uv_ioctl)))
>> +		ret = -EFAULT;
>> +
>> + out:
>> +	vfree(uv_ioctl);
>> +	return ret;
>> +}
>> +
>> +static const struct file_operations uvio_dev_fops = {
>> +	.owner = THIS_MODULE,
>> +	.unlocked_ioctl = uvio_ioctl,
>> +	.open = uvio_dev_open,
>> +	.release = uvio_dev_close,
>> +	.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");
> 
> Nothing to cause this to automatically be loaded when the "hardware" is
> present?
We do not have anything like a s390 facility bus which could trigger 
such automatic loads. Developing such a bus would be an overkill.

However we could do the approach e.g. kvm-s390 takes. Define 
MODULE_ALIAS(devname:uv) that will trigger an automatic module load if
someone calls open on /dev/uv the first time.
IIRC we need to define a fixed misc minor number with this approach.

something like that:
--- a/drivers/s390/char/uvdevice.c
+++ b/drivers/s390/char/uvdevice.c
@@ -309,3 +309,5
  MODULE_AUTHOR("IBM Corporation");
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("Ultravisor UAPI driver");
+MODULE_ALIAS_MISCDEV(SOME_FIXED_MISC_MINOR);
+MODULE_ALIAS("devname:uv");

We then maybe need to discuss if 'uv' is unique enough.


> 
> thanks,
> 
> greg k-h
> 

Steffen

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/3] selftests: drivers/s390x: Add uvdevice tests
  2022-02-18 23:28   ` Shuah Khan
@ 2022-02-21 11:09     ` Steffen Eiden
  0 siblings, 0 replies; 8+ messages in thread
From: Steffen Eiden @ 2022-02-21 11:09 UTC (permalink / raw)
  To: Shuah Khan, Heiko Carstens, Vasily Gorbik, Christian Borntraeger,
	Alexander Gordeev, Janosch Frank, David Hildenbrand,
	Claudio Imbrenda, Shuah Khan, linux-kernel, linux-s390, kvm,
	linux-kselftest

Hey Shuah,
thanks for your review.

On 2/19/22 00:28, Shuah Khan wrote:
> On 2/17/22 4:37 AM, Steffen Eiden wrote:
>> Adds some selftests to test ioctl error paths of the uv-uapi.
>>
> 
> Please add information on how to run this test and example output.
> 

I will do it in a v2

>> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
>> ---
>>   MAINTAINERS                                   |   1 +
>>   tools/testing/selftests/Makefile              |   1 +
>>   tools/testing/selftests/drivers/.gitignore    |   1 +
>>   .../selftests/drivers/s390x/uvdevice/Makefile |  22 ++
>>   .../selftests/drivers/s390x/uvdevice/config   |   1 +
>>   .../drivers/s390x/uvdevice/test_uvdevice.c    | 280 ++++++++++++++++++
>>   6 files changed, 306 insertions(+)
>>   create mode 100644 
>> tools/testing/selftests/drivers/s390x/uvdevice/Makefile
>>   create mode 100644 
>> tools/testing/selftests/drivers/s390x/uvdevice/config
>>   create mode 100644 
>> tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index c7d8d0fe48cf..c6a0311c3fa8 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -10462,6 +10462,7 @@ 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/
>> diff --git a/tools/testing/selftests/Makefile 
>> b/tools/testing/selftests/Makefile
>> index c852eb40c4f7..3b8abaee9271 100644
>> --- a/tools/testing/selftests/Makefile
>> +++ b/tools/testing/selftests/Makefile
>> @@ -9,6 +9,7 @@ TARGETS += core
>>   TARGETS += cpufreq
>>   TARGETS += cpu-hotplug
>>   TARGETS += drivers/dma-buf
>> +TARGETS += drivers/s390x/uvdevice
>>   TARGETS += efivarfs
>>   TARGETS += exec
>>   TARGETS += filesystems
>> diff --git a/tools/testing/selftests/drivers/.gitignore 
>> b/tools/testing/selftests/drivers/.gitignore
>> index ca74f2e1c719..09e23b5afa96 100644
>> --- a/tools/testing/selftests/drivers/.gitignore
>> +++ b/tools/testing/selftests/drivers/.gitignore
>> @@ -1,2 +1,3 @@
>>   # SPDX-License-Identifier: GPL-2.0-only
>>   /dma-buf/udmabuf
>> +/s390x/uvdevice/test_uvdevice
>> diff --git a/tools/testing/selftests/drivers/s390x/uvdevice/Makefile 
>> b/tools/testing/selftests/drivers/s390x/uvdevice/Makefile
>> new file mode 100644
>> index 000000000000..5e701d2708d4
>> --- /dev/null
>> +++ b/tools/testing/selftests/drivers/s390x/uvdevice/Makefile
>> @@ -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
>> diff --git a/tools/testing/selftests/drivers/s390x/uvdevice/config 
>> b/tools/testing/selftests/drivers/s390x/uvdevice/config
>> new file mode 100644
>> index 000000000000..f28a04b99eff
>> --- /dev/null
>> +++ b/tools/testing/selftests/drivers/s390x/uvdevice/config
>> @@ -0,0 +1 @@
>> +CONFIG_S390_UV_UAPI=y
>> diff --git 
>> a/tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c 
>> b/tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c
>> new file mode 100644
>> index 000000000000..f23663bcab03
>> --- /dev/null
>> +++ b/tools/testing/selftests/drivers/s390x/uvdevice/test_uvdevice.c
>> @@ -0,0 +1,280 @@
>> +// 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 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, qui) {
>> +    .ioctl_cmd = UVIO_IOCTL_QUI,
>> +    .arg_size = BUFFER_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("/dev/uv", O_RDWR);
>> +
>> +    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, EINVAL);
>> +    }
>> +}
>> +
>> +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("/dev/uv", O_RDWR);
> 
> So each test opne and closes the devuce. Can this file stay open
> for the duraction of the test run and close it in main() after
> test exits?

IIRC 'test_harness_run' will never return and instead exit the program.
Therefore, I cannot do anything after that function including closing 
files if I want a clean cleanup of my test.

> 
>> +
>> +    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("/dev/uv", O_RDWR);
>> +
>> +    if (fd < 0) {
>> +        ksft_exit_skip("No uv-device.\n");
> 
> Please add information on which confg options need to be enabled
> to run this test - CONFIG_S390_UV_UAPI?
OK.
> 
> Also add a check and skip when run by non-root if this test requires
> root privileges.
The test just needs RDWR access to /dev/uv.
> 
>> +        ksft_exit(KSFT_SKIP);
> 
> You don't need this - ksft_exit_skip() calls exit(KSFT_SKIP)
thanks, I missed that.
> 
> 
>> +    }
>> +    close(fd);
> 
> Closing the file before test inocation?

See comment about fixture setup/cleanup.
If you prefer, I can skip the close and use fd in the tests instead of 
self->uv_fd. However, I am unabale to do an explicit close to /dev/uv 
after running the tests.
> 
>> +    return test_harness_run(argc, argv);
>> +}
>>
> 
> thanks,
> -- Shuah

Steffen

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2022-02-21 11:21 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-17 11:37 [PATCH 0/3] s390: Ultravisor device Steffen Eiden
2022-02-17 11:37 ` [PATCH 1/3] drivers/s390/char: Add Ultravisor io device Steffen Eiden
2022-02-17 12:33   ` Greg KH
2022-02-21 11:08     ` Steffen Eiden
2022-02-17 11:37 ` [PATCH 2/3] drivers/s390/char: Add Ultravisor attestation to uvdevice Steffen Eiden
2022-02-17 11:37 ` [PATCH 3/3] selftests: drivers/s390x: Add uvdevice tests Steffen Eiden
2022-02-18 23:28   ` Shuah Khan
2022-02-21 11:09     ` Steffen Eiden

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).