|
| 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