root/drivers/s390/char/vmcp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. early_parse_vmcp_cma
  2. vmcp_cma_reserve
  3. vmcp_response_alloc
  4. vmcp_response_free
  5. vmcp_open
  6. vmcp_release
  7. vmcp_read
  8. vmcp_write
  9. vmcp_ioctl
  10. vmcp_init

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright IBM Corp. 2004, 2010
   4  * Interface implementation for communication with the z/VM control program
   5  *
   6  * Author(s): Christian Borntraeger <borntraeger@de.ibm.com>
   7  *
   8  * z/VMs CP offers the possibility to issue commands via the diagnose code 8
   9  * this driver implements a character device that issues these commands and
  10  * returns the answer of CP.
  11  *
  12  * The idea of this driver is based on cpint from Neale Ferguson and #CP in CMS
  13  */
  14 
  15 #include <linux/fs.h>
  16 #include <linux/init.h>
  17 #include <linux/compat.h>
  18 #include <linux/kernel.h>
  19 #include <linux/miscdevice.h>
  20 #include <linux/slab.h>
  21 #include <linux/uaccess.h>
  22 #include <linux/export.h>
  23 #include <linux/mutex.h>
  24 #include <linux/cma.h>
  25 #include <linux/mm.h>
  26 #include <asm/cpcmd.h>
  27 #include <asm/debug.h>
  28 #include <asm/vmcp.h>
  29 
  30 struct vmcp_session {
  31         char *response;
  32         unsigned int bufsize;
  33         unsigned int cma_alloc : 1;
  34         int resp_size;
  35         int resp_code;
  36         struct mutex mutex;
  37 };
  38 
  39 static debug_info_t *vmcp_debug;
  40 
  41 static unsigned long vmcp_cma_size __initdata = CONFIG_VMCP_CMA_SIZE * 1024 * 1024;
  42 static struct cma *vmcp_cma;
  43 
  44 static int __init early_parse_vmcp_cma(char *p)
  45 {
  46         if (!p)
  47                 return 1;
  48         vmcp_cma_size = ALIGN(memparse(p, NULL), PAGE_SIZE);
  49         return 0;
  50 }
  51 early_param("vmcp_cma", early_parse_vmcp_cma);
  52 
  53 void __init vmcp_cma_reserve(void)
  54 {
  55         if (!MACHINE_IS_VM)
  56                 return;
  57         cma_declare_contiguous(0, vmcp_cma_size, 0, 0, 0, false, "vmcp", &vmcp_cma);
  58 }
  59 
  60 static void vmcp_response_alloc(struct vmcp_session *session)
  61 {
  62         struct page *page = NULL;
  63         int nr_pages, order;
  64 
  65         order = get_order(session->bufsize);
  66         nr_pages = ALIGN(session->bufsize, PAGE_SIZE) >> PAGE_SHIFT;
  67         /*
  68          * For anything below order 3 allocations rely on the buddy
  69          * allocator. If such low-order allocations can't be handled
  70          * anymore the system won't work anyway.
  71          */
  72         if (order > 2)
  73                 page = cma_alloc(vmcp_cma, nr_pages, 0, false);
  74         if (page) {
  75                 session->response = (char *)page_to_phys(page);
  76                 session->cma_alloc = 1;
  77                 return;
  78         }
  79         session->response = (char *)__get_free_pages(GFP_KERNEL | __GFP_RETRY_MAYFAIL, order);
  80 }
  81 
  82 static void vmcp_response_free(struct vmcp_session *session)
  83 {
  84         int nr_pages, order;
  85         struct page *page;
  86 
  87         if (!session->response)
  88                 return;
  89         order = get_order(session->bufsize);
  90         nr_pages = ALIGN(session->bufsize, PAGE_SIZE) >> PAGE_SHIFT;
  91         if (session->cma_alloc) {
  92                 page = phys_to_page((unsigned long)session->response);
  93                 cma_release(vmcp_cma, page, nr_pages);
  94                 session->cma_alloc = 0;
  95         } else {
  96                 free_pages((unsigned long)session->response, order);
  97         }
  98         session->response = NULL;
  99 }
 100 
 101 static int vmcp_open(struct inode *inode, struct file *file)
 102 {
 103         struct vmcp_session *session;
 104 
 105         if (!capable(CAP_SYS_ADMIN))
 106                 return -EPERM;
 107 
 108         session = kmalloc(sizeof(*session), GFP_KERNEL);
 109         if (!session)
 110                 return -ENOMEM;
 111 
 112         session->bufsize = PAGE_SIZE;
 113         session->response = NULL;
 114         session->resp_size = 0;
 115         mutex_init(&session->mutex);
 116         file->private_data = session;
 117         return nonseekable_open(inode, file);
 118 }
 119 
 120 static int vmcp_release(struct inode *inode, struct file *file)
 121 {
 122         struct vmcp_session *session;
 123 
 124         session = file->private_data;
 125         file->private_data = NULL;
 126         vmcp_response_free(session);
 127         kfree(session);
 128         return 0;
 129 }
 130 
 131 static ssize_t
 132 vmcp_read(struct file *file, char __user *buff, size_t count, loff_t *ppos)
 133 {
 134         ssize_t ret;
 135         size_t size;
 136         struct vmcp_session *session;
 137 
 138         session = file->private_data;
 139         if (mutex_lock_interruptible(&session->mutex))
 140                 return -ERESTARTSYS;
 141         if (!session->response) {
 142                 mutex_unlock(&session->mutex);
 143                 return 0;
 144         }
 145         size = min_t(size_t, session->resp_size, session->bufsize);
 146         ret = simple_read_from_buffer(buff, count, ppos,
 147                                         session->response, size);
 148 
 149         mutex_unlock(&session->mutex);
 150 
 151         return ret;
 152 }
 153 
 154 static ssize_t
 155 vmcp_write(struct file *file, const char __user *buff, size_t count,
 156            loff_t *ppos)
 157 {
 158         char *cmd;
 159         struct vmcp_session *session;
 160 
 161         if (count > 240)
 162                 return -EINVAL;
 163         cmd = memdup_user_nul(buff, count);
 164         if (IS_ERR(cmd))
 165                 return PTR_ERR(cmd);
 166         session = file->private_data;
 167         if (mutex_lock_interruptible(&session->mutex)) {
 168                 kfree(cmd);
 169                 return -ERESTARTSYS;
 170         }
 171         if (!session->response)
 172                 vmcp_response_alloc(session);
 173         if (!session->response) {
 174                 mutex_unlock(&session->mutex);
 175                 kfree(cmd);
 176                 return -ENOMEM;
 177         }
 178         debug_text_event(vmcp_debug, 1, cmd);
 179         session->resp_size = cpcmd(cmd, session->response, session->bufsize,
 180                                    &session->resp_code);
 181         mutex_unlock(&session->mutex);
 182         kfree(cmd);
 183         *ppos = 0;              /* reset the file pointer after a command */
 184         return count;
 185 }
 186 
 187 
 188 /*
 189  * These ioctls are available, as the semantics of the diagnose 8 call
 190  * does not fit very well into a Linux call. Diagnose X'08' is described in
 191  * CP Programming Services SC24-6084-00
 192  *
 193  * VMCP_GETCODE: gives the CP return code back to user space
 194  * VMCP_SETBUF: sets the response buffer for the next write call. diagnose 8
 195  * expects adjacent pages in real storage and to make matters worse, we
 196  * dont know the size of the response. Therefore we default to PAGESIZE and
 197  * let userspace to change the response size, if userspace expects a bigger
 198  * response
 199  */
 200 static long vmcp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 201 {
 202         struct vmcp_session *session;
 203         int ret = -ENOTTY;
 204         int __user *argp;
 205 
 206         session = file->private_data;
 207         if (is_compat_task())
 208                 argp = compat_ptr(arg);
 209         else
 210                 argp = (int __user *)arg;
 211         if (mutex_lock_interruptible(&session->mutex))
 212                 return -ERESTARTSYS;
 213         switch (cmd) {
 214         case VMCP_GETCODE:
 215                 ret = put_user(session->resp_code, argp);
 216                 break;
 217         case VMCP_SETBUF:
 218                 vmcp_response_free(session);
 219                 ret = get_user(session->bufsize, argp);
 220                 if (ret)
 221                         session->bufsize = PAGE_SIZE;
 222                 if (!session->bufsize || get_order(session->bufsize) > 8) {
 223                         session->bufsize = PAGE_SIZE;
 224                         ret = -EINVAL;
 225                 }
 226                 break;
 227         case VMCP_GETSIZE:
 228                 ret = put_user(session->resp_size, argp);
 229                 break;
 230         default:
 231                 break;
 232         }
 233         mutex_unlock(&session->mutex);
 234         return ret;
 235 }
 236 
 237 static const struct file_operations vmcp_fops = {
 238         .owner          = THIS_MODULE,
 239         .open           = vmcp_open,
 240         .release        = vmcp_release,
 241         .read           = vmcp_read,
 242         .write          = vmcp_write,
 243         .unlocked_ioctl = vmcp_ioctl,
 244         .compat_ioctl   = vmcp_ioctl,
 245         .llseek         = no_llseek,
 246 };
 247 
 248 static struct miscdevice vmcp_dev = {
 249         .name   = "vmcp",
 250         .minor  = MISC_DYNAMIC_MINOR,
 251         .fops   = &vmcp_fops,
 252 };
 253 
 254 static int __init vmcp_init(void)
 255 {
 256         int ret;
 257 
 258         if (!MACHINE_IS_VM)
 259                 return 0;
 260 
 261         vmcp_debug = debug_register("vmcp", 1, 1, 240);
 262         if (!vmcp_debug)
 263                 return -ENOMEM;
 264 
 265         ret = debug_register_view(vmcp_debug, &debug_hex_ascii_view);
 266         if (ret) {
 267                 debug_unregister(vmcp_debug);
 268                 return ret;
 269         }
 270 
 271         ret = misc_register(&vmcp_dev);
 272         if (ret)
 273                 debug_unregister(vmcp_debug);
 274         return ret;
 275 }
 276 device_initcall(vmcp_init);

/* [<][>][^][v][top][bottom][index][help] */