1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #define pr_fmt(fmt) "papr-sysparm: " fmt 4 5 #include <linux/anon_inodes.h> 6 #include <linux/bug.h> 7 #include <linux/file.h> 8 #include <linux/fs.h> 9 #include <linux/init.h> 10 #include <linux/kernel.h> 11 #include <linux/miscdevice.h> 12 #include <linux/printk.h> 13 #include <linux/slab.h> 14 #include <linux/uaccess.h> 15 #include <asm/machdep.h> 16 #include <asm/papr-sysparm.h> 17 #include <asm/rtas-work-area.h> 18 #include <asm/rtas.h> 19 20 struct papr_sysparm_buf *papr_sysparm_buf_alloc(void) 21 { 22 struct papr_sysparm_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 23 24 return buf; 25 } 26 27 void papr_sysparm_buf_free(struct papr_sysparm_buf *buf) 28 { 29 kfree(buf); 30 } 31 32 static size_t papr_sysparm_buf_get_length(const struct papr_sysparm_buf *buf) 33 { 34 return be16_to_cpu(buf->len); 35 } 36 37 static void papr_sysparm_buf_set_length(struct papr_sysparm_buf *buf, size_t length) 38 { 39 WARN_ONCE(length > sizeof(buf->val), 40 "bogus length %zu, clamping to safe value", length); 41 length = min(sizeof(buf->val), length); 42 buf->len = cpu_to_be16(length); 43 } 44 45 /* 46 * For use on buffers returned from ibm,get-system-parameter before 47 * returning them to callers. Ensures the encoded length of valid data 48 * cannot overrun buf->val[]. 49 */ 50 static void papr_sysparm_buf_clamp_length(struct papr_sysparm_buf *buf) 51 { 52 papr_sysparm_buf_set_length(buf, papr_sysparm_buf_get_length(buf)); 53 } 54 55 /* 56 * Perform some basic diligence on the system parameter buffer before 57 * submitting it to RTAS. 58 */ 59 static bool papr_sysparm_buf_can_submit(const struct papr_sysparm_buf *buf) 60 { 61 /* 62 * Firmware ought to reject buffer lengths that exceed the 63 * maximum specified in PAPR, but there's no reason for the 64 * kernel to allow them either. 65 */ 66 if (papr_sysparm_buf_get_length(buf) > sizeof(buf->val)) 67 return false; 68 69 return true; 70 } 71 72 /** 73 * papr_sysparm_get() - Retrieve the value of a PAPR system parameter. 74 * @param: PAPR system parameter token as described in 75 * 7.3.16 "System Parameters Option". 76 * @buf: A &struct papr_sysparm_buf as returned from papr_sysparm_buf_alloc(). 77 * 78 * Place the result of querying the specified parameter, if available, 79 * in @buf. The result includes a be16 length header followed by the 80 * value, which may be a string or binary data. See &struct papr_sysparm_buf. 81 * 82 * Since there is at least one parameter (60, OS Service Entitlement 83 * Status) where the results depend on the incoming contents of the 84 * work area, the caller-supplied buffer is copied unmodified into the 85 * work area before calling ibm,get-system-parameter. 86 * 87 * A defined parameter may not be implemented on a given system, and 88 * some implemented parameters may not be available to all partitions 89 * on a system. A parameter's disposition may change at any time due 90 * to system configuration changes or partition migration. 91 * 92 * Context: This function may sleep. 93 * 94 * Return: 0 on success, -errno otherwise. @buf is unmodified on error. 95 */ 96 int papr_sysparm_get(papr_sysparm_t param, struct papr_sysparm_buf *buf) 97 { 98 const s32 token = rtas_function_token(RTAS_FN_IBM_GET_SYSTEM_PARAMETER); 99 struct rtas_work_area *work_area; 100 s32 fwrc; 101 int ret; 102 103 might_sleep(); 104 105 if (WARN_ON(!buf)) 106 return -EFAULT; 107 108 if (token == RTAS_UNKNOWN_SERVICE) 109 return -ENOENT; 110 111 if (!papr_sysparm_buf_can_submit(buf)) 112 return -EINVAL; 113 114 work_area = rtas_work_area_alloc(sizeof(*buf)); 115 116 memcpy(rtas_work_area_raw_buf(work_area), buf, sizeof(*buf)); 117 118 do { 119 fwrc = rtas_call(token, 3, 1, NULL, param.token, 120 rtas_work_area_phys(work_area), 121 rtas_work_area_size(work_area)); 122 } while (rtas_busy_delay(fwrc)); 123 124 switch (fwrc) { 125 case 0: 126 ret = 0; 127 memcpy(buf, rtas_work_area_raw_buf(work_area), sizeof(*buf)); 128 papr_sysparm_buf_clamp_length(buf); 129 break; 130 case -3: /* parameter not implemented */ 131 ret = -EOPNOTSUPP; 132 break; 133 case -9002: /* this partition not authorized to retrieve this parameter */ 134 ret = -EPERM; 135 break; 136 case -9999: /* "parameter error" e.g. the buffer is too small */ 137 ret = -EINVAL; 138 break; 139 default: 140 pr_err("unexpected ibm,get-system-parameter result %d\n", fwrc); 141 fallthrough; 142 case -1: /* Hardware/platform error */ 143 ret = -EIO; 144 break; 145 } 146 147 rtas_work_area_free(work_area); 148 149 return ret; 150 } 151 152 int papr_sysparm_set(papr_sysparm_t param, const struct papr_sysparm_buf *buf) 153 { 154 const s32 token = rtas_function_token(RTAS_FN_IBM_SET_SYSTEM_PARAMETER); 155 struct rtas_work_area *work_area; 156 s32 fwrc; 157 int ret; 158 159 might_sleep(); 160 161 if (WARN_ON(!buf)) 162 return -EFAULT; 163 164 if (token == RTAS_UNKNOWN_SERVICE) 165 return -ENOENT; 166 167 if (!papr_sysparm_buf_can_submit(buf)) 168 return -EINVAL; 169 170 work_area = rtas_work_area_alloc(sizeof(*buf)); 171 172 memcpy(rtas_work_area_raw_buf(work_area), buf, sizeof(*buf)); 173 174 do { 175 fwrc = rtas_call(token, 2, 1, NULL, param.token, 176 rtas_work_area_phys(work_area)); 177 } while (rtas_busy_delay(fwrc)); 178 179 switch (fwrc) { 180 case 0: 181 ret = 0; 182 break; 183 case -3: /* parameter not supported */ 184 ret = -EOPNOTSUPP; 185 break; 186 case -9002: /* this partition not authorized to modify this parameter */ 187 ret = -EPERM; 188 break; 189 case -9999: /* "parameter error" e.g. invalid input data */ 190 ret = -EINVAL; 191 break; 192 default: 193 pr_err("unexpected ibm,set-system-parameter result %d\n", fwrc); 194 fallthrough; 195 case -1: /* Hardware/platform error */ 196 ret = -EIO; 197 break; 198 } 199 200 rtas_work_area_free(work_area); 201 202 return ret; 203 } 204 205 static struct papr_sysparm_buf * 206 papr_sysparm_buf_from_user(const struct papr_sysparm_io_block __user *user_iob) 207 { 208 struct papr_sysparm_buf *kern_spbuf; 209 long err; 210 u16 len; 211 212 /* 213 * The length of valid data that userspace claims to be in 214 * user_iob->data[]. 215 */ 216 if (get_user(len, &user_iob->length)) 217 return ERR_PTR(-EFAULT); 218 219 static_assert(sizeof(user_iob->data) >= PAPR_SYSPARM_MAX_INPUT); 220 static_assert(sizeof(kern_spbuf->val) >= PAPR_SYSPARM_MAX_INPUT); 221 222 if (len > PAPR_SYSPARM_MAX_INPUT) 223 return ERR_PTR(-EINVAL); 224 225 kern_spbuf = papr_sysparm_buf_alloc(); 226 if (!kern_spbuf) 227 return ERR_PTR(-ENOMEM); 228 229 papr_sysparm_buf_set_length(kern_spbuf, len); 230 231 if (len > 0 && copy_from_user(kern_spbuf->val, user_iob->data, len)) { 232 err = -EFAULT; 233 goto free_sysparm_buf; 234 } 235 236 return kern_spbuf; 237 238 free_sysparm_buf: 239 papr_sysparm_buf_free(kern_spbuf); 240 return ERR_PTR(err); 241 } 242 243 static int papr_sysparm_buf_to_user(const struct papr_sysparm_buf *kern_spbuf, 244 struct papr_sysparm_io_block __user *user_iob) 245 { 246 u16 len_out = papr_sysparm_buf_get_length(kern_spbuf); 247 248 if (put_user(len_out, &user_iob->length)) 249 return -EFAULT; 250 251 static_assert(sizeof(user_iob->data) >= PAPR_SYSPARM_MAX_OUTPUT); 252 static_assert(sizeof(kern_spbuf->val) >= PAPR_SYSPARM_MAX_OUTPUT); 253 254 if (copy_to_user(user_iob->data, kern_spbuf->val, PAPR_SYSPARM_MAX_OUTPUT)) 255 return -EFAULT; 256 257 return 0; 258 } 259 260 static long papr_sysparm_ioctl_get(struct papr_sysparm_io_block __user *user_iob) 261 { 262 struct papr_sysparm_buf *kern_spbuf; 263 papr_sysparm_t param; 264 long ret; 265 266 if (get_user(param.token, &user_iob->parameter)) 267 return -EFAULT; 268 269 kern_spbuf = papr_sysparm_buf_from_user(user_iob); 270 if (IS_ERR(kern_spbuf)) 271 return PTR_ERR(kern_spbuf); 272 273 ret = papr_sysparm_get(param, kern_spbuf); 274 if (ret) 275 goto free_sysparm_buf; 276 277 ret = papr_sysparm_buf_to_user(kern_spbuf, user_iob); 278 if (ret) 279 goto free_sysparm_buf; 280 281 ret = 0; 282 283 free_sysparm_buf: 284 papr_sysparm_buf_free(kern_spbuf); 285 return ret; 286 } 287 288 289 static long papr_sysparm_ioctl_set(struct papr_sysparm_io_block __user *user_iob) 290 { 291 struct papr_sysparm_buf *kern_spbuf; 292 papr_sysparm_t param; 293 long ret; 294 295 if (get_user(param.token, &user_iob->parameter)) 296 return -EFAULT; 297 298 kern_spbuf = papr_sysparm_buf_from_user(user_iob); 299 if (IS_ERR(kern_spbuf)) 300 return PTR_ERR(kern_spbuf); 301 302 ret = papr_sysparm_set(param, kern_spbuf); 303 if (ret) 304 goto free_sysparm_buf; 305 306 ret = 0; 307 308 free_sysparm_buf: 309 papr_sysparm_buf_free(kern_spbuf); 310 return ret; 311 } 312 313 static long papr_sysparm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) 314 { 315 void __user *argp = (__force void __user *)arg; 316 long ret; 317 318 switch (ioctl) { 319 case PAPR_SYSPARM_IOC_GET: 320 ret = papr_sysparm_ioctl_get(argp); 321 break; 322 case PAPR_SYSPARM_IOC_SET: 323 if (filp->f_mode & FMODE_WRITE) 324 ret = papr_sysparm_ioctl_set(argp); 325 else 326 ret = -EBADF; 327 break; 328 default: 329 ret = -ENOIOCTLCMD; 330 break; 331 } 332 return ret; 333 } 334 335 static const struct file_operations papr_sysparm_ops = { 336 .unlocked_ioctl = papr_sysparm_ioctl, 337 }; 338 339 static struct miscdevice papr_sysparm_dev = { 340 .minor = MISC_DYNAMIC_MINOR, 341 .name = "papr-sysparm", 342 .fops = &papr_sysparm_ops, 343 }; 344 345 static __init int papr_sysparm_init(void) 346 { 347 if (!rtas_function_implemented(RTAS_FN_IBM_GET_SYSTEM_PARAMETER)) 348 return -ENODEV; 349 350 return misc_register(&papr_sysparm_dev); 351 } 352 machine_device_initcall(pseries, papr_sysparm_init); 353
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.