18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IOCTL interface for SCLP 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2012 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/compat.h> 118c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 128c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/gfp.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/ioctl.h> 168c2ecf20Sopenharmony_ci#include <linux/fs.h> 178c2ecf20Sopenharmony_ci#include <asm/sclp_ctl.h> 188c2ecf20Sopenharmony_ci#include <asm/sclp.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "sclp.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * Supported command words 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_cistatic unsigned int sclp_ctl_sccb_wlist[] = { 268c2ecf20Sopenharmony_ci 0x00400002, 278c2ecf20Sopenharmony_ci 0x00410002, 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * Check if command word is supported 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_cistatic int sclp_ctl_cmdw_supported(unsigned int cmdw) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci int i; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sclp_ctl_sccb_wlist); i++) { 388c2ecf20Sopenharmony_ci if (cmdw == sclp_ctl_sccb_wlist[i]) 398c2ecf20Sopenharmony_ci return 1; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void __user *u64_to_uptr(u64 value) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci if (is_compat_task()) 478c2ecf20Sopenharmony_ci return compat_ptr(value); 488c2ecf20Sopenharmony_ci else 498c2ecf20Sopenharmony_ci return (void __user *)(unsigned long)value; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * Start SCLP request 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_cistatic int sclp_ctl_ioctl_sccb(void __user *user_area) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct sclp_ctl_sccb ctl_sccb; 588c2ecf20Sopenharmony_ci struct sccb_header *sccb; 598c2ecf20Sopenharmony_ci unsigned long copied; 608c2ecf20Sopenharmony_ci int rc; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb))) 638c2ecf20Sopenharmony_ci return -EFAULT; 648c2ecf20Sopenharmony_ci if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw)) 658c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 668c2ecf20Sopenharmony_ci sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 678c2ecf20Sopenharmony_ci if (!sccb) 688c2ecf20Sopenharmony_ci return -ENOMEM; 698c2ecf20Sopenharmony_ci copied = PAGE_SIZE - 708c2ecf20Sopenharmony_ci copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), PAGE_SIZE); 718c2ecf20Sopenharmony_ci if (offsetof(struct sccb_header, length) + 728c2ecf20Sopenharmony_ci sizeof(sccb->length) > copied || sccb->length > copied) { 738c2ecf20Sopenharmony_ci rc = -EFAULT; 748c2ecf20Sopenharmony_ci goto out_free; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci if (sccb->length < 8) { 778c2ecf20Sopenharmony_ci rc = -EINVAL; 788c2ecf20Sopenharmony_ci goto out_free; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci rc = sclp_sync_request(ctl_sccb.cmdw, sccb); 818c2ecf20Sopenharmony_ci if (rc) 828c2ecf20Sopenharmony_ci goto out_free; 838c2ecf20Sopenharmony_ci if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length)) 848c2ecf20Sopenharmony_ci rc = -EFAULT; 858c2ecf20Sopenharmony_ciout_free: 868c2ecf20Sopenharmony_ci free_page((unsigned long) sccb); 878c2ecf20Sopenharmony_ci return rc; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * SCLP SCCB ioctl function 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_cistatic long sclp_ctl_ioctl(struct file *filp, unsigned int cmd, 948c2ecf20Sopenharmony_ci unsigned long arg) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci void __user *argp; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (is_compat_task()) 998c2ecf20Sopenharmony_ci argp = compat_ptr(arg); 1008c2ecf20Sopenharmony_ci else 1018c2ecf20Sopenharmony_ci argp = (void __user *) arg; 1028c2ecf20Sopenharmony_ci switch (cmd) { 1038c2ecf20Sopenharmony_ci case SCLP_CTL_SCCB: 1048c2ecf20Sopenharmony_ci return sclp_ctl_ioctl_sccb(argp); 1058c2ecf20Sopenharmony_ci default: /* unknown ioctl number */ 1068c2ecf20Sopenharmony_ci return -ENOTTY; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* 1118c2ecf20Sopenharmony_ci * File operations 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistatic const struct file_operations sclp_ctl_fops = { 1148c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1158c2ecf20Sopenharmony_ci .open = nonseekable_open, 1168c2ecf20Sopenharmony_ci .unlocked_ioctl = sclp_ctl_ioctl, 1178c2ecf20Sopenharmony_ci .compat_ioctl = sclp_ctl_ioctl, 1188c2ecf20Sopenharmony_ci .llseek = no_llseek, 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * Misc device definition 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_cistatic struct miscdevice sclp_ctl_device = { 1258c2ecf20Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 1268c2ecf20Sopenharmony_ci .name = "sclp", 1278c2ecf20Sopenharmony_ci .fops = &sclp_ctl_fops, 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_cibuiltin_misc_device(sclp_ctl_device); 130