Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/s390/char/   (Linux Kernel Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 9 kB image not shown  

Quelle  vmcp.c   Sprache: unbekannt

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright IBM Corp. 2004, 2010
 * Interface implementation for communication with the z/VM control program
 *
 * Author(s): Christian Borntraeger <borntraeger@de.ibm.com>
 *
 * z/VMs CP offers the possibility to issue commands via the diagnose code 8
 * this driver implements a character device that issues these commands and
 * returns the answer of CP.
 *
 * The idea of this driver is based on cpint from Neale Ferguson and #CP in CMS
 */


#include <linux/fs.h>
#include <linux/init.h>
#include <linux/compat.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/cma.h>
#include <linux/mm.h>
#include <asm/machine.h>
#include <asm/cpcmd.h>
#include <asm/debug.h>
#include <asm/vmcp.h>

struct vmcp_session {
 char *response;
 unsigned int bufsize;
 unsigned int cma_alloc : 1;
 int resp_size;
 int resp_code;
 struct mutex mutex;
};

static debug_info_t *vmcp_debug;

static unsigned long vmcp_cma_size __initdata = CONFIG_VMCP_CMA_SIZE * 1024 * 1024;
static struct cma *vmcp_cma;

static int __init early_parse_vmcp_cma(char *p)
{
 if (!p)
  return 1;
 vmcp_cma_size = ALIGN(memparse(p, NULL), PAGE_SIZE);
 return 0;
}
early_param("vmcp_cma", early_parse_vmcp_cma);

void __init vmcp_cma_reserve(void)
{
 if (!machine_is_vm())
  return;
 cma_declare_contiguous(0, vmcp_cma_size, 000false"vmcp", &vmcp_cma);
}

static void vmcp_response_alloc(struct vmcp_session *session)
{
 struct page *page = NULL;
 int nr_pages, order;

 order = get_order(session->bufsize);
 nr_pages = ALIGN(session->bufsize, PAGE_SIZE) >> PAGE_SHIFT;
 /*
 * For anything below order 3 allocations rely on the buddy
 * allocator. If such low-order allocations can't be handled
 * anymore the system won't work anyway.
 */

 if (order > 2)
  page = cma_alloc(vmcp_cma, nr_pages, 0false);
 if (page) {
  session->response = (char *)page_to_virt(page);
  session->cma_alloc = 1;
  return;
 }
 session->response = (char *)__get_free_pages(GFP_KERNEL | __GFP_RETRY_MAYFAIL, order);
}

static void vmcp_response_free(struct vmcp_session *session)
{
 int nr_pages, order;
 struct page *page;

 if (!session->response)
  return;
 order = get_order(session->bufsize);
 nr_pages = ALIGN(session->bufsize, PAGE_SIZE) >> PAGE_SHIFT;
 if (session->cma_alloc) {
  page = virt_to_page(session->response);
  cma_release(vmcp_cma, page, nr_pages);
  session->cma_alloc = 0;
 } else {
  free_pages((unsigned long)session->response, order);
 }
 session->response = NULL;
}

static int vmcp_open(struct inode *inode, struct file *file)
{
 struct vmcp_session *session;

 if (!capable(CAP_SYS_ADMIN))
  return -EPERM;

 session = kmalloc(sizeof(*session), GFP_KERNEL);
 if (!session)
  return -ENOMEM;

 session->bufsize = PAGE_SIZE;
 session->response = NULL;
 session->resp_size = 0;
 mutex_init(&session->mutex);
 file->private_data = session;
 return nonseekable_open(inode, file);
}

static int vmcp_release(struct inode *inode, struct file *file)
{
 struct vmcp_session *session;

 session = file->private_data;
 file->private_data = NULL;
 vmcp_response_free(session);
 kfree(session);
 return 0;
}

static ssize_t
vmcp_read(struct file *file, char __user *buff, size_t count, loff_t *ppos)
{
 ssize_t ret;
 size_t size;
 struct vmcp_session *session;

 session = file->private_data;
 if (mutex_lock_interruptible(&session->mutex))
  return -ERESTARTSYS;
 if (!session->response) {
  mutex_unlock(&session->mutex);
  return 0;
 }
 size = min_t(size_t, session->resp_size, session->bufsize);
 ret = simple_read_from_buffer(buff, count, ppos,
     session->response, size);

 mutex_unlock(&session->mutex);

 return ret;
}

static ssize_t
vmcp_write(struct file *file, const char __user *buff, size_t count,
    loff_t *ppos)
{
 char *cmd;
 struct vmcp_session *session;

 if (count > 240)
  return -EINVAL;
 cmd = memdup_user_nul(buff, count);
 if (IS_ERR(cmd))
  return PTR_ERR(cmd);
 session = file->private_data;
 if (mutex_lock_interruptible(&session->mutex)) {
  kfree(cmd);
  return -ERESTARTSYS;
 }
 if (!session->response)
  vmcp_response_alloc(session);
 if (!session->response) {
  mutex_unlock(&session->mutex);
  kfree(cmd);
  return -ENOMEM;
 }
 debug_text_event(vmcp_debug, 1, cmd);
 session->resp_size = cpcmd(cmd, session->response, session->bufsize,
       &session->resp_code);
 mutex_unlock(&session->mutex);
 kfree(cmd);
 *ppos = 0;  /* reset the file pointer after a command */
 return count;
}


/*
 * These ioctls are available, as the semantics of the diagnose 8 call
 * does not fit very well into a Linux call. Diagnose X'08' is described in
 * CP Programming Services SC24-6084-00
 *
 * VMCP_GETCODE: gives the CP return code back to user space
 * VMCP_SETBUF: sets the response buffer for the next write call. diagnose 8
 * expects adjacent pages in real storage and to make matters worse, we
 * dont know the size of the response. Therefore we default to PAGESIZE and
 * let userspace to change the response size, if userspace expects a bigger
 * response
 */

static long vmcp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
 struct vmcp_session *session;
 int ret = -ENOTTY;
 int __user *argp;

 session = file->private_data;
 if (is_compat_task())
  argp = compat_ptr(arg);
 else
  argp = (int __user *)arg;
 if (mutex_lock_interruptible(&session->mutex))
  return -ERESTARTSYS;
 switch (cmd) {
 case VMCP_GETCODE:
  ret = put_user(session->resp_code, argp);
  break;
 case VMCP_SETBUF:
  vmcp_response_free(session);
  ret = get_user(session->bufsize, argp);
  if (ret)
   session->bufsize = PAGE_SIZE;
  if (!session->bufsize || get_order(session->bufsize) > 8) {
   session->bufsize = PAGE_SIZE;
   ret = -EINVAL;
  }
  break;
 case VMCP_GETSIZE:
  ret = put_user(session->resp_size, argp);
  break;
 default:
  break;
 }
 mutex_unlock(&session->mutex);
 return ret;
}

static const struct file_operations vmcp_fops = {
 .owner  = THIS_MODULE,
 .open  = vmcp_open,
 .release = vmcp_release,
 .read  = vmcp_read,
 .write  = vmcp_write,
 .unlocked_ioctl = vmcp_ioctl,
 .compat_ioctl = vmcp_ioctl,
};

static struct miscdevice vmcp_dev = {
 .name = "vmcp",
 .minor = MISC_DYNAMIC_MINOR,
 .fops = &vmcp_fops,
};

static int __init vmcp_init(void)
{
 int ret;

 if (!machine_is_vm())
  return 0;

 vmcp_debug = debug_register("vmcp"11240);
 if (!vmcp_debug)
  return -ENOMEM;

 ret = debug_register_view(vmcp_debug, &debug_hex_ascii_view);
 if (ret) {
  debug_unregister(vmcp_debug);
  return ret;
 }

 ret = misc_register(&vmcp_dev);
 if (ret)
  debug_unregister(vmcp_debug);
 return ret;
}
device_initcall(vmcp_init);

Messung V0.5 in Prozent
C=95 H=93 G=93

[Dauer der Verarbeitung: 0.1 Sekunden, vorverarbeitet 2026-06-08]