IBM Secure Execution guests may want to inject secrets into the Ultravisor(UV).
Also they should be able to know which secrets the UV possesses and prevent the
further addition of more secrets.
Therefore, add three new Ultravisor-Calls and expose them via the uvdevice:
Add Secret, List Secrets, and Lock Secrets.
The uvdevice still acts as the messenger only and does not inspect or modify
the requests. Only some sanity checks are made to prevent the kernel from corruption.
Also add a new IOCTL to get information about the supported UV-calls of the uvdevice.
As userspace wants to know which secrets, types, etc. are supported expose the corresponding
UV Query info data to userspace via sysfs.
The series contains:
* A new info IOCTL, giving information about the capabilities of the uvdevice and UV
* 3 patches adding new Ultravisor-Calls and expose them to userspace
* A patch with an Ultravisor Query Info update for the new secret related information
Steffen
Steffen Eiden (5):
s390/uvdevice: Add info IOCTL
s390/uvdevice: Add 'Add Secret' UVC
s390/uvdevice: Add 'List Secrets' UVC
s390/uvdevice: Add 'Lock Secret Store' UVC
s390/uv: Update query for secret-UVCs
arch/s390/boot/uv.c | 4 +
arch/s390/include/asm/uv.h | 30 +++-
arch/s390/include/uapi/asm/uvdevice.h | 56 ++++++-
arch/s390/kernel/uv.c | 40 +++++
drivers/s390/char/uvdevice.c | 229 +++++++++++++++++++++++++-
5 files changed, 350 insertions(+), 9 deletions(-)
--
2.40.1
Userspace can call the Add Secret Ultravisor Call
using IOCTLs on the uvdevice.
During the handling of the new IOCTL nr the uvdevice will do some sanity
checks first. Then, copy the request data to kernel space, perform the
Ultravisor command, and copy the return codes to userspace.
If the Add Secret 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. This is also true for
any other possible error code the UV can return.
Signed-off-by: Steffen Eiden <[email protected]>
---
arch/s390/include/asm/uv.h | 14 +++++++
arch/s390/include/uapi/asm/uvdevice.h | 4 ++
drivers/s390/char/uvdevice.c | 58 +++++++++++++++++++++++++++
3 files changed, 76 insertions(+)
diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h
index 28a9ad57b6f1..a7dff64e1e24 100644
--- a/arch/s390/include/asm/uv.h
+++ b/arch/s390/include/asm/uv.h
@@ -58,6 +58,7 @@
#define UVC_CMD_SET_SHARED_ACCESS 0x1000
#define UVC_CMD_REMOVE_SHARED_ACCESS 0x1001
#define UVC_CMD_RETR_ATTEST 0x1020
+#define UVC_CMD_ADD_SECRET 0x1031
/* Bits in installed uv calls */
enum uv_cmds_inst {
@@ -88,6 +89,7 @@ enum uv_cmds_inst {
BIT_UVC_CMD_DUMP_CPU = 26,
BIT_UVC_CMD_DUMP_COMPLETE = 27,
BIT_UVC_CMD_RETR_ATTEST = 28,
+ BIT_UVC_CMD_ADD_SECRET = 29,
};
enum uv_feat_ind {
@@ -292,6 +294,18 @@ struct uv_cb_dump_complete {
u64 reserved30[5];
} __packed __aligned(8);
+/*
+ * A common call for pv guests that contains a single address
+ * Examples:
+ * Add Secret
+ */
+struct uv_cb_guest_addr {
+ struct uv_cb_header header;
+ u64 reserved08[3];
+ u64 addr;
+ u64 reserved28[4];
+} __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 ed58979b8438..e919b802213c 100644
--- a/arch/s390/include/uapi/asm/uvdevice.h
+++ b/arch/s390/include/uapi/asm/uvdevice.h
@@ -72,6 +72,7 @@ struct uvio_uvdev_info {
#define UVIO_ATT_ARCB_MAX_LEN 0x100000
#define UVIO_ATT_MEASUREMENT_MAX_LEN 0x8000
#define UVIO_ATT_ADDITIONAL_MAX_LEN 0x8000
+#define UVIO_ADD_SECRET_MAX_LEN 0x100000
#define UVIO_DEVICE_NAME "uv"
#define UVIO_TYPE_UVC 'u'
@@ -79,6 +80,7 @@ struct uvio_uvdev_info {
enum UVIO_IOCTL_NR {
UVIO_IOCTL_UVDEV_INFO_NR = 0x00,
UVIO_IOCTL_ATT_NR,
+ UVIO_IOCTL_ADD_SECRET_NR,
/* must be the last entry */
UVIO_IOCTL_NUM_IOCTLS
};
@@ -86,9 +88,11 @@ enum UVIO_IOCTL_NR {
#define UVIO_IOCTL(nr) _IOWR(UVIO_TYPE_UVC, nr, struct uvio_ioctl_cb)
#define UVIO_IOCTL_UVDEV_INFO UVIO_IOCTL(UVIO_IOCTL_UVDEV_INFO_NR)
#define UVIO_IOCTL_ATT UVIO_IOCTL(UVIO_IOCTL_ATT_NR)
+#define UVIO_IOCTL_ADD_SECRET UVIO_IOCTL(UVIO_IOCTL_ADD_SECRET_NR)
#define UVIO_SUPP_CALL(nr) (1ULL << (nr))
#define UVIO_SUPP_UDEV_INFO UVIO_SUPP_CALL(UVIO_IOCTL_UDEV_INFO_NR)
#define UVIO_SUPP_ATT UVIO_SUPP_CALL(UVIO_IOCTL_ATT_NR)
+#define UVIO_SUPP_ADD_SECRET UVIO_SUPP_CALL(UVIO_IOCTL_ADD_SECRET_NR)
#endif /* __S390_ASM_UVDEVICE_H */
diff --git a/drivers/s390/char/uvdevice.c b/drivers/s390/char/uvdevice.c
index 27f5bf1cf27a..ba3e60dc4ba8 100644
--- a/drivers/s390/char/uvdevice.c
+++ b/drivers/s390/char/uvdevice.c
@@ -37,6 +37,7 @@
static const u64 ioctl_nr_to_uvc_bit[] __initconst = {
[UVIO_IOCTL_UVDEV_INFO_NR] = -1UL,
[UVIO_IOCTL_ATT_NR] = BIT_UVC_CMD_RETR_ATTEST,
+ [UVIO_IOCTL_ADD_SECRET_NR] = BIT_UVC_CMD_ADD_SECRET,
};
static_assert(ARRAY_SIZE(ioctl_nr_to_uvc_bit) == UVIO_IOCTL_NUM_IOCTLS);
@@ -231,6 +232,60 @@ static int uvio_attestation(struct uvio_ioctl_cb *uv_ioctl)
return ret;
}
+/** uvio_add_secret() - perform an Add Secret UVC
+ *
+ * @uv_ioctl: ioctl control block
+ *
+ * uvio_add_secret() performs the Add Secret Ultravisor Call.
+ * It verifies that the given userspace argument address is valid and its size
+ * is sane. Every other check is made by the Ultravisor (UV) and won't result
+ * in a negative return value. It copies the request to kernelspace,
+ * performs the UV-call, and copies the return codes to the ioctl control block.
+ * The argument has to point to an Add Secret Request Control Block.
+ * It is an encrypted and cryptographically verified request generated by
+ * userspace to insert the actual secret into the UV.
+ * If the Add Secret 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_add_secret(struct uvio_ioctl_cb *uv_ioctl)
+{
+ void __user *user_buf_arg = (void __user *)uv_ioctl->argument_addr;
+ struct uv_cb_guest_addr uvcb = {
+ .header.len = sizeof(uvcb),
+ .header.cmd = UVC_CMD_ADD_SECRET,
+ };
+ void *asrcb = NULL;
+ int ret;
+
+ if (uv_ioctl->argument_len > UVIO_ADD_SECRET_MAX_LEN)
+ return -EINVAL;
+ if (uv_ioctl->argument_len == 0)
+ return -EINVAL;
+
+ asrcb = kvzalloc(uv_ioctl->argument_len, GFP_KERNEL);
+ if (!asrcb)
+ return -EINVAL;
+
+ ret = -EFAULT;
+ if (copy_from_user(asrcb, user_buf_arg, uv_ioctl->argument_len))
+ goto out;
+
+ ret = 0;
+ uvcb.addr = (u64)asrcb;
+ uv_call_sched(0, (u64)&uvcb);
+ uv_ioctl->uv_rc = uvcb.header.rc;
+ uv_ioctl->uv_rrc = uvcb.header.rrc;
+
+out:
+ kvfree(asrcb);
+ return ret;
+}
+
static int uvio_copy_and_check_ioctl(struct uvio_ioctl_cb *ioctl, void __user *argp,
unsigned long cmd)
{
@@ -275,6 +330,9 @@ static long uvio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case UVIO_IOCTL_ATT_NR:
ret = uvio_attestation(&uv_ioctl);
break;
+ case UVIO_IOCTL_ADD_SECRET_NR:
+ ret = uvio_add_secret(&uv_ioctl);
+ break;
default:
ret = -ENOIOCTLCMD;
break;
--
2.40.1
Update the query struct such that secret-UVC related
information can be parsed.
Add sysfs files for these new values.
Signed-off-by: Steffen Eiden <[email protected]>
---
arch/s390/boot/uv.c | 4 ++++
arch/s390/include/asm/uv.h | 11 ++++++++++-
arch/s390/kernel/uv.c | 40 ++++++++++++++++++++++++++++++++++++++
3 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/arch/s390/boot/uv.c b/arch/s390/boot/uv.c
index 0a077c0a2056..323b5cae3cf1 100644
--- a/arch/s390/boot/uv.c
+++ b/arch/s390/boot/uv.c
@@ -47,6 +47,10 @@ void uv_query_info(void)
uv_info.conf_dump_finalize_len = uvcb.conf_dump_finalize_len;
uv_info.supp_att_req_hdr_ver = uvcb.supp_att_req_hdr_ver;
uv_info.supp_att_pflags = uvcb.supp_att_pflags;
+ uv_info.supp_add_secret_req_ver = uvcb.supp_add_secret_req_ver;
+ uv_info.supp_add_secret_pcf = uvcb.supp_add_secret_pcf;
+ uv_info.supp_secret_types = uvcb.supp_secret_types;
+ uv_info.max_secrets = uvcb.max_num_secrets;
}
#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h
index 6180ac8909d5..eb2e11d8123f 100644
--- a/arch/s390/include/asm/uv.h
+++ b/arch/s390/include/asm/uv.h
@@ -135,7 +135,12 @@ struct uv_cb_qui {
u64 reservedd8; /* 0x00d8 */
u64 supp_att_req_hdr_ver; /* 0x00e0 */
u64 supp_att_pflags; /* 0x00e8 */
- u8 reservedf0[256 - 240]; /* 0x00f0 */
+ u64 reservedf0; /* 0x00f0 */
+ u64 supp_add_secret_req_ver; /* 0x00f8 */
+ u64 supp_add_secret_pcf; /* 0x0100 */
+ u64 supp_secret_types; /* 0x0180 */
+ u16 max_num_secrets; /* 0x0110 */
+ u8 reserved112[288 - 274]; /* 0x0112 */
} __packed __aligned(8);
/* Initialize Ultravisor */
@@ -384,6 +389,10 @@ struct uv_info {
unsigned long conf_dump_finalize_len;
unsigned long supp_att_req_hdr_ver;
unsigned long supp_att_pflags;
+ unsigned long supp_add_secret_req_ver;
+ unsigned long supp_add_secret_pcf;
+ unsigned long supp_secret_types;
+ unsigned short max_secrets;
};
extern struct uv_info uv_info;
diff --git a/arch/s390/kernel/uv.c b/arch/s390/kernel/uv.c
index 9f18a4af9c13..381444511bb7 100644
--- a/arch/s390/kernel/uv.c
+++ b/arch/s390/kernel/uv.c
@@ -587,6 +587,42 @@ static ssize_t uv_query_supp_att_pflags(struct kobject *kobj,
static struct kobj_attribute uv_query_supp_att_pflags_attr =
__ATTR(supp_att_pflags, 0444, uv_query_supp_att_pflags, NULL);
+static ssize_t uv_query_supp_add_secret_req_ver(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return scnprintf(page, PAGE_SIZE, "%lx\n", uv_info.supp_add_secret_req_ver);
+}
+
+static struct kobj_attribute uv_query_supp_add_secret_req_ver_attr =
+ __ATTR(supp_add_secret_req_ver, 0444, uv_query_supp_add_secret_req_ver, NULL);
+
+static ssize_t uv_query_supp_add_secret_pcf(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return scnprintf(page, PAGE_SIZE, "%lx\n", uv_info.supp_add_secret_pcf);
+}
+
+static struct kobj_attribute uv_query_supp_add_secret_pcf_attr =
+ __ATTR(supp_add_secret_pcf, 0444, uv_query_supp_add_secret_pcf, NULL);
+
+static ssize_t uv_query_supp_secret_types(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return scnprintf(page, PAGE_SIZE, "%lx\n", uv_info.supp_secret_types);
+}
+
+static struct kobj_attribute uv_query_supp_secret_types_attr =
+ __ATTR(supp_secret_types, 0444, uv_query_supp_secret_types, NULL);
+
+static ssize_t uv_query_max_secrets(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return scnprintf(page, PAGE_SIZE, "%d\n", uv_info.max_secrets);
+}
+
+static struct kobj_attribute uv_query_max_num_secrets_attr =
+ __ATTR(max_secrets, 0444, uv_query_max_secrets, NULL);
+
static struct attribute *uv_query_attrs[] = {
&uv_query_facilities_attr.attr,
&uv_query_feature_indications_attr.attr,
@@ -600,6 +636,10 @@ static struct attribute *uv_query_attrs[] = {
&uv_query_dump_cpu_len_attr.attr,
&uv_query_supp_att_req_hdr_ver_attr.attr,
&uv_query_supp_att_pflags_attr.attr,
+ &uv_query_supp_add_secret_req_ver_attr.attr,
+ &uv_query_supp_add_secret_pcf_attr.attr,
+ &uv_query_supp_secret_types_attr.attr,
+ &uv_query_max_num_secrets_attr.attr,
NULL,
};
--
2.40.1
Userspace can call the Lock Secret Store Ultravisor Call
using IOCTLs on the uvdevice.
During the handling of the new IOCTL nr the uvdevice will do some sanity
checks first. Then, perform the Ultravisor command, and copy the
return codes to userspace.
If the Lock Secrets 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. This is also true for any other
possible error code the UV can return.
Signed-off-by: Steffen Eiden <[email protected]>
---
arch/s390/include/asm/uv.h | 2 ++
arch/s390/include/uapi/asm/uvdevice.h | 3 +++
drivers/s390/char/uvdevice.c | 39 +++++++++++++++++++++++++++
3 files changed, 44 insertions(+)
diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h
index 1e4f0f6d4923..6180ac8909d5 100644
--- a/arch/s390/include/asm/uv.h
+++ b/arch/s390/include/asm/uv.h
@@ -60,6 +60,7 @@
#define UVC_CMD_RETR_ATTEST 0x1020
#define UVC_CMD_ADD_SECRET 0x1031
#define UVC_CMD_LIST_SECRETS 0x1033
+#define UVC_CMD_LOCK_SECRETS 0x1034
/* Bits in installed uv calls */
enum uv_cmds_inst {
@@ -92,6 +93,7 @@ enum uv_cmds_inst {
BIT_UVC_CMD_RETR_ATTEST = 28,
BIT_UVC_CMD_ADD_SECRET = 29,
BIT_UVC_CMD_LIST_SECRETS = 30,
+ BIT_UVC_CMD_LOCK_SECRETS = 31,
};
enum uv_feat_ind {
diff --git a/arch/s390/include/uapi/asm/uvdevice.h b/arch/s390/include/uapi/asm/uvdevice.h
index cf12d6b8d8d8..b6e8d47dd589 100644
--- a/arch/s390/include/uapi/asm/uvdevice.h
+++ b/arch/s390/include/uapi/asm/uvdevice.h
@@ -83,6 +83,7 @@ enum UVIO_IOCTL_NR {
UVIO_IOCTL_ATT_NR,
UVIO_IOCTL_ADD_SECRET_NR,
UVIO_IOCTL_LIST_SECRETS_NR,
+ UVIO_IOCTL_LOCK_SECRETS_NR,
/* must be the last entry */
UVIO_IOCTL_NUM_IOCTLS
};
@@ -92,11 +93,13 @@ enum UVIO_IOCTL_NR {
#define UVIO_IOCTL_ATT UVIO_IOCTL(UVIO_IOCTL_ATT_NR)
#define UVIO_IOCTL_ADD_SECRET UVIO_IOCTL(UVIO_IOCTL_ADD_SECRET_NR)
#define UVIO_IOCTL_LIST_SECRETS UVIO_IOCTL(UVIO_IOCTL_LIST_SECRETS_NR)
+#define UVIO_IOCTL_LOCK_SECRETS UVIO_IOCTL(UVIO_IOCTL_LOCK_SECRETS_NR)
#define UVIO_SUPP_CALL(nr) (1ULL << (nr))
#define UVIO_SUPP_UDEV_INFO UVIO_SUPP_CALL(UVIO_IOCTL_UDEV_INFO_NR)
#define UVIO_SUPP_ATT UVIO_SUPP_CALL(UVIO_IOCTL_ATT_NR)
#define UVIO_SUPP_ADD_SECRET UVIO_SUPP_CALL(UVIO_IOCTL_ADD_SECRET_NR)
#define UVIO_SUPP_LIST_SECRETS UVIO_SUPP_CALL(UVIO_IOCTL_LIST_SECRETS_NR)
+#define UVIO_SUPP_LOCK_SECRETS UVIO_SUPP_CALL(UVIO_IOCTL_LOCK_SECRETS_NR)
#endif /* __S390_ASM_UVDEVICE_H */
diff --git a/drivers/s390/char/uvdevice.c b/drivers/s390/char/uvdevice.c
index 5fcd719047ab..621c9f2b9853 100644
--- a/drivers/s390/char/uvdevice.c
+++ b/drivers/s390/char/uvdevice.c
@@ -39,6 +39,7 @@ static const u64 ioctl_nr_to_uvc_bit[] __initconst = {
[UVIO_IOCTL_ATT_NR] = BIT_UVC_CMD_RETR_ATTEST,
[UVIO_IOCTL_ADD_SECRET_NR] = BIT_UVC_CMD_ADD_SECRET,
[UVIO_IOCTL_LIST_SECRETS_NR] = BIT_UVC_CMD_LIST_SECRETS,
+ [UVIO_IOCTL_LOCK_SECRETS_NR] = BIT_UVC_CMD_LOCK_SECRETS,
};
static_assert(ARRAY_SIZE(ioctl_nr_to_uvc_bit) == UVIO_IOCTL_NUM_IOCTLS);
@@ -338,6 +339,41 @@ static int uvio_list_secrets(struct uvio_ioctl_cb *uv_ioctl)
return ret;
}
+/** uvio_lock_secrets() - perform a Lock Secret Store UVC
+ *
+ * @uv_ioctl: ioctl control block
+ *
+ * uvio_lock_secrets() performs the Lock Secret Store Ultravisor Call.
+ * It performs the UV-call and copies the return codes to the
+ * ioctl control block.
+ *
+ * The argument address and size must be 0.
+ *
+ * If the List Secrets 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_lock_secrets(struct uvio_ioctl_cb *ioctl)
+{
+ struct uv_cb_nodata uvcb = {
+ .header.len = sizeof(uvcb),
+ .header.cmd = UVC_CMD_LOCK_SECRETS,
+ };
+
+ if (ioctl->argument_addr || ioctl->argument_len)
+ return -EINVAL;
+
+ uv_call(0, (u64)&uvcb);
+ ioctl->uv_rc = uvcb.header.rc;
+ ioctl->uv_rrc = uvcb.header.rrc;
+
+ return 0;
+}
+
static int uvio_copy_and_check_ioctl(struct uvio_ioctl_cb *ioctl, void __user *argp,
unsigned long cmd)
{
@@ -388,6 +424,9 @@ static long uvio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case UVIO_IOCTL_LIST_SECRETS_NR:
ret = uvio_list_secrets(&uv_ioctl);
break;
+ case UVIO_IOCTL_LOCK_SECRETS_NR:
+ ret = uvio_lock_secrets(&uv_ioctl);
+ break;
default:
ret = -ENOIOCTLCMD;
break;
--
2.40.1
Add an IOCTL that allows userspace to find out which IOCTLs the uvdevice
supports without trial and error.
Explicitly expose the IOCTL nr for the request types.
Signed-off-by: Steffen Eiden <[email protected]>
---
arch/s390/include/uapi/asm/uvdevice.h | 45 +++++++++++++++-
drivers/s390/char/uvdevice.c | 77 ++++++++++++++++++++++++---
2 files changed, 114 insertions(+), 8 deletions(-)
diff --git a/arch/s390/include/uapi/asm/uvdevice.h b/arch/s390/include/uapi/asm/uvdevice.h
index 10a5ac918e02..ed58979b8438 100644
--- a/arch/s390/include/uapi/asm/uvdevice.h
+++ b/arch/s390/include/uapi/asm/uvdevice.h
@@ -32,6 +32,36 @@ struct uvio_attest {
__u16 reserved136; /* 0x0136 */
};
+/**
+ * uvio_uvdev_info - Information of supported functions
+ *
+ * @supp_uvio_cmds - supported IOCTLs by this device
+ * @supp_uv_cmds - supported UVCs corresponding to the IOCTL
+ *
+ * UVIO request to get information about supported request types by this
+ * uvdevice and the Ultravisor.
+ * Everything is output. Bits are in LSB0 ordering.
+ * If the bit is set in both, @supp_uvio_cmds and @supp_uv_cmds,
+ * the uvdevice and the Ultravisor support that call.
+ *
+ * Note that bit 0 (UVIO_IOCTL_UVDEV_INFO_NR) is always zero for `supp_uv_cmds`
+ * as there is no corresponding UV-call.
+ */
+struct uvio_uvdev_info {
+ /*
+ * If bit `n` is set, this device supports the IOCTL with nr `n`.
+ */
+ __u64 supp_uvio_cmds;
+ /*
+ * If bit `n` is set, the Ultravisor(UV) supports the UV-call
+ * corresponding to the IOCTL with nr `n` in the calling contextx
+ * (host or guest).
+ * The value is only valid if the corresponding bit in @supp_uvio_cmds
+ * is set as well.
+ */
+ __u64 supp_uv_cmds;
+};
+
/*
* 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
@@ -46,6 +76,19 @@ struct uvio_attest {
#define UVIO_DEVICE_NAME "uv"
#define UVIO_TYPE_UVC 'u'
-#define UVIO_IOCTL_ATT _IOWR(UVIO_TYPE_UVC, 0x01, struct uvio_ioctl_cb)
+enum UVIO_IOCTL_NR {
+ UVIO_IOCTL_UVDEV_INFO_NR = 0x00,
+ UVIO_IOCTL_ATT_NR,
+ /* must be the last entry */
+ UVIO_IOCTL_NUM_IOCTLS
+};
+
+#define UVIO_IOCTL(nr) _IOWR(UVIO_TYPE_UVC, nr, struct uvio_ioctl_cb)
+#define UVIO_IOCTL_UVDEV_INFO UVIO_IOCTL(UVIO_IOCTL_UVDEV_INFO_NR)
+#define UVIO_IOCTL_ATT UVIO_IOCTL(UVIO_IOCTL_ATT_NR)
+
+#define UVIO_SUPP_CALL(nr) (1ULL << (nr))
+#define UVIO_SUPP_UDEV_INFO UVIO_SUPP_CALL(UVIO_IOCTL_UDEV_INFO_NR)
+#define UVIO_SUPP_ATT UVIO_SUPP_CALL(UVIO_IOCTL_ATT_NR)
#endif /* __S390_ASM_UVDEVICE_H */
diff --git a/drivers/s390/char/uvdevice.c b/drivers/s390/char/uvdevice.c
index 1d40457c7b10..27f5bf1cf27a 100644
--- a/drivers/s390/char/uvdevice.c
+++ b/drivers/s390/char/uvdevice.c
@@ -20,6 +20,7 @@
* channel for userspace to the Ultravisor.
*/
+#include "asm-generic/ioctl.h"
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
@@ -32,6 +33,51 @@
#include <asm/uvdevice.h>
#include <asm/uv.h>
+/* Mapping from IOCTL-nr to UVC-bit */
+static const u64 ioctl_nr_to_uvc_bit[] __initconst = {
+ [UVIO_IOCTL_UVDEV_INFO_NR] = -1UL,
+ [UVIO_IOCTL_ATT_NR] = BIT_UVC_CMD_RETR_ATTEST,
+};
+
+static_assert(ARRAY_SIZE(ioctl_nr_to_uvc_bit) == UVIO_IOCTL_NUM_IOCTLS);
+
+static struct uvio_uvdev_info uvdev_info = {
+ .supp_uvio_cmds = GENMASK_ULL(UVIO_IOCTL_NUM_IOCTLS - 1, 0),
+};
+
+static void __init set_supp_uv_cmds(struct uvio_uvdev_info *info)
+{
+ int i;
+
+ for (i = 0; i < UVIO_IOCTL_NUM_IOCTLS; i++) {
+ if (ioctl_nr_to_uvc_bit[i] == -1UL)
+ continue;
+ if (!test_bit_inv(ioctl_nr_to_uvc_bit[i], uv_info.inst_calls_list))
+ continue;
+ set_bit(i, (unsigned long *)&info->supp_uv_cmds);
+ }
+}
+
+/**
+ * uvio_uvdev_info() - get information about the uvdevice
+ *
+ * @uv_ioctl: ioctl control block
+ *
+ * Lists all supported IOCTLs by this uvdevice
+ */
+static int uvio_uvdev_info(struct uvio_ioctl_cb *uv_ioctl)
+{
+ void __user *user_buf_arg = (void __user *)uv_ioctl->argument_addr;
+
+ if (uv_ioctl->argument_len < sizeof(uvdev_info))
+ return -EINVAL;
+ if (copy_to_user(user_buf_arg, &uvdev_info, sizeof(uvdev_info)))
+ return -EFAULT;
+
+ uv_ioctl->uv_rc = UVC_RC_EXECUTED;
+ return 0;
+}
+
static int uvio_build_uvcb_attest(struct uv_cb_attest *uvcb_attest, u8 *arcb,
u8 *meas, u8 *add_data, struct uvio_attest *uvio_attest)
{
@@ -185,8 +231,19 @@ static int uvio_attestation(struct uvio_ioctl_cb *uv_ioctl)
return ret;
}
-static int uvio_copy_and_check_ioctl(struct uvio_ioctl_cb *ioctl, void __user *argp)
+static int uvio_copy_and_check_ioctl(struct uvio_ioctl_cb *ioctl, void __user *argp,
+ unsigned long cmd)
{
+ u8 nr = _IOC_NR(cmd);
+
+ if (_IOC_DIR(cmd) != (_IOC_READ | _IOC_WRITE))
+ return -ENOIOCTLCMD;
+ if (_IOC_TYPE(cmd) != UVIO_TYPE_UVC)
+ return -ENOIOCTLCMD;
+ if (nr >= UVIO_IOCTL_NUM_IOCTLS)
+ return -ENOIOCTLCMD;
+ if (_IOC_SIZE(cmd) != sizeof(*ioctl))
+ return -ENOIOCTLCMD;
if (copy_from_user(ioctl, argp, sizeof(*ioctl)))
return -EFAULT;
if (ioctl->flags != 0)
@@ -194,7 +251,7 @@ static int uvio_copy_and_check_ioctl(struct uvio_ioctl_cb *ioctl, void __user *a
if (memchr_inv(ioctl->reserved14, 0, sizeof(ioctl->reserved14)))
return -EINVAL;
- return 0;
+ return nr;
}
/*
@@ -205,12 +262,17 @@ 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;
+ int nr;
+
+ nr = uvio_copy_and_check_ioctl(&uv_ioctl, argp, cmd);
+ if (nr < 0)
+ return nr;
- switch (cmd) {
- case UVIO_IOCTL_ATT:
- ret = uvio_copy_and_check_ioctl(&uv_ioctl, argp);
- if (ret)
- return ret;
+ switch (nr) {
+ case UVIO_IOCTL_UVDEV_INFO_NR:
+ ret = uvio_uvdev_info(&uv_ioctl);
+ break;
+ case UVIO_IOCTL_ATT_NR:
ret = uvio_attestation(&uv_ioctl);
break;
default:
@@ -245,6 +307,7 @@ static void __exit uvio_dev_exit(void)
static int __init uvio_dev_init(void)
{
+ set_supp_uv_cmds(&uvdev_info);
return misc_register(&uvio_dev_miscdev);
}
--
2.40.1
Userspace can call the List Secrets Ultravisor Call
using IOCTLs on the uvdevice.
During the handling of the new IOCTL nr the uvdevice will do some sanity
checks first. Then, perform the Ultravisor command, and copy the answer
to userspace.
If the List Secrets 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. This is also true for any other
possible error code the UV can return.
Signed-off-by: Steffen Eiden <[email protected]>
---
arch/s390/include/asm/uv.h | 3 ++
arch/s390/include/uapi/asm/uvdevice.h | 4 ++
drivers/s390/char/uvdevice.c | 55 +++++++++++++++++++++++++++
3 files changed, 62 insertions(+)
diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h
index a7dff64e1e24..1e4f0f6d4923 100644
--- a/arch/s390/include/asm/uv.h
+++ b/arch/s390/include/asm/uv.h
@@ -59,6 +59,7 @@
#define UVC_CMD_REMOVE_SHARED_ACCESS 0x1001
#define UVC_CMD_RETR_ATTEST 0x1020
#define UVC_CMD_ADD_SECRET 0x1031
+#define UVC_CMD_LIST_SECRETS 0x1033
/* Bits in installed uv calls */
enum uv_cmds_inst {
@@ -90,6 +91,7 @@ enum uv_cmds_inst {
BIT_UVC_CMD_DUMP_COMPLETE = 27,
BIT_UVC_CMD_RETR_ATTEST = 28,
BIT_UVC_CMD_ADD_SECRET = 29,
+ BIT_UVC_CMD_LIST_SECRETS = 30,
};
enum uv_feat_ind {
@@ -298,6 +300,7 @@ struct uv_cb_dump_complete {
* A common call for pv guests that contains a single address
* Examples:
* Add Secret
+ * List Secrets
*/
struct uv_cb_guest_addr {
struct uv_cb_header header;
diff --git a/arch/s390/include/uapi/asm/uvdevice.h b/arch/s390/include/uapi/asm/uvdevice.h
index e919b802213c..cf12d6b8d8d8 100644
--- a/arch/s390/include/uapi/asm/uvdevice.h
+++ b/arch/s390/include/uapi/asm/uvdevice.h
@@ -73,6 +73,7 @@ struct uvio_uvdev_info {
#define UVIO_ATT_MEASUREMENT_MAX_LEN 0x8000
#define UVIO_ATT_ADDITIONAL_MAX_LEN 0x8000
#define UVIO_ADD_SECRET_MAX_LEN 0x100000
+#define UVIO_LIST_SECRETS_LEN 0x1000
#define UVIO_DEVICE_NAME "uv"
#define UVIO_TYPE_UVC 'u'
@@ -81,6 +82,7 @@ enum UVIO_IOCTL_NR {
UVIO_IOCTL_UVDEV_INFO_NR = 0x00,
UVIO_IOCTL_ATT_NR,
UVIO_IOCTL_ADD_SECRET_NR,
+ UVIO_IOCTL_LIST_SECRETS_NR,
/* must be the last entry */
UVIO_IOCTL_NUM_IOCTLS
};
@@ -89,10 +91,12 @@ enum UVIO_IOCTL_NR {
#define UVIO_IOCTL_UVDEV_INFO UVIO_IOCTL(UVIO_IOCTL_UVDEV_INFO_NR)
#define UVIO_IOCTL_ATT UVIO_IOCTL(UVIO_IOCTL_ATT_NR)
#define UVIO_IOCTL_ADD_SECRET UVIO_IOCTL(UVIO_IOCTL_ADD_SECRET_NR)
+#define UVIO_IOCTL_LIST_SECRETS UVIO_IOCTL(UVIO_IOCTL_LIST_SECRETS_NR)
#define UVIO_SUPP_CALL(nr) (1ULL << (nr))
#define UVIO_SUPP_UDEV_INFO UVIO_SUPP_CALL(UVIO_IOCTL_UDEV_INFO_NR)
#define UVIO_SUPP_ATT UVIO_SUPP_CALL(UVIO_IOCTL_ATT_NR)
#define UVIO_SUPP_ADD_SECRET UVIO_SUPP_CALL(UVIO_IOCTL_ADD_SECRET_NR)
+#define UVIO_SUPP_LIST_SECRETS UVIO_SUPP_CALL(UVIO_IOCTL_LIST_SECRETS_NR)
#endif /* __S390_ASM_UVDEVICE_H */
diff --git a/drivers/s390/char/uvdevice.c b/drivers/s390/char/uvdevice.c
index ba3e60dc4ba8..5fcd719047ab 100644
--- a/drivers/s390/char/uvdevice.c
+++ b/drivers/s390/char/uvdevice.c
@@ -38,6 +38,7 @@ static const u64 ioctl_nr_to_uvc_bit[] __initconst = {
[UVIO_IOCTL_UVDEV_INFO_NR] = -1UL,
[UVIO_IOCTL_ATT_NR] = BIT_UVC_CMD_RETR_ATTEST,
[UVIO_IOCTL_ADD_SECRET_NR] = BIT_UVC_CMD_ADD_SECRET,
+ [UVIO_IOCTL_LIST_SECRETS_NR] = BIT_UVC_CMD_LIST_SECRETS,
};
static_assert(ARRAY_SIZE(ioctl_nr_to_uvc_bit) == UVIO_IOCTL_NUM_IOCTLS);
@@ -286,6 +287,57 @@ static int uvio_add_secret(struct uvio_ioctl_cb *uv_ioctl)
return ret;
}
+/** uvio_list_secrets() - perform a List Secret UVC
+ *
+ * @uv_ioctl: ioctl control block
+ *
+ * uvio_list_secrets() performs the List Secret Ultravisor Call.
+ * It verifies that the given userspace argument address is valid and its size
+ * is sane. Every other check is made by the Ultravisor (UV) and won't result
+ * in a negative return value. It builds the request, performs the UV-call,
+ * and copies the result to userspace.
+ *
+ * The argument specifies the location for the result of the UV-Call.
+ *
+ * If the List Secrets 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_list_secrets(struct uvio_ioctl_cb *uv_ioctl)
+{
+ void __user *user_buf_arg = (void __user *)uv_ioctl->argument_addr;
+ struct uv_cb_guest_addr uvcb = {
+ .header.len = sizeof(uvcb),
+ .header.cmd = UVC_CMD_LIST_SECRETS,
+ };
+ void *secrets = NULL;
+ int ret;
+
+ if (uv_ioctl->argument_len != UVIO_LIST_SECRETS_LEN)
+ return -EINVAL;
+
+ secrets = kvzalloc(uv_ioctl->argument_len, GFP_KERNEL);
+ if (!secrets)
+ return -ENOMEM;
+
+ uvcb.addr = (u64)secrets;
+ uv_call_sched(0, (u64)&uvcb);
+ uv_ioctl->uv_rc = uvcb.header.rc;
+ uv_ioctl->uv_rrc = uvcb.header.rrc;
+
+ if (copy_to_user(user_buf_arg, secrets, uv_ioctl->argument_len))
+ ret = -EFAULT;
+ else
+ ret = 0;
+
+ kvfree(secrets);
+ return ret;
+}
+
static int uvio_copy_and_check_ioctl(struct uvio_ioctl_cb *ioctl, void __user *argp,
unsigned long cmd)
{
@@ -333,6 +385,9 @@ static long uvio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case UVIO_IOCTL_ADD_SECRET_NR:
ret = uvio_add_secret(&uv_ioctl);
break;
+ case UVIO_IOCTL_LIST_SECRETS_NR:
+ ret = uvio_list_secrets(&uv_ioctl);
+ break;
default:
ret = -ENOIOCTLCMD;
break;
--
2.40.1
On Fri, May 12, 2023 at 11:31:53AM +0200, Steffen Eiden wrote:
> Update the query struct such that secret-UVC related
> information can be parsed.
> Add sysfs files for these new values.
>
> Signed-off-by: Steffen Eiden <[email protected]>
> ---
> arch/s390/boot/uv.c | 4 ++++
> arch/s390/include/asm/uv.h | 11 ++++++++++-
> arch/s390/kernel/uv.c | 40 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 54 insertions(+), 1 deletion(-)
...
> +static ssize_t uv_query_supp_add_secret_req_ver(struct kobject *kobj,
> + struct kobj_attribute *attr, char *page)
> +{
> + return scnprintf(page, PAGE_SIZE, "%lx\n", uv_info.supp_add_secret_req_ver);
> +}
FWIW, another minor thing: all of these should be sysfs_emit() instead.
On Fri, May 12, 2023 at 11:31:49AM +0200, Steffen Eiden wrote:
> Add an IOCTL that allows userspace to find out which IOCTLs the uvdevice
> supports without trial and error.
>
> Explicitly expose the IOCTL nr for the request types.
>
> Signed-off-by: Steffen Eiden <[email protected]>
> ---
> arch/s390/include/uapi/asm/uvdevice.h | 45 +++++++++++++++-
> drivers/s390/char/uvdevice.c | 77 ++++++++++++++++++++++++---
> 2 files changed, 114 insertions(+), 8 deletions(-)
...
> +static void __init set_supp_uv_cmds(struct uvio_uvdev_info *info)
> +{
> + int i;
> +
> + for (i = 0; i < UVIO_IOCTL_NUM_IOCTLS; i++) {
> + if (ioctl_nr_to_uvc_bit[i] == -1UL)
> + continue;
> + if (!test_bit_inv(ioctl_nr_to_uvc_bit[i], uv_info.inst_calls_list))
> + continue;
> + set_bit(i, (unsigned long *)&info->supp_uv_cmds);
Just a minor thing: please use
__set_bit(i, (unsigned long *)&info->supp_uv_cmds);
set_bit() is the atomic variant which may even generate a compare and swap
loop (dependent on config options) to set bits, which is not what is needed
here. Didn't see that in previous version.
So if atomicity is not needed, please use __set_bit() instead.
On 5/12/23 14:53, Heiko Carstens wrote:
> On Fri, May 12, 2023 at 11:31:53AM +0200, Steffen Eiden wrote:
>> Update the query struct such that secret-UVC related
>> information can be parsed.
>> Add sysfs files for these new values.
>>
>> Signed-off-by: Steffen Eiden <[email protected]>
>> ---
>> arch/s390/boot/uv.c | 4 ++++
>> arch/s390/include/asm/uv.h | 11 ++++++++++-
>> arch/s390/kernel/uv.c | 40 ++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 54 insertions(+), 1 deletion(-)
> ...
>> +static ssize_t uv_query_supp_add_secret_req_ver(struct kobject *kobj,
>> + struct kobj_attribute *attr, char *page)
>> +{
>> + return scnprintf(page, PAGE_SIZE, "%lx\n", uv_info.supp_add_secret_req_ver);
>> +}
>
> FWIW, another minor thing: all of these should be sysfs_emit() instead.
Do you want us to move all of them to sysfs_emit() eventually or should
we keep the scnprintfs() which are already in uv.c ?
On Fri, May 12, 2023 at 03:27:52PM +0200, Janosch Frank wrote:
> On 5/12/23 14:53, Heiko Carstens wrote:
> > On Fri, May 12, 2023 at 11:31:53AM +0200, Steffen Eiden wrote:
> > > Update the query struct such that secret-UVC related
> > > information can be parsed.
> > > Add sysfs files for these new values.
> > >
> > > Signed-off-by: Steffen Eiden <[email protected]>
> > > ---
> > > arch/s390/boot/uv.c | 4 ++++
> > > arch/s390/include/asm/uv.h | 11 ++++++++++-
> > > arch/s390/kernel/uv.c | 40 ++++++++++++++++++++++++++++++++++++++
> > > 3 files changed, 54 insertions(+), 1 deletion(-)
> > ...
> > > +static ssize_t uv_query_supp_add_secret_req_ver(struct kobject *kobj,
> > > + struct kobj_attribute *attr, char *page)
> > > +{
> > > + return scnprintf(page, PAGE_SIZE, "%lx\n", uv_info.supp_add_secret_req_ver);
> > > +}
> >
> > FWIW, another minor thing: all of these should be sysfs_emit() instead.
>
> Do you want us to move all of them to sysfs_emit() eventually or should we
> keep the scnprintfs() which are already in uv.c ?
I'd convert the existing ones as well.
Hi Steffen,
kernel test robot noticed the following build errors:
[auto build test ERROR on kvms390/next]
[also build test ERROR on s390/features mst-vhost/linux-next linus/master v6.4-rc1 next-20230512]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Steffen-Eiden/s390-uvdevice-Add-info-IOCTL/20230512-174226
base: https://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux.git next
patch link: https://lore.kernel.org/r/20230512093153.206378-2-seiden%40linux.ibm.com
patch subject: [PATCH 1/5] s390/uvdevice: Add info IOCTL
config: s390-randconfig-s043-20230509 (https://download.01.org/0day-ci/archive/20230513/[email protected]/config)
compiler: s390-linux-gcc (GCC) 12.1.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.4-39-gce1a6720-dirty
# https://github.com/intel-lab-lkp/linux/commit/82f137c1686ef0317cd12c5737fa349f2582163c
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Steffen-Eiden/s390-uvdevice-Add-info-IOCTL/20230512-174226
git checkout 82f137c1686ef0317cd12c5737fa349f2582163c
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=s390 olddefconfig
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=s390 SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>, old ones prefixed by <<):
>> ERROR: modpost: "uv_info" [drivers/s390/char/uvdevice.ko] undefined!
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests
On Fri, 12 May 2023 11:31:53 +0200
Steffen Eiden <[email protected]> wrote:
> Update the query struct such that secret-UVC related
> information can be parsed.
> Add sysfs files for these new values.
>
> Signed-off-by: Steffen Eiden <[email protected]>
> ---
> arch/s390/boot/uv.c | 4 ++++
> arch/s390/include/asm/uv.h | 11 ++++++++++-
> arch/s390/kernel/uv.c | 40 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 54 insertions(+), 1 deletion(-)
>
> diff --git a/arch/s390/boot/uv.c b/arch/s390/boot/uv.c
> index 0a077c0a2056..323b5cae3cf1 100644
> --- a/arch/s390/boot/uv.c
> +++ b/arch/s390/boot/uv.c
> @@ -47,6 +47,10 @@ void uv_query_info(void)
> uv_info.conf_dump_finalize_len = uvcb.conf_dump_finalize_len;
> uv_info.supp_att_req_hdr_ver = uvcb.supp_att_req_hdr_ver;
> uv_info.supp_att_pflags = uvcb.supp_att_pflags;
> + uv_info.supp_add_secret_req_ver = uvcb.supp_add_secret_req_ver;
> + uv_info.supp_add_secret_pcf = uvcb.supp_add_secret_pcf;
> + uv_info.supp_secret_types = uvcb.supp_secret_types;
> + uv_info.max_secrets = uvcb.max_num_secrets;
> }
>
> #ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
> diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h
> index 6180ac8909d5..eb2e11d8123f 100644
> --- a/arch/s390/include/asm/uv.h
> +++ b/arch/s390/include/asm/uv.h
> @@ -135,7 +135,12 @@ struct uv_cb_qui {
> u64 reservedd8; /* 0x00d8 */
> u64 supp_att_req_hdr_ver; /* 0x00e0 */
> u64 supp_att_pflags; /* 0x00e8 */
> - u8 reservedf0[256 - 240]; /* 0x00f0 */
> + u64 reservedf0; /* 0x00f0 */
> + u64 supp_add_secret_req_ver; /* 0x00f8 */
> + u64 supp_add_secret_pcf; /* 0x0100 */
> + u64 supp_secret_types; /* 0x0180 */
> + u16 max_num_secrets; /* 0x0110 */
> + u8 reserved112[288 - 274]; /* 0x0112 */
I think it would be more readable and maintainable if you put the
offsets in hex (i.e. reserved112[0x120 - 0x112])
otherwise, something like this:
u16 max_num_secrets;
u16 reserved112[3]
u64 reserved118;
(I know it was not in hex before, it was not necessarily nice as it was)
> } __packed __aligned(8);
>
> /* Initialize Ultravisor */
> @@ -384,6 +389,10 @@ struct uv_info {
> unsigned long conf_dump_finalize_len;
> unsigned long supp_att_req_hdr_ver;
> unsigned long supp_att_pflags;
> + unsigned long supp_add_secret_req_ver;
> + unsigned long supp_add_secret_pcf;
> + unsigned long supp_secret_types;
> + unsigned short max_secrets;
> };
>
> extern struct uv_info uv_info;
> diff --git a/arch/s390/kernel/uv.c b/arch/s390/kernel/uv.c
> index 9f18a4af9c13..381444511bb7 100644
> --- a/arch/s390/kernel/uv.c
> +++ b/arch/s390/kernel/uv.c
> @@ -587,6 +587,42 @@ static ssize_t uv_query_supp_att_pflags(struct kobject *kobj,
> static struct kobj_attribute uv_query_supp_att_pflags_attr =
> __ATTR(supp_att_pflags, 0444, uv_query_supp_att_pflags, NULL);
>
> +static ssize_t uv_query_supp_add_secret_req_ver(struct kobject *kobj,
> + struct kobj_attribute *attr, char *page)
> +{
> + return scnprintf(page, PAGE_SIZE, "%lx\n", uv_info.supp_add_secret_req_ver);
> +}
> +
> +static struct kobj_attribute uv_query_supp_add_secret_req_ver_attr =
> + __ATTR(supp_add_secret_req_ver, 0444, uv_query_supp_add_secret_req_ver, NULL);
> +
> +static ssize_t uv_query_supp_add_secret_pcf(struct kobject *kobj,
> + struct kobj_attribute *attr, char *page)
> +{
> + return scnprintf(page, PAGE_SIZE, "%lx\n", uv_info.supp_add_secret_pcf);
> +}
> +
> +static struct kobj_attribute uv_query_supp_add_secret_pcf_attr =
> + __ATTR(supp_add_secret_pcf, 0444, uv_query_supp_add_secret_pcf, NULL);
> +
> +static ssize_t uv_query_supp_secret_types(struct kobject *kobj,
> + struct kobj_attribute *attr, char *page)
> +{
> + return scnprintf(page, PAGE_SIZE, "%lx\n", uv_info.supp_secret_types);
> +}
> +
> +static struct kobj_attribute uv_query_supp_secret_types_attr =
> + __ATTR(supp_secret_types, 0444, uv_query_supp_secret_types, NULL);
> +
> +static ssize_t uv_query_max_secrets(struct kobject *kobj,
> + struct kobj_attribute *attr, char *page)
> +{
> + return scnprintf(page, PAGE_SIZE, "%d\n", uv_info.max_secrets);
> +}
> +
> +static struct kobj_attribute uv_query_max_num_secrets_attr =
> + __ATTR(max_secrets, 0444, uv_query_max_secrets, NULL);
> +
> static struct attribute *uv_query_attrs[] = {
> &uv_query_facilities_attr.attr,
> &uv_query_feature_indications_attr.attr,
> @@ -600,6 +636,10 @@ static struct attribute *uv_query_attrs[] = {
> &uv_query_dump_cpu_len_attr.attr,
> &uv_query_supp_att_req_hdr_ver_attr.attr,
> &uv_query_supp_att_pflags_attr.attr,
> + &uv_query_supp_add_secret_req_ver_attr.attr,
> + &uv_query_supp_add_secret_pcf_attr.attr,
> + &uv_query_supp_secret_types_attr.attr,
> + &uv_query_max_num_secrets_attr.attr,
> NULL,
> };
>
Thanks for your review.
On 5/17/23 18:03, Claudio Imbrenda wrote:
>> static int uvio_list_secrets(struct uvio_ioctl_cb *uv_ioctl)
>> +{
>> + void __user *user_buf_arg = (void __user *)uv_ioctl->argument_addr;
>> + struct uv_cb_guest_addr uvcb = {
>> + .header.len = sizeof(uvcb),
>> + .header.cmd = UVC_CMD_LIST_SECRETS,
>> + };
>> + void *secrets = NULL;
>> + int ret;
>> +
>> + if (uv_ioctl->argument_len != UVIO_LIST_SECRETS_LEN)
>> + return -EINVAL;
>> +
>> + secrets = kvzalloc(uv_ioctl->argument_len, GFP_KERNEL);
>> + if (!secrets)
>> + return -ENOMEM;
>> +
>> + uvcb.addr = (u64)secrets;
> I think you need virt_to_phys()
The UV expects a virtual address here.
>
>> + uv_call_sched(0, (u64)&uvcb);
>> + uv_ioctl->uv_rc = uvcb.header.rc;
>> + uv_ioctl->uv_rrc = uvcb.header.rrc;
>> +
>> + if (copy_to_user(user_buf_arg, secrets, uv_ioctl->argument_len))
>> + ret = -EFAULT;
>> + else
>> + ret = 0;
>> +
>> + kvfree(secrets);
>> + return ret;
>> +}
Steffen
On Fri, 12 May 2023 11:31:51 +0200
Steffen Eiden <[email protected]> wrote:
> Userspace can call the List Secrets Ultravisor Call
> using IOCTLs on the uvdevice.
> During the handling of the new IOCTL nr the uvdevice will do some sanity
> checks first. Then, perform the Ultravisor command, and copy the answer
> to userspace.
> If the List Secrets 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. This is also true for any other
> possible error code the UV can return.
>
> Signed-off-by: Steffen Eiden <[email protected]>
> ---
[...]
> diff --git a/drivers/s390/char/uvdevice.c b/drivers/s390/char/uvdevice.c
> index ba3e60dc4ba8..5fcd719047ab 100644
> --- a/drivers/s390/char/uvdevice.c
> +++ b/drivers/s390/char/uvdevice.c
> @@ -38,6 +38,7 @@ static const u64 ioctl_nr_to_uvc_bit[] __initconst = {
> [UVIO_IOCTL_UVDEV_INFO_NR] = -1UL,
> [UVIO_IOCTL_ATT_NR] = BIT_UVC_CMD_RETR_ATTEST,
> [UVIO_IOCTL_ADD_SECRET_NR] = BIT_UVC_CMD_ADD_SECRET,
> + [UVIO_IOCTL_LIST_SECRETS_NR] = BIT_UVC_CMD_LIST_SECRETS,
> };
>
> static_assert(ARRAY_SIZE(ioctl_nr_to_uvc_bit) == UVIO_IOCTL_NUM_IOCTLS);
> @@ -286,6 +287,57 @@ static int uvio_add_secret(struct uvio_ioctl_cb *uv_ioctl)
> return ret;
> }
>
> +/** uvio_list_secrets() - perform a List Secret UVC
> + *
> + * @uv_ioctl: ioctl control block
> + *
> + * uvio_list_secrets() performs the List Secret Ultravisor Call.
> + * It verifies that the given userspace argument address is valid and its size
> + * is sane. Every other check is made by the Ultravisor (UV) and won't result
> + * in a negative return value. It builds the request, performs the UV-call,
> + * and copies the result to userspace.
> + *
> + * The argument specifies the location for the result of the UV-Call.
> + *
> + * If the List Secrets 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_list_secrets(struct uvio_ioctl_cb *uv_ioctl)
> +{
> + void __user *user_buf_arg = (void __user *)uv_ioctl->argument_addr;
> + struct uv_cb_guest_addr uvcb = {
> + .header.len = sizeof(uvcb),
> + .header.cmd = UVC_CMD_LIST_SECRETS,
> + };
> + void *secrets = NULL;
> + int ret;
> +
> + if (uv_ioctl->argument_len != UVIO_LIST_SECRETS_LEN)
> + return -EINVAL;
> +
> + secrets = kvzalloc(uv_ioctl->argument_len, GFP_KERNEL);
> + if (!secrets)
> + return -ENOMEM;
> +
> + uvcb.addr = (u64)secrets;
I think you need virt_to_phys()
> + uv_call_sched(0, (u64)&uvcb);
> + uv_ioctl->uv_rc = uvcb.header.rc;
> + uv_ioctl->uv_rrc = uvcb.header.rrc;
> +
> + if (copy_to_user(user_buf_arg, secrets, uv_ioctl->argument_len))
> + ret = -EFAULT;
> + else
> + ret = 0;
> +
> + kvfree(secrets);
> + return ret;
> +}
> +
> static int uvio_copy_and_check_ioctl(struct uvio_ioctl_cb *ioctl, void __user *argp,
> unsigned long cmd)
> {
> @@ -333,6 +385,9 @@ static long uvio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> case UVIO_IOCTL_ADD_SECRET_NR:
> ret = uvio_add_secret(&uv_ioctl);
> break;
> + case UVIO_IOCTL_LIST_SECRETS_NR:
> + ret = uvio_list_secrets(&uv_ioctl);
> + break;
> default:
> ret = -ENOIOCTLCMD;
> break;