Skip to content

Commit 4689752

Browse files
steffen-eidenfrankjaa
authored andcommitted
drivers/s390/char: Add Ultravisor io device
This patch adds a new miscdevice to expose some Ultravisor functions to userspace. Userspace can send IOCTLs 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 can 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. Signed-off-by: Steffen Eiden <seiden@linux.ibm.com> Reviewed-by: Janosch Frank <frankja@linux.ibm.com> Reviewed-by: Claudio Imbrenda <imbrenda@linux.ibm.com> Link: https://lore.kernel.org/kvm/20220516113335.338212-1-seiden@linux.ibm.com/ Message-Id: <20220516113335.338212-1-seiden@linux.ibm.com> Signed-off-by: Janosch Frank <frankja@linux.ibm.com> (whitespace and tristate fixes, pick)
1 parent ce522ba commit 4689752

6 files changed

Lines changed: 343 additions & 1 deletion

File tree

MAINTAINERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10779,9 +10779,11 @@ F: Documentation/virt/kvm/s390*
1077910779
F: arch/s390/include/asm/gmap.h
1078010780
F: arch/s390/include/asm/kvm*
1078110781
F: arch/s390/include/uapi/asm/kvm*
10782+
F: arch/s390/include/uapi/asm/uvdevice.h
1078210783
F: arch/s390/kernel/uv.c
1078310784
F: arch/s390/kvm/
1078410785
F: arch/s390/mm/gmap.c
10786+
F: drivers/s390/char/uvdevice.c
1078510787
F: tools/testing/selftests/kvm/*/s390x/
1078610788
F: tools/testing/selftests/kvm/s390x/
1078710789

arch/s390/include/asm/uv.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/*
33
* Ultravisor Interfaces
44
*
5-
* Copyright IBM Corp. 2019
5+
* Copyright IBM Corp. 2019, 2022
66
*
77
* Author(s):
88
* Vasily Gorbik <gor@linux.ibm.com>
@@ -52,6 +52,7 @@
5252
#define UVC_CMD_UNPIN_PAGE_SHARED 0x0342
5353
#define UVC_CMD_SET_SHARED_ACCESS 0x1000
5454
#define UVC_CMD_REMOVE_SHARED_ACCESS 0x1001
55+
#define UVC_CMD_RETR_ATTEST 0x1020
5556

5657
/* Bits in installed uv calls */
5758
enum uv_cmds_inst {
@@ -76,6 +77,7 @@ enum uv_cmds_inst {
7677
BIT_UVC_CMD_UNSHARE_ALL = 20,
7778
BIT_UVC_CMD_PIN_PAGE_SHARED = 21,
7879
BIT_UVC_CMD_UNPIN_PAGE_SHARED = 22,
80+
BIT_UVC_CMD_RETR_ATTEST = 28,
7981
};
8082

8183
enum uv_feat_ind {
@@ -219,6 +221,25 @@ struct uv_cb_share {
219221
u64 reserved28;
220222
} __packed __aligned(8);
221223

224+
/* Retrieve Attestation Measurement */
225+
struct uv_cb_attest {
226+
struct uv_cb_header header; /* 0x0000 */
227+
u64 reserved08[2]; /* 0x0008 */
228+
u64 arcb_addr; /* 0x0018 */
229+
u64 cont_token; /* 0x0020 */
230+
u8 reserved28[6]; /* 0x0028 */
231+
u16 user_data_len; /* 0x002e */
232+
u8 user_data[256]; /* 0x0030 */
233+
u32 reserved130[3]; /* 0x0130 */
234+
u32 meas_len; /* 0x013c */
235+
u64 meas_addr; /* 0x0140 */
236+
u8 config_uid[16]; /* 0x0148 */
237+
u32 reserved158; /* 0x0158 */
238+
u32 add_data_len; /* 0x015c */
239+
u64 add_data_addr; /* 0x0160 */
240+
u64 reserved168[4]; /* 0x0168 */
241+
} __packed __aligned(8);
242+
222243
static inline int __uv_call(unsigned long r1, unsigned long r2)
223244
{
224245
int cc;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2+
/*
3+
* Copyright IBM Corp. 2022
4+
* Author(s): Steffen Eiden <seiden@linux.ibm.com>
5+
*/
6+
#ifndef __S390_ASM_UVDEVICE_H
7+
#define __S390_ASM_UVDEVICE_H
8+
9+
#include <linux/types.h>
10+
11+
struct uvio_ioctl_cb {
12+
__u32 flags;
13+
__u16 uv_rc; /* UV header rc value */
14+
__u16 uv_rrc; /* UV header rrc value */
15+
__u64 argument_addr; /* Userspace address of uvio argument */
16+
__u32 argument_len;
17+
__u8 reserved14[0x40 - 0x14]; /* must be zero */
18+
};
19+
20+
#define UVIO_ATT_USER_DATA_LEN 0x100
21+
#define UVIO_ATT_UID_LEN 0x10
22+
struct uvio_attest {
23+
__u64 arcb_addr; /* 0x0000 */
24+
__u64 meas_addr; /* 0x0008 */
25+
__u64 add_data_addr; /* 0x0010 */
26+
__u8 user_data[UVIO_ATT_USER_DATA_LEN]; /* 0x0018 */
27+
__u8 config_uid[UVIO_ATT_UID_LEN]; /* 0x0118 */
28+
__u32 arcb_len; /* 0x0128 */
29+
__u32 meas_len; /* 0x012c */
30+
__u32 add_data_len; /* 0x0130 */
31+
__u16 user_data_len; /* 0x0134 */
32+
__u16 reserved136; /* 0x0136 */
33+
};
34+
35+
/*
36+
* The following max values define an upper length for the IOCTL in/out buffers.
37+
* However, they do not represent the maximum the Ultravisor allows which is
38+
* often way smaller. By allowing larger buffer sizes we hopefully do not need
39+
* to update the code with every machine update. It is therefore possible for
40+
* userspace to request more memory than actually used by kernel/UV.
41+
*/
42+
#define UVIO_ATT_ARCB_MAX_LEN 0x100000
43+
#define UVIO_ATT_MEASUREMENT_MAX_LEN 0x8000
44+
#define UVIO_ATT_ADDITIONAL_MAX_LEN 0x8000
45+
46+
#define UVIO_DEVICE_NAME "uv"
47+
#define UVIO_TYPE_UVC 'u'
48+
49+
#define UVIO_IOCTL_ATT _IOWR(UVIO_TYPE_UVC, 0x01, struct uvio_ioctl_cb)
50+
51+
#endif /* __S390_ASM_UVDEVICE_H */

drivers/s390/char/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ config SCLP_OFB
100100
This option enables the Open-for-Business interface to the s390
101101
Service Element.
102102

103+
config S390_UV_UAPI
104+
def_tristate m
105+
prompt "Ultravisor userspace API"
106+
help
107+
Selecting exposes parts of the UV interface to userspace
108+
by providing a misc character device at /dev/uv.
109+
Using IOCTLs one can interact with the UV.
110+
The device is only available if the Ultravisor
111+
Facility (158) is present.
112+
103113
config S390_TAPE
104114
def_tristate m
105115
prompt "S/390 tape device support"

drivers/s390/char/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ obj-$(CONFIG_MONREADER) += monreader.o
4848
obj-$(CONFIG_MONWRITER) += monwriter.o
4949
obj-$(CONFIG_S390_VMUR) += vmur.o
5050
obj-$(CONFIG_CRASH_DUMP) += sclp_sdias.o zcore.o
51+
obj-$(CONFIG_S390_UV_UAPI) += uvdevice.o
5152

5253
hmcdrv-objs := hmcdrv_mod.o hmcdrv_dev.o hmcdrv_ftp.o hmcdrv_cache.o diag_ftp.o sclp_ftp.o
5354
obj-$(CONFIG_HMC_DRV) += hmcdrv.o

drivers/s390/char/uvdevice.c

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright IBM Corp. 2022
4+
* Author(s): Steffen Eiden <seiden@linux.ibm.com>
5+
*
6+
* This file provides a Linux misc device to give userspace access to some
7+
* Ultravisor (UV) functions. The device only accepts IOCTLs and will only
8+
* be present if the Ultravisor facility (158) is present.
9+
*
10+
* When userspace sends a valid IOCTL uvdevice will copy the input data to
11+
* kernel space, do some basic validity checks to avoid kernel/system
12+
* corruption. Any other check that the Ultravisor does will not be done by
13+
* the uvdevice to keep changes minimal when adding new functionalities
14+
* to existing UV-calls.
15+
* After the checks uvdevice builds a corresponding
16+
* Ultravisor Call Control Block, and sends the request to the Ultravisor.
17+
* Then, it copies the response, including the return codes, back to userspace.
18+
* It is the responsibility of the userspace to check for any error issued
19+
* by UV and to interpret the UV response. The uvdevice acts as a communication
20+
* channel for userspace to the Ultravisor.
21+
*/
22+
23+
#include <linux/module.h>
24+
#include <linux/kernel.h>
25+
#include <linux/miscdevice.h>
26+
#include <linux/types.h>
27+
#include <linux/stddef.h>
28+
#include <linux/vmalloc.h>
29+
#include <linux/slab.h>
30+
31+
#include <asm/uvdevice.h>
32+
#include <asm/uv.h>
33+
34+
static int uvio_build_uvcb_attest(struct uv_cb_attest *uvcb_attest, u8 *arcb,
35+
u8 *meas, u8 *add_data, struct uvio_attest *uvio_attest)
36+
{
37+
void __user *user_buf_arcb = (void __user *)uvio_attest->arcb_addr;
38+
39+
if (copy_from_user(arcb, user_buf_arcb, uvio_attest->arcb_len))
40+
return -EFAULT;
41+
42+
uvcb_attest->header.len = sizeof(*uvcb_attest);
43+
uvcb_attest->header.cmd = UVC_CMD_RETR_ATTEST;
44+
uvcb_attest->arcb_addr = (u64)arcb;
45+
uvcb_attest->cont_token = 0;
46+
uvcb_attest->user_data_len = uvio_attest->user_data_len;
47+
memcpy(uvcb_attest->user_data, uvio_attest->user_data, sizeof(uvcb_attest->user_data));
48+
uvcb_attest->meas_len = uvio_attest->meas_len;
49+
uvcb_attest->meas_addr = (u64)meas;
50+
uvcb_attest->add_data_len = uvio_attest->add_data_len;
51+
uvcb_attest->add_data_addr = (u64)add_data;
52+
53+
return 0;
54+
}
55+
56+
static int uvio_copy_attest_result_to_user(struct uv_cb_attest *uvcb_attest,
57+
struct uvio_ioctl_cb *uv_ioctl,
58+
u8 *measurement, u8 *add_data,
59+
struct uvio_attest *uvio_attest)
60+
{
61+
struct uvio_attest __user *user_uvio_attest = (void __user *)uv_ioctl->argument_addr;
62+
void __user *user_buf_add = (void __user *)uvio_attest->add_data_addr;
63+
void __user *user_buf_meas = (void __user *)uvio_attest->meas_addr;
64+
void __user *user_buf_uid = &user_uvio_attest->config_uid;
65+
66+
if (copy_to_user(user_buf_meas, measurement, uvio_attest->meas_len))
67+
return -EFAULT;
68+
if (add_data && copy_to_user(user_buf_add, add_data, uvio_attest->add_data_len))
69+
return -EFAULT;
70+
if (copy_to_user(user_buf_uid, uvcb_attest->config_uid, sizeof(uvcb_attest->config_uid)))
71+
return -EFAULT;
72+
return 0;
73+
}
74+
75+
static int get_uvio_attest(struct uvio_ioctl_cb *uv_ioctl, struct uvio_attest *uvio_attest)
76+
{
77+
u8 __user *user_arg_buf = (u8 __user *)uv_ioctl->argument_addr;
78+
79+
if (copy_from_user(uvio_attest, user_arg_buf, sizeof(*uvio_attest)))
80+
return -EFAULT;
81+
82+
if (uvio_attest->arcb_len > UVIO_ATT_ARCB_MAX_LEN)
83+
return -EINVAL;
84+
if (uvio_attest->arcb_len == 0)
85+
return -EINVAL;
86+
if (uvio_attest->meas_len > UVIO_ATT_MEASUREMENT_MAX_LEN)
87+
return -EINVAL;
88+
if (uvio_attest->meas_len == 0)
89+
return -EINVAL;
90+
if (uvio_attest->add_data_len > UVIO_ATT_ADDITIONAL_MAX_LEN)
91+
return -EINVAL;
92+
if (uvio_attest->reserved136)
93+
return -EINVAL;
94+
return 0;
95+
}
96+
97+
/**
98+
* uvio_attestation() - Perform a Retrieve Attestation Measurement UVC.
99+
*
100+
* @uv_ioctl: ioctl control block
101+
*
102+
* uvio_attestation() does a Retrieve Attestation Measurement Ultravisor Call.
103+
* It verifies that the given userspace addresses are valid and request sizes
104+
* are sane. Every other check is made by the Ultravisor (UV) and won't result
105+
* in a negative return value. It copies the input to kernelspace, builds the
106+
* request, sends the UV-call, and copies the result to userspace.
107+
*
108+
* The Attestation Request has two input and two outputs.
109+
* ARCB and User Data are inputs for the UV generated by userspace.
110+
* Measurement and Additional Data are outputs for userspace generated by UV.
111+
*
112+
* The Attestation Request Control Block (ARCB) is a cryptographically verified
113+
* and secured request to UV and User Data is some plaintext data which is
114+
* going to be included in the Attestation Measurement calculation.
115+
*
116+
* Measurement is a cryptographic measurement of the callers properties,
117+
* optional data configured by the ARCB and the user data. If specified by the
118+
* ARCB, UV will add some Additional Data to the measurement calculation.
119+
* This Additional Data is then returned as well.
120+
*
121+
* If the Retrieve Attestation Measurement UV facility is not present,
122+
* UV will return invalid command rc. This won't be fenced in the driver
123+
* and does not result in a negative return value.
124+
*
125+
* Context: might sleep
126+
*
127+
* Return: 0 on success or a negative error code on error.
128+
*/
129+
static int uvio_attestation(struct uvio_ioctl_cb *uv_ioctl)
130+
{
131+
struct uv_cb_attest *uvcb_attest = NULL;
132+
struct uvio_attest *uvio_attest = NULL;
133+
u8 *measurement = NULL;
134+
u8 *add_data = NULL;
135+
u8 *arcb = NULL;
136+
int ret;
137+
138+
ret = -EINVAL;
139+
if (uv_ioctl->argument_len != sizeof(*uvio_attest))
140+
goto out;
141+
142+
ret = -ENOMEM;
143+
uvio_attest = kzalloc(sizeof(*uvio_attest), GFP_KERNEL);
144+
if (!uvio_attest)
145+
goto out;
146+
147+
ret = get_uvio_attest(uv_ioctl, uvio_attest);
148+
if (ret)
149+
goto out;
150+
151+
ret = -ENOMEM;
152+
arcb = kvzalloc(uvio_attest->arcb_len, GFP_KERNEL);
153+
measurement = kvzalloc(uvio_attest->meas_len, GFP_KERNEL);
154+
if (!arcb || !measurement)
155+
goto out;
156+
157+
if (uvio_attest->add_data_len) {
158+
add_data = kvzalloc(uvio_attest->add_data_len, GFP_KERNEL);
159+
if (!add_data)
160+
goto out;
161+
}
162+
163+
uvcb_attest = kzalloc(sizeof(*uvcb_attest), GFP_KERNEL);
164+
if (!uvcb_attest)
165+
goto out;
166+
167+
ret = uvio_build_uvcb_attest(uvcb_attest, arcb, measurement, add_data, uvio_attest);
168+
if (ret)
169+
goto out;
170+
171+
uv_call_sched(0, (u64)uvcb_attest);
172+
173+
uv_ioctl->uv_rc = uvcb_attest->header.rc;
174+
uv_ioctl->uv_rrc = uvcb_attest->header.rrc;
175+
176+
ret = uvio_copy_attest_result_to_user(uvcb_attest, uv_ioctl, measurement, add_data,
177+
uvio_attest);
178+
out:
179+
kvfree(arcb);
180+
kvfree(measurement);
181+
kvfree(add_data);
182+
kfree(uvio_attest);
183+
kfree(uvcb_attest);
184+
return ret;
185+
}
186+
187+
static int uvio_copy_and_check_ioctl(struct uvio_ioctl_cb *ioctl, void __user *argp)
188+
{
189+
if (copy_from_user(ioctl, argp, sizeof(*ioctl)))
190+
return -EFAULT;
191+
if (ioctl->flags != 0)
192+
return -EINVAL;
193+
if (memchr_inv(ioctl->reserved14, 0, sizeof(ioctl->reserved14)))
194+
return -EINVAL;
195+
196+
return 0;
197+
}
198+
199+
/*
200+
* IOCTL entry point for the Ultravisor device.
201+
*/
202+
static long uvio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
203+
{
204+
void __user *argp = (void __user *)arg;
205+
struct uvio_ioctl_cb uv_ioctl = { };
206+
long ret;
207+
208+
switch (cmd) {
209+
case UVIO_IOCTL_ATT:
210+
ret = uvio_copy_and_check_ioctl(&uv_ioctl, argp);
211+
if (ret)
212+
return ret;
213+
ret = uvio_attestation(&uv_ioctl);
214+
break;
215+
default:
216+
ret = -ENOIOCTLCMD;
217+
break;
218+
}
219+
if (ret)
220+
return ret;
221+
222+
if (copy_to_user(argp, &uv_ioctl, sizeof(uv_ioctl)))
223+
ret = -EFAULT;
224+
225+
return ret;
226+
}
227+
228+
static const struct file_operations uvio_dev_fops = {
229+
.owner = THIS_MODULE,
230+
.unlocked_ioctl = uvio_ioctl,
231+
.llseek = no_llseek,
232+
};
233+
234+
static struct miscdevice uvio_dev_miscdev = {
235+
.minor = MISC_DYNAMIC_MINOR,
236+
.name = UVIO_DEVICE_NAME,
237+
.fops = &uvio_dev_fops,
238+
};
239+
240+
static void __exit uvio_dev_exit(void)
241+
{
242+
misc_deregister(&uvio_dev_miscdev);
243+
}
244+
245+
static int __init uvio_dev_init(void)
246+
{
247+
if (!test_facility(158))
248+
return -ENXIO;
249+
return misc_register(&uvio_dev_miscdev);
250+
}
251+
252+
module_init(uvio_dev_init);
253+
module_exit(uvio_dev_exit);
254+
255+
MODULE_AUTHOR("IBM Corporation");
256+
MODULE_LICENSE("GPL");
257+
MODULE_DESCRIPTION("Ultravisor UAPI driver");

0 commit comments

Comments
 (0)