mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
129 lines
2.5 KiB
129 lines
2.5 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* IOCTL interface for SCLP |
|
* |
|
* Copyright IBM Corp. 2012 |
|
* |
|
* Author: Michael Holzheu <[email protected]> |
|
*/ |
|
|
|
#include <linux/compat.h> |
|
#include <linux/uaccess.h> |
|
#include <linux/miscdevice.h> |
|
#include <linux/gfp.h> |
|
#include <linux/init.h> |
|
#include <linux/ioctl.h> |
|
#include <linux/fs.h> |
|
#include <asm/sclp_ctl.h> |
|
#include <asm/sclp.h> |
|
|
|
#include "sclp.h" |
|
|
|
/* |
|
* Supported command words |
|
*/ |
|
static unsigned int sclp_ctl_sccb_wlist[] = { |
|
0x00400002, |
|
0x00410002, |
|
}; |
|
|
|
/* |
|
* Check if command word is supported |
|
*/ |
|
static int sclp_ctl_cmdw_supported(unsigned int cmdw) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < ARRAY_SIZE(sclp_ctl_sccb_wlist); i++) { |
|
if (cmdw == sclp_ctl_sccb_wlist[i]) |
|
return 1; |
|
} |
|
return 0; |
|
} |
|
|
|
static void __user *u64_to_uptr(u64 value) |
|
{ |
|
if (is_compat_task()) |
|
return compat_ptr(value); |
|
else |
|
return (void __user *)(unsigned long)value; |
|
} |
|
|
|
/* |
|
* Start SCLP request |
|
*/ |
|
static int sclp_ctl_ioctl_sccb(void __user *user_area) |
|
{ |
|
struct sclp_ctl_sccb ctl_sccb; |
|
struct sccb_header *sccb; |
|
unsigned long copied; |
|
int rc; |
|
|
|
if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb))) |
|
return -EFAULT; |
|
if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw)) |
|
return -EOPNOTSUPP; |
|
sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); |
|
if (!sccb) |
|
return -ENOMEM; |
|
copied = PAGE_SIZE - |
|
copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), PAGE_SIZE); |
|
if (offsetof(struct sccb_header, length) + |
|
sizeof(sccb->length) > copied || sccb->length > copied) { |
|
rc = -EFAULT; |
|
goto out_free; |
|
} |
|
if (sccb->length < 8) { |
|
rc = -EINVAL; |
|
goto out_free; |
|
} |
|
rc = sclp_sync_request(ctl_sccb.cmdw, sccb); |
|
if (rc) |
|
goto out_free; |
|
if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length)) |
|
rc = -EFAULT; |
|
out_free: |
|
free_page((unsigned long) sccb); |
|
return rc; |
|
} |
|
|
|
/* |
|
* SCLP SCCB ioctl function |
|
*/ |
|
static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd, |
|
unsigned long arg) |
|
{ |
|
void __user *argp; |
|
|
|
if (is_compat_task()) |
|
argp = compat_ptr(arg); |
|
else |
|
argp = (void __user *) arg; |
|
switch (cmd) { |
|
case SCLP_CTL_SCCB: |
|
return sclp_ctl_ioctl_sccb(argp); |
|
default: /* unknown ioctl number */ |
|
return -ENOTTY; |
|
} |
|
} |
|
|
|
/* |
|
* File operations |
|
*/ |
|
static const struct file_operations sclp_ctl_fops = { |
|
.owner = THIS_MODULE, |
|
.open = nonseekable_open, |
|
.unlocked_ioctl = sclp_ctl_ioctl, |
|
.compat_ioctl = sclp_ctl_ioctl, |
|
.llseek = no_llseek, |
|
}; |
|
|
|
/* |
|
* Misc device definition |
|
*/ |
|
static struct miscdevice sclp_ctl_device = { |
|
.minor = MISC_DYNAMIC_MINOR, |
|
.name = "sclp", |
|
.fops = &sclp_ctl_fops, |
|
}; |
|
builtin_misc_device(sclp_ctl_device);
|
|
|