forked from Minki/linux
s390/sclp: Add SCLP character device driver
Add a character misc device "sclp_ctl" that allows to run SCCBs from user space using the SCLP_CTL_SCCB ioctl. Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
e9a8f32a98
commit
d475f942b1
@ -72,6 +72,7 @@ Code Seq#(hex) Include File Comments
|
||||
0x06 all linux/lp.h
|
||||
0x09 all linux/raid/md_u.h
|
||||
0x10 00-0F drivers/char/s390/vmcp.h
|
||||
0x10 10-1F arch/s390/include/uapi/sclp_ctl.h
|
||||
0x12 all linux/fs.h
|
||||
linux/blkpg.h
|
||||
0x1b all InfiniBand Subsystem <http://infiniband.sourceforge.net/>
|
||||
|
@ -35,6 +35,7 @@ header-y += siginfo.h
|
||||
header-y += signal.h
|
||||
header-y += socket.h
|
||||
header-y += sockios.h
|
||||
header-y += sclp_ctl.h
|
||||
header-y += stat.h
|
||||
header-y += statfs.h
|
||||
header-y += swab.h
|
||||
|
24
arch/s390/include/uapi/asm/sclp_ctl.h
Normal file
24
arch/s390/include/uapi/asm/sclp_ctl.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* IOCTL interface for SCLP
|
||||
*
|
||||
* Copyright IBM Corp. 2012
|
||||
*
|
||||
* Author: Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
||||
*/
|
||||
|
||||
#ifndef _ASM_SCLP_CTL_H
|
||||
#define _ASM_SCLP_CTL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct sclp_ctl_sccb {
|
||||
__u32 cmdw;
|
||||
__u64 sccb;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define SCLP_CTL_IOCTL_MAGIC 0x10
|
||||
|
||||
#define SCLP_CTL_SCCB \
|
||||
_IOWR(SCLP_CTL_IOCTL_MAGIC, 0x10, struct sclp_ctl_sccb)
|
||||
|
||||
#endif
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
|
||||
sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o
|
||||
sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o
|
||||
|
||||
obj-$(CONFIG_TN3270) += raw3270.o
|
||||
obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
|
||||
|
@ -148,14 +148,19 @@ static int sclp_init(void);
|
||||
int
|
||||
sclp_service_call(sclp_cmdw_t command, void *sccb)
|
||||
{
|
||||
int cc;
|
||||
int cc = 4; /* Initialize for program check handling */
|
||||
|
||||
asm volatile(
|
||||
" .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */
|
||||
" ipm %0\n"
|
||||
" srl %0,28"
|
||||
: "=&d" (cc) : "d" (command), "a" (__pa(sccb))
|
||||
"0: .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */
|
||||
"1: ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
"2:\n"
|
||||
EX_TABLE(0b, 2b)
|
||||
EX_TABLE(1b, 2b)
|
||||
: "+&d" (cc) : "d" (command), "a" (__pa(sccb))
|
||||
: "cc", "memory");
|
||||
if (cc == 4)
|
||||
return -EINVAL;
|
||||
if (cc == 3)
|
||||
return -EIO;
|
||||
if (cc == 2)
|
||||
|
@ -171,6 +171,7 @@ int sclp_remove_processed(struct sccb_header *sccb);
|
||||
int sclp_deactivate(void);
|
||||
int sclp_reactivate(void);
|
||||
int sclp_service_call(sclp_cmdw_t command, void *sccb);
|
||||
int sclp_sync_request(sclp_cmdw_t command, void *sccb);
|
||||
|
||||
int sclp_sdias_init(void);
|
||||
void sclp_sdias_exit(void);
|
||||
|
@ -195,7 +195,7 @@ static void sclp_sync_callback(struct sclp_req *req, void *data)
|
||||
complete(completion);
|
||||
}
|
||||
|
||||
static int do_sync_request(sclp_cmdw_t cmd, void *sccb)
|
||||
int sclp_sync_request(sclp_cmdw_t cmd, void *sccb)
|
||||
{
|
||||
struct completion completion;
|
||||
struct sclp_req *request;
|
||||
@ -270,7 +270,7 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info)
|
||||
if (!sccb)
|
||||
return -ENOMEM;
|
||||
sccb->header.length = sizeof(*sccb);
|
||||
rc = do_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb);
|
||||
rc = sclp_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb);
|
||||
if (rc)
|
||||
goto out;
|
||||
if (sccb->header.response_code != 0x0010) {
|
||||
@ -304,7 +304,7 @@ static int do_cpu_configure(sclp_cmdw_t cmd)
|
||||
if (!sccb)
|
||||
return -ENOMEM;
|
||||
sccb->header.length = sizeof(*sccb);
|
||||
rc = do_sync_request(cmd, sccb);
|
||||
rc = sclp_sync_request(cmd, sccb);
|
||||
if (rc)
|
||||
goto out;
|
||||
switch (sccb->header.response_code) {
|
||||
@ -374,7 +374,7 @@ static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
|
||||
return -ENOMEM;
|
||||
sccb->header.length = PAGE_SIZE;
|
||||
sccb->rn = rn;
|
||||
rc = do_sync_request(cmd, sccb);
|
||||
rc = sclp_sync_request(cmd, sccb);
|
||||
if (rc)
|
||||
goto out;
|
||||
switch (sccb->header.response_code) {
|
||||
@ -429,7 +429,7 @@ static int sclp_attach_storage(u8 id)
|
||||
if (!sccb)
|
||||
return -ENOMEM;
|
||||
sccb->header.length = PAGE_SIZE;
|
||||
rc = do_sync_request(0x00080001 | id << 8, sccb);
|
||||
rc = sclp_sync_request(0x00080001 | id << 8, sccb);
|
||||
if (rc)
|
||||
goto out;
|
||||
switch (sccb->header.response_code) {
|
||||
@ -627,7 +627,7 @@ static int __init sclp_detect_standby_memory(void)
|
||||
for (id = 0; id <= sclp_max_storage_id; id++) {
|
||||
memset(sccb, 0, PAGE_SIZE);
|
||||
sccb->header.length = PAGE_SIZE;
|
||||
rc = do_sync_request(0x00040001 | id << 8, sccb);
|
||||
rc = sclp_sync_request(0x00040001 | id << 8, sccb);
|
||||
if (rc)
|
||||
goto out;
|
||||
switch (sccb->header.response_code) {
|
||||
@ -714,7 +714,7 @@ static int do_pci_configure(sclp_cmdw_t cmd, u32 fid)
|
||||
sccb->header.length = PAGE_SIZE;
|
||||
sccb->atype = SCLP_RECONFIG_PCI_ATPYE;
|
||||
sccb->aid = fid;
|
||||
rc = do_sync_request(cmd, sccb);
|
||||
rc = sclp_sync_request(cmd, sccb);
|
||||
if (rc)
|
||||
goto out;
|
||||
switch (sccb->header.response_code) {
|
||||
@ -771,7 +771,7 @@ static int do_chp_configure(sclp_cmdw_t cmd)
|
||||
if (!sccb)
|
||||
return -ENOMEM;
|
||||
sccb->header.length = sizeof(*sccb);
|
||||
rc = do_sync_request(cmd, sccb);
|
||||
rc = sclp_sync_request(cmd, sccb);
|
||||
if (rc)
|
||||
goto out;
|
||||
switch (sccb->header.response_code) {
|
||||
@ -846,7 +846,7 @@ int sclp_chp_read_info(struct sclp_chp_info *info)
|
||||
if (!sccb)
|
||||
return -ENOMEM;
|
||||
sccb->header.length = sizeof(*sccb);
|
||||
rc = do_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb);
|
||||
rc = sclp_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb);
|
||||
if (rc)
|
||||
goto out;
|
||||
if (sccb->header.response_code != 0x0010) {
|
||||
|
145
drivers/s390/char/sclp_ctl.c
Normal file
145
drivers/s390/char/sclp_ctl.c
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* IOCTL interface for SCLP
|
||||
*
|
||||
* Copyright IBM Corp. 2012
|
||||
*
|
||||
* Author: Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/compat.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/compat.h>
|
||||
#include <asm/compat.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;
|
||||
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;
|
||||
if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sizeof(*sccb))) {
|
||||
rc = -EFAULT;
|
||||
goto out_free;
|
||||
}
|
||||
if (sccb->length > PAGE_SIZE || sccb->length < 8)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sccb->length)) {
|
||||
rc = -EFAULT;
|
||||
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,
|
||||
};
|
||||
|
||||
/*
|
||||
* Register sclp_ctl misc device
|
||||
*/
|
||||
static int __init sclp_ctl_init(void)
|
||||
{
|
||||
return misc_register(&sclp_ctl_device);
|
||||
}
|
||||
module_init(sclp_ctl_init);
|
||||
|
||||
/*
|
||||
* Deregister sclp_ctl misc device
|
||||
*/
|
||||
static void __exit sclp_ctl_exit(void)
|
||||
{
|
||||
misc_deregister(&sclp_ctl_device);
|
||||
}
|
||||
module_exit(sclp_ctl_exit);
|
Loading…
Reference in New Issue
Block a user