Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright IBM Corp. 2004, 2010
0004  * Interface implementation for communication with the z/VM control program
0005  *
0006  * Author(s): Christian Borntraeger <borntraeger@de.ibm.com>
0007  *
0008  * z/VMs CP offers the possibility to issue commands via the diagnose code 8
0009  * this driver implements a character device that issues these commands and
0010  * returns the answer of CP.
0011  *
0012  * The idea of this driver is based on cpint from Neale Ferguson and #CP in CMS
0013  */
0014 
0015 #include <linux/fs.h>
0016 #include <linux/init.h>
0017 #include <linux/compat.h>
0018 #include <linux/kernel.h>
0019 #include <linux/miscdevice.h>
0020 #include <linux/slab.h>
0021 #include <linux/uaccess.h>
0022 #include <linux/export.h>
0023 #include <linux/mutex.h>
0024 #include <linux/cma.h>
0025 #include <linux/mm.h>
0026 #include <asm/cpcmd.h>
0027 #include <asm/debug.h>
0028 #include <asm/vmcp.h>
0029 
0030 struct vmcp_session {
0031     char *response;
0032     unsigned int bufsize;
0033     unsigned int cma_alloc : 1;
0034     int resp_size;
0035     int resp_code;
0036     struct mutex mutex;
0037 };
0038 
0039 static debug_info_t *vmcp_debug;
0040 
0041 static unsigned long vmcp_cma_size __initdata = CONFIG_VMCP_CMA_SIZE * 1024 * 1024;
0042 static struct cma *vmcp_cma;
0043 
0044 static int __init early_parse_vmcp_cma(char *p)
0045 {
0046     if (!p)
0047         return 1;
0048     vmcp_cma_size = ALIGN(memparse(p, NULL), PAGE_SIZE);
0049     return 0;
0050 }
0051 early_param("vmcp_cma", early_parse_vmcp_cma);
0052 
0053 void __init vmcp_cma_reserve(void)
0054 {
0055     if (!MACHINE_IS_VM)
0056         return;
0057     cma_declare_contiguous(0, vmcp_cma_size, 0, 0, 0, false, "vmcp", &vmcp_cma);
0058 }
0059 
0060 static void vmcp_response_alloc(struct vmcp_session *session)
0061 {
0062     struct page *page = NULL;
0063     int nr_pages, order;
0064 
0065     order = get_order(session->bufsize);
0066     nr_pages = ALIGN(session->bufsize, PAGE_SIZE) >> PAGE_SHIFT;
0067     /*
0068      * For anything below order 3 allocations rely on the buddy
0069      * allocator. If such low-order allocations can't be handled
0070      * anymore the system won't work anyway.
0071      */
0072     if (order > 2)
0073         page = cma_alloc(vmcp_cma, nr_pages, 0, false);
0074     if (page) {
0075         session->response = (char *)page_to_virt(page);
0076         session->cma_alloc = 1;
0077         return;
0078     }
0079     session->response = (char *)__get_free_pages(GFP_KERNEL | __GFP_RETRY_MAYFAIL, order);
0080 }
0081 
0082 static void vmcp_response_free(struct vmcp_session *session)
0083 {
0084     int nr_pages, order;
0085     struct page *page;
0086 
0087     if (!session->response)
0088         return;
0089     order = get_order(session->bufsize);
0090     nr_pages = ALIGN(session->bufsize, PAGE_SIZE) >> PAGE_SHIFT;
0091     if (session->cma_alloc) {
0092         page = virt_to_page((unsigned long)session->response);
0093         cma_release(vmcp_cma, page, nr_pages);
0094         session->cma_alloc = 0;
0095     } else {
0096         free_pages((unsigned long)session->response, order);
0097     }
0098     session->response = NULL;
0099 }
0100 
0101 static int vmcp_open(struct inode *inode, struct file *file)
0102 {
0103     struct vmcp_session *session;
0104 
0105     if (!capable(CAP_SYS_ADMIN))
0106         return -EPERM;
0107 
0108     session = kmalloc(sizeof(*session), GFP_KERNEL);
0109     if (!session)
0110         return -ENOMEM;
0111 
0112     session->bufsize = PAGE_SIZE;
0113     session->response = NULL;
0114     session->resp_size = 0;
0115     mutex_init(&session->mutex);
0116     file->private_data = session;
0117     return nonseekable_open(inode, file);
0118 }
0119 
0120 static int vmcp_release(struct inode *inode, struct file *file)
0121 {
0122     struct vmcp_session *session;
0123 
0124     session = file->private_data;
0125     file->private_data = NULL;
0126     vmcp_response_free(session);
0127     kfree(session);
0128     return 0;
0129 }
0130 
0131 static ssize_t
0132 vmcp_read(struct file *file, char __user *buff, size_t count, loff_t *ppos)
0133 {
0134     ssize_t ret;
0135     size_t size;
0136     struct vmcp_session *session;
0137 
0138     session = file->private_data;
0139     if (mutex_lock_interruptible(&session->mutex))
0140         return -ERESTARTSYS;
0141     if (!session->response) {
0142         mutex_unlock(&session->mutex);
0143         return 0;
0144     }
0145     size = min_t(size_t, session->resp_size, session->bufsize);
0146     ret = simple_read_from_buffer(buff, count, ppos,
0147                     session->response, size);
0148 
0149     mutex_unlock(&session->mutex);
0150 
0151     return ret;
0152 }
0153 
0154 static ssize_t
0155 vmcp_write(struct file *file, const char __user *buff, size_t count,
0156        loff_t *ppos)
0157 {
0158     char *cmd;
0159     struct vmcp_session *session;
0160 
0161     if (count > 240)
0162         return -EINVAL;
0163     cmd = memdup_user_nul(buff, count);
0164     if (IS_ERR(cmd))
0165         return PTR_ERR(cmd);
0166     session = file->private_data;
0167     if (mutex_lock_interruptible(&session->mutex)) {
0168         kfree(cmd);
0169         return -ERESTARTSYS;
0170     }
0171     if (!session->response)
0172         vmcp_response_alloc(session);
0173     if (!session->response) {
0174         mutex_unlock(&session->mutex);
0175         kfree(cmd);
0176         return -ENOMEM;
0177     }
0178     debug_text_event(vmcp_debug, 1, cmd);
0179     session->resp_size = cpcmd(cmd, session->response, session->bufsize,
0180                    &session->resp_code);
0181     mutex_unlock(&session->mutex);
0182     kfree(cmd);
0183     *ppos = 0;      /* reset the file pointer after a command */
0184     return count;
0185 }
0186 
0187 
0188 /*
0189  * These ioctls are available, as the semantics of the diagnose 8 call
0190  * does not fit very well into a Linux call. Diagnose X'08' is described in
0191  * CP Programming Services SC24-6084-00
0192  *
0193  * VMCP_GETCODE: gives the CP return code back to user space
0194  * VMCP_SETBUF: sets the response buffer for the next write call. diagnose 8
0195  * expects adjacent pages in real storage and to make matters worse, we
0196  * dont know the size of the response. Therefore we default to PAGESIZE and
0197  * let userspace to change the response size, if userspace expects a bigger
0198  * response
0199  */
0200 static long vmcp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0201 {
0202     struct vmcp_session *session;
0203     int ret = -ENOTTY;
0204     int __user *argp;
0205 
0206     session = file->private_data;
0207     if (is_compat_task())
0208         argp = compat_ptr(arg);
0209     else
0210         argp = (int __user *)arg;
0211     if (mutex_lock_interruptible(&session->mutex))
0212         return -ERESTARTSYS;
0213     switch (cmd) {
0214     case VMCP_GETCODE:
0215         ret = put_user(session->resp_code, argp);
0216         break;
0217     case VMCP_SETBUF:
0218         vmcp_response_free(session);
0219         ret = get_user(session->bufsize, argp);
0220         if (ret)
0221             session->bufsize = PAGE_SIZE;
0222         if (!session->bufsize || get_order(session->bufsize) > 8) {
0223             session->bufsize = PAGE_SIZE;
0224             ret = -EINVAL;
0225         }
0226         break;
0227     case VMCP_GETSIZE:
0228         ret = put_user(session->resp_size, argp);
0229         break;
0230     default:
0231         break;
0232     }
0233     mutex_unlock(&session->mutex);
0234     return ret;
0235 }
0236 
0237 static const struct file_operations vmcp_fops = {
0238     .owner      = THIS_MODULE,
0239     .open       = vmcp_open,
0240     .release    = vmcp_release,
0241     .read       = vmcp_read,
0242     .write      = vmcp_write,
0243     .unlocked_ioctl = vmcp_ioctl,
0244     .compat_ioctl   = vmcp_ioctl,
0245     .llseek     = no_llseek,
0246 };
0247 
0248 static struct miscdevice vmcp_dev = {
0249     .name   = "vmcp",
0250     .minor  = MISC_DYNAMIC_MINOR,
0251     .fops   = &vmcp_fops,
0252 };
0253 
0254 static int __init vmcp_init(void)
0255 {
0256     int ret;
0257 
0258     if (!MACHINE_IS_VM)
0259         return 0;
0260 
0261     vmcp_debug = debug_register("vmcp", 1, 1, 240);
0262     if (!vmcp_debug)
0263         return -ENOMEM;
0264 
0265     ret = debug_register_view(vmcp_debug, &debug_hex_ascii_view);
0266     if (ret) {
0267         debug_unregister(vmcp_debug);
0268         return ret;
0269     }
0270 
0271     ret = misc_register(&vmcp_dev);
0272     if (ret)
0273         debug_unregister(vmcp_debug);
0274     return ret;
0275 }
0276 device_initcall(vmcp_init);