cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

chsc_sch.c (22756B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Driver for s390 chsc subchannels
      4 *
      5 * Copyright IBM Corp. 2008, 2011
      6 *
      7 * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
      8 *
      9 */
     10
     11#include <linux/slab.h>
     12#include <linux/compat.h>
     13#include <linux/device.h>
     14#include <linux/module.h>
     15#include <linux/uaccess.h>
     16#include <linux/miscdevice.h>
     17#include <linux/kernel_stat.h>
     18
     19#include <asm/cio.h>
     20#include <asm/chsc.h>
     21#include <asm/isc.h>
     22
     23#include "cio.h"
     24#include "cio_debug.h"
     25#include "css.h"
     26#include "chsc_sch.h"
     27#include "ioasm.h"
     28
     29static debug_info_t *chsc_debug_msg_id;
     30static debug_info_t *chsc_debug_log_id;
     31
     32static struct chsc_request *on_close_request;
     33static struct chsc_async_area *on_close_chsc_area;
     34static DEFINE_MUTEX(on_close_mutex);
     35
     36#define CHSC_MSG(imp, args...) do {					\
     37		debug_sprintf_event(chsc_debug_msg_id, imp , ##args);	\
     38	} while (0)
     39
     40#define CHSC_LOG(imp, txt) do {					\
     41		debug_text_event(chsc_debug_log_id, imp , txt);	\
     42	} while (0)
     43
     44static void CHSC_LOG_HEX(int level, void *data, int length)
     45{
     46	debug_event(chsc_debug_log_id, level, data, length);
     47}
     48
     49MODULE_AUTHOR("IBM Corporation");
     50MODULE_DESCRIPTION("driver for s390 chsc subchannels");
     51MODULE_LICENSE("GPL");
     52
     53static void chsc_subchannel_irq(struct subchannel *sch)
     54{
     55	struct chsc_private *private = dev_get_drvdata(&sch->dev);
     56	struct chsc_request *request = private->request;
     57	struct irb *irb = this_cpu_ptr(&cio_irb);
     58
     59	CHSC_LOG(4, "irb");
     60	CHSC_LOG_HEX(4, irb, sizeof(*irb));
     61	inc_irq_stat(IRQIO_CSC);
     62
     63	/* Copy irb to provided request and set done. */
     64	if (!request) {
     65		CHSC_MSG(0, "Interrupt on sch 0.%x.%04x with no request\n",
     66			 sch->schid.ssid, sch->schid.sch_no);
     67		return;
     68	}
     69	private->request = NULL;
     70	memcpy(&request->irb, irb, sizeof(*irb));
     71	cio_update_schib(sch);
     72	complete(&request->completion);
     73	put_device(&sch->dev);
     74}
     75
     76static int chsc_subchannel_probe(struct subchannel *sch)
     77{
     78	struct chsc_private *private;
     79	int ret;
     80
     81	CHSC_MSG(6, "Detected chsc subchannel 0.%x.%04x\n",
     82		 sch->schid.ssid, sch->schid.sch_no);
     83	sch->isc = CHSC_SCH_ISC;
     84	private = kzalloc(sizeof(*private), GFP_KERNEL);
     85	if (!private)
     86		return -ENOMEM;
     87	dev_set_drvdata(&sch->dev, private);
     88	ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
     89	if (ret) {
     90		CHSC_MSG(0, "Failed to enable 0.%x.%04x: %d\n",
     91			 sch->schid.ssid, sch->schid.sch_no, ret);
     92		dev_set_drvdata(&sch->dev, NULL);
     93		kfree(private);
     94	}
     95	return ret;
     96}
     97
     98static void chsc_subchannel_remove(struct subchannel *sch)
     99{
    100	struct chsc_private *private;
    101
    102	cio_disable_subchannel(sch);
    103	private = dev_get_drvdata(&sch->dev);
    104	dev_set_drvdata(&sch->dev, NULL);
    105	if (private->request) {
    106		complete(&private->request->completion);
    107		put_device(&sch->dev);
    108	}
    109	kfree(private);
    110}
    111
    112static void chsc_subchannel_shutdown(struct subchannel *sch)
    113{
    114	cio_disable_subchannel(sch);
    115}
    116
    117static struct css_device_id chsc_subchannel_ids[] = {
    118	{ .match_flags = 0x1, .type =SUBCHANNEL_TYPE_CHSC, },
    119	{ /* end of list */ },
    120};
    121MODULE_DEVICE_TABLE(css, chsc_subchannel_ids);
    122
    123static struct css_driver chsc_subchannel_driver = {
    124	.drv = {
    125		.owner = THIS_MODULE,
    126		.name = "chsc_subchannel",
    127	},
    128	.subchannel_type = chsc_subchannel_ids,
    129	.irq = chsc_subchannel_irq,
    130	.probe = chsc_subchannel_probe,
    131	.remove = chsc_subchannel_remove,
    132	.shutdown = chsc_subchannel_shutdown,
    133};
    134
    135static int __init chsc_init_dbfs(void)
    136{
    137	chsc_debug_msg_id = debug_register("chsc_msg", 8, 1, 4 * sizeof(long));
    138	if (!chsc_debug_msg_id)
    139		goto out;
    140	debug_register_view(chsc_debug_msg_id, &debug_sprintf_view);
    141	debug_set_level(chsc_debug_msg_id, 2);
    142	chsc_debug_log_id = debug_register("chsc_log", 16, 1, 16);
    143	if (!chsc_debug_log_id)
    144		goto out;
    145	debug_register_view(chsc_debug_log_id, &debug_hex_ascii_view);
    146	debug_set_level(chsc_debug_log_id, 2);
    147	return 0;
    148out:
    149	debug_unregister(chsc_debug_msg_id);
    150	return -ENOMEM;
    151}
    152
    153static void chsc_remove_dbfs(void)
    154{
    155	debug_unregister(chsc_debug_log_id);
    156	debug_unregister(chsc_debug_msg_id);
    157}
    158
    159static int __init chsc_init_sch_driver(void)
    160{
    161	return css_driver_register(&chsc_subchannel_driver);
    162}
    163
    164static void chsc_cleanup_sch_driver(void)
    165{
    166	css_driver_unregister(&chsc_subchannel_driver);
    167}
    168
    169static DEFINE_SPINLOCK(chsc_lock);
    170
    171static int chsc_subchannel_match_next_free(struct device *dev, const void *data)
    172{
    173	struct subchannel *sch = to_subchannel(dev);
    174
    175	return sch->schib.pmcw.ena && !scsw_fctl(&sch->schib.scsw);
    176}
    177
    178static struct subchannel *chsc_get_next_subchannel(struct subchannel *sch)
    179{
    180	struct device *dev;
    181
    182	dev = driver_find_device(&chsc_subchannel_driver.drv,
    183				 sch ? &sch->dev : NULL, NULL,
    184				 chsc_subchannel_match_next_free);
    185	return dev ? to_subchannel(dev) : NULL;
    186}
    187
    188/**
    189 * chsc_async() - try to start a chsc request asynchronously
    190 * @chsc_area: request to be started
    191 * @request: request structure to associate
    192 *
    193 * Tries to start a chsc request on one of the existing chsc subchannels.
    194 * Returns:
    195 *  %0 if the request was performed synchronously
    196 *  %-EINPROGRESS if the request was successfully started
    197 *  %-EBUSY if all chsc subchannels are busy
    198 *  %-ENODEV if no chsc subchannels are available
    199 * Context:
    200 *  interrupts disabled, chsc_lock held
    201 */
    202static int chsc_async(struct chsc_async_area *chsc_area,
    203		      struct chsc_request *request)
    204{
    205	int cc;
    206	struct chsc_private *private;
    207	struct subchannel *sch = NULL;
    208	int ret = -ENODEV;
    209	char dbf[10];
    210
    211	chsc_area->header.key = PAGE_DEFAULT_KEY >> 4;
    212	while ((sch = chsc_get_next_subchannel(sch))) {
    213		spin_lock(sch->lock);
    214		private = dev_get_drvdata(&sch->dev);
    215		if (private->request) {
    216			spin_unlock(sch->lock);
    217			ret = -EBUSY;
    218			continue;
    219		}
    220		chsc_area->header.sid = sch->schid;
    221		CHSC_LOG(2, "schid");
    222		CHSC_LOG_HEX(2, &sch->schid, sizeof(sch->schid));
    223		cc = chsc(chsc_area);
    224		snprintf(dbf, sizeof(dbf), "cc:%d", cc);
    225		CHSC_LOG(2, dbf);
    226		switch (cc) {
    227		case 0:
    228			ret = 0;
    229			break;
    230		case 1:
    231			sch->schib.scsw.cmd.fctl |= SCSW_FCTL_START_FUNC;
    232			ret = -EINPROGRESS;
    233			private->request = request;
    234			break;
    235		case 2:
    236			ret = -EBUSY;
    237			break;
    238		default:
    239			ret = -ENODEV;
    240		}
    241		spin_unlock(sch->lock);
    242		CHSC_MSG(2, "chsc on 0.%x.%04x returned cc=%d\n",
    243			 sch->schid.ssid, sch->schid.sch_no, cc);
    244		if (ret == -EINPROGRESS)
    245			return -EINPROGRESS;
    246		put_device(&sch->dev);
    247		if (ret == 0)
    248			return 0;
    249	}
    250	return ret;
    251}
    252
    253static void chsc_log_command(void *chsc_area)
    254{
    255	char dbf[10];
    256
    257	snprintf(dbf, sizeof(dbf), "CHSC:%x", ((uint16_t *)chsc_area)[1]);
    258	CHSC_LOG(0, dbf);
    259	CHSC_LOG_HEX(0, chsc_area, 32);
    260}
    261
    262static int chsc_examine_irb(struct chsc_request *request)
    263{
    264	int backed_up;
    265
    266	if (!(scsw_stctl(&request->irb.scsw) & SCSW_STCTL_STATUS_PEND))
    267		return -EIO;
    268	backed_up = scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHAIN_CHECK;
    269	request->irb.scsw.cmd.cstat &= ~SCHN_STAT_CHAIN_CHECK;
    270	if (scsw_cstat(&request->irb.scsw) == 0)
    271		return 0;
    272	if (!backed_up)
    273		return 0;
    274	if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_PROG_CHECK)
    275		return -EIO;
    276	if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_PROT_CHECK)
    277		return -EPERM;
    278	if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHN_DATA_CHK)
    279		return -EAGAIN;
    280	if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHN_CTRL_CHK)
    281		return -EAGAIN;
    282	return -EIO;
    283}
    284
    285static int chsc_ioctl_start(void __user *user_area)
    286{
    287	struct chsc_request *request;
    288	struct chsc_async_area *chsc_area;
    289	int ret;
    290	char dbf[10];
    291
    292	if (!css_general_characteristics.dynio)
    293		/* It makes no sense to try. */
    294		return -EOPNOTSUPP;
    295	chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
    296	if (!chsc_area)
    297		return -ENOMEM;
    298	request = kzalloc(sizeof(*request), GFP_KERNEL);
    299	if (!request) {
    300		ret = -ENOMEM;
    301		goto out_free;
    302	}
    303	init_completion(&request->completion);
    304	if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
    305		ret = -EFAULT;
    306		goto out_free;
    307	}
    308	chsc_log_command(chsc_area);
    309	spin_lock_irq(&chsc_lock);
    310	ret = chsc_async(chsc_area, request);
    311	spin_unlock_irq(&chsc_lock);
    312	if (ret == -EINPROGRESS) {
    313		wait_for_completion(&request->completion);
    314		ret = chsc_examine_irb(request);
    315	}
    316	/* copy area back to user */
    317	if (!ret)
    318		if (copy_to_user(user_area, chsc_area, PAGE_SIZE))
    319			ret = -EFAULT;
    320out_free:
    321	snprintf(dbf, sizeof(dbf), "ret:%d", ret);
    322	CHSC_LOG(0, dbf);
    323	kfree(request);
    324	free_page((unsigned long)chsc_area);
    325	return ret;
    326}
    327
    328static int chsc_ioctl_on_close_set(void __user *user_area)
    329{
    330	char dbf[13];
    331	int ret;
    332
    333	mutex_lock(&on_close_mutex);
    334	if (on_close_chsc_area) {
    335		ret = -EBUSY;
    336		goto out_unlock;
    337	}
    338	on_close_request = kzalloc(sizeof(*on_close_request), GFP_KERNEL);
    339	if (!on_close_request) {
    340		ret = -ENOMEM;
    341		goto out_unlock;
    342	}
    343	on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
    344	if (!on_close_chsc_area) {
    345		ret = -ENOMEM;
    346		goto out_free_request;
    347	}
    348	if (copy_from_user(on_close_chsc_area, user_area, PAGE_SIZE)) {
    349		ret = -EFAULT;
    350		goto out_free_chsc;
    351	}
    352	ret = 0;
    353	goto out_unlock;
    354
    355out_free_chsc:
    356	free_page((unsigned long)on_close_chsc_area);
    357	on_close_chsc_area = NULL;
    358out_free_request:
    359	kfree(on_close_request);
    360	on_close_request = NULL;
    361out_unlock:
    362	mutex_unlock(&on_close_mutex);
    363	snprintf(dbf, sizeof(dbf), "ocsret:%d", ret);
    364	CHSC_LOG(0, dbf);
    365	return ret;
    366}
    367
    368static int chsc_ioctl_on_close_remove(void)
    369{
    370	char dbf[13];
    371	int ret;
    372
    373	mutex_lock(&on_close_mutex);
    374	if (!on_close_chsc_area) {
    375		ret = -ENOENT;
    376		goto out_unlock;
    377	}
    378	free_page((unsigned long)on_close_chsc_area);
    379	on_close_chsc_area = NULL;
    380	kfree(on_close_request);
    381	on_close_request = NULL;
    382	ret = 0;
    383out_unlock:
    384	mutex_unlock(&on_close_mutex);
    385	snprintf(dbf, sizeof(dbf), "ocrret:%d", ret);
    386	CHSC_LOG(0, dbf);
    387	return ret;
    388}
    389
    390static int chsc_ioctl_start_sync(void __user *user_area)
    391{
    392	struct chsc_sync_area *chsc_area;
    393	int ret, ccode;
    394
    395	chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
    396	if (!chsc_area)
    397		return -ENOMEM;
    398	if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
    399		ret = -EFAULT;
    400		goto out_free;
    401	}
    402	if (chsc_area->header.code & 0x4000) {
    403		ret = -EINVAL;
    404		goto out_free;
    405	}
    406	chsc_log_command(chsc_area);
    407	ccode = chsc(chsc_area);
    408	if (ccode != 0) {
    409		ret = -EIO;
    410		goto out_free;
    411	}
    412	if (copy_to_user(user_area, chsc_area, PAGE_SIZE))
    413		ret = -EFAULT;
    414	else
    415		ret = 0;
    416out_free:
    417	free_page((unsigned long)chsc_area);
    418	return ret;
    419}
    420
    421static int chsc_ioctl_info_channel_path(void __user *user_cd)
    422{
    423	struct chsc_chp_cd *cd;
    424	int ret, ccode;
    425	struct {
    426		struct chsc_header request;
    427		u32 : 2;
    428		u32 m : 1;
    429		u32 : 1;
    430		u32 fmt1 : 4;
    431		u32 cssid : 8;
    432		u32 : 8;
    433		u32 first_chpid : 8;
    434		u32 : 24;
    435		u32 last_chpid : 8;
    436		u32 : 32;
    437		struct chsc_header response;
    438		u8 data[PAGE_SIZE - 20];
    439	} __attribute__ ((packed)) *scpcd_area;
    440
    441	scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
    442	if (!scpcd_area)
    443		return -ENOMEM;
    444	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
    445	if (!cd) {
    446		ret = -ENOMEM;
    447		goto out_free;
    448	}
    449	if (copy_from_user(cd, user_cd, sizeof(*cd))) {
    450		ret = -EFAULT;
    451		goto out_free;
    452	}
    453	scpcd_area->request.length = 0x0010;
    454	scpcd_area->request.code = 0x0028;
    455	scpcd_area->m = cd->m;
    456	scpcd_area->fmt1 = cd->fmt;
    457	scpcd_area->cssid = cd->chpid.cssid;
    458	scpcd_area->first_chpid = cd->chpid.id;
    459	scpcd_area->last_chpid = cd->chpid.id;
    460
    461	ccode = chsc(scpcd_area);
    462	if (ccode != 0) {
    463		ret = -EIO;
    464		goto out_free;
    465	}
    466	if (scpcd_area->response.code != 0x0001) {
    467		ret = -EIO;
    468		CHSC_MSG(0, "scpcd: response code=%x\n",
    469			 scpcd_area->response.code);
    470		goto out_free;
    471	}
    472	memcpy(&cd->cpcb, &scpcd_area->response, scpcd_area->response.length);
    473	if (copy_to_user(user_cd, cd, sizeof(*cd)))
    474		ret = -EFAULT;
    475	else
    476		ret = 0;
    477out_free:
    478	kfree(cd);
    479	free_page((unsigned long)scpcd_area);
    480	return ret;
    481}
    482
    483static int chsc_ioctl_info_cu(void __user *user_cd)
    484{
    485	struct chsc_cu_cd *cd;
    486	int ret, ccode;
    487	struct {
    488		struct chsc_header request;
    489		u32 : 2;
    490		u32 m : 1;
    491		u32 : 1;
    492		u32 fmt1 : 4;
    493		u32 cssid : 8;
    494		u32 : 8;
    495		u32 first_cun : 8;
    496		u32 : 24;
    497		u32 last_cun : 8;
    498		u32 : 32;
    499		struct chsc_header response;
    500		u8 data[PAGE_SIZE - 20];
    501	} __attribute__ ((packed)) *scucd_area;
    502
    503	scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
    504	if (!scucd_area)
    505		return -ENOMEM;
    506	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
    507	if (!cd) {
    508		ret = -ENOMEM;
    509		goto out_free;
    510	}
    511	if (copy_from_user(cd, user_cd, sizeof(*cd))) {
    512		ret = -EFAULT;
    513		goto out_free;
    514	}
    515	scucd_area->request.length = 0x0010;
    516	scucd_area->request.code = 0x0026;
    517	scucd_area->m = cd->m;
    518	scucd_area->fmt1 = cd->fmt;
    519	scucd_area->cssid = cd->cssid;
    520	scucd_area->first_cun = cd->cun;
    521	scucd_area->last_cun = cd->cun;
    522
    523	ccode = chsc(scucd_area);
    524	if (ccode != 0) {
    525		ret = -EIO;
    526		goto out_free;
    527	}
    528	if (scucd_area->response.code != 0x0001) {
    529		ret = -EIO;
    530		CHSC_MSG(0, "scucd: response code=%x\n",
    531			 scucd_area->response.code);
    532		goto out_free;
    533	}
    534	memcpy(&cd->cucb, &scucd_area->response, scucd_area->response.length);
    535	if (copy_to_user(user_cd, cd, sizeof(*cd)))
    536		ret = -EFAULT;
    537	else
    538		ret = 0;
    539out_free:
    540	kfree(cd);
    541	free_page((unsigned long)scucd_area);
    542	return ret;
    543}
    544
    545static int chsc_ioctl_info_sch_cu(void __user *user_cud)
    546{
    547	struct chsc_sch_cud *cud;
    548	int ret, ccode;
    549	struct {
    550		struct chsc_header request;
    551		u32 : 2;
    552		u32 m : 1;
    553		u32 : 5;
    554		u32 fmt1 : 4;
    555		u32 : 2;
    556		u32 ssid : 2;
    557		u32 first_sch : 16;
    558		u32 : 8;
    559		u32 cssid : 8;
    560		u32 last_sch : 16;
    561		u32 : 32;
    562		struct chsc_header response;
    563		u8 data[PAGE_SIZE - 20];
    564	} __attribute__ ((packed)) *sscud_area;
    565
    566	sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
    567	if (!sscud_area)
    568		return -ENOMEM;
    569	cud = kzalloc(sizeof(*cud), GFP_KERNEL);
    570	if (!cud) {
    571		ret = -ENOMEM;
    572		goto out_free;
    573	}
    574	if (copy_from_user(cud, user_cud, sizeof(*cud))) {
    575		ret = -EFAULT;
    576		goto out_free;
    577	}
    578	sscud_area->request.length = 0x0010;
    579	sscud_area->request.code = 0x0006;
    580	sscud_area->m = cud->schid.m;
    581	sscud_area->fmt1 = cud->fmt;
    582	sscud_area->ssid = cud->schid.ssid;
    583	sscud_area->first_sch = cud->schid.sch_no;
    584	sscud_area->cssid = cud->schid.cssid;
    585	sscud_area->last_sch = cud->schid.sch_no;
    586
    587	ccode = chsc(sscud_area);
    588	if (ccode != 0) {
    589		ret = -EIO;
    590		goto out_free;
    591	}
    592	if (sscud_area->response.code != 0x0001) {
    593		ret = -EIO;
    594		CHSC_MSG(0, "sscud: response code=%x\n",
    595			 sscud_area->response.code);
    596		goto out_free;
    597	}
    598	memcpy(&cud->scub, &sscud_area->response, sscud_area->response.length);
    599	if (copy_to_user(user_cud, cud, sizeof(*cud)))
    600		ret = -EFAULT;
    601	else
    602		ret = 0;
    603out_free:
    604	kfree(cud);
    605	free_page((unsigned long)sscud_area);
    606	return ret;
    607}
    608
    609static int chsc_ioctl_conf_info(void __user *user_ci)
    610{
    611	struct chsc_conf_info *ci;
    612	int ret, ccode;
    613	struct {
    614		struct chsc_header request;
    615		u32 : 2;
    616		u32 m : 1;
    617		u32 : 1;
    618		u32 fmt1 : 4;
    619		u32 cssid : 8;
    620		u32 : 6;
    621		u32 ssid : 2;
    622		u32 : 8;
    623		u64 : 64;
    624		struct chsc_header response;
    625		u8 data[PAGE_SIZE - 20];
    626	} __attribute__ ((packed)) *sci_area;
    627
    628	sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
    629	if (!sci_area)
    630		return -ENOMEM;
    631	ci = kzalloc(sizeof(*ci), GFP_KERNEL);
    632	if (!ci) {
    633		ret = -ENOMEM;
    634		goto out_free;
    635	}
    636	if (copy_from_user(ci, user_ci, sizeof(*ci))) {
    637		ret = -EFAULT;
    638		goto out_free;
    639	}
    640	sci_area->request.length = 0x0010;
    641	sci_area->request.code = 0x0012;
    642	sci_area->m = ci->id.m;
    643	sci_area->fmt1 = ci->fmt;
    644	sci_area->cssid = ci->id.cssid;
    645	sci_area->ssid = ci->id.ssid;
    646
    647	ccode = chsc(sci_area);
    648	if (ccode != 0) {
    649		ret = -EIO;
    650		goto out_free;
    651	}
    652	if (sci_area->response.code != 0x0001) {
    653		ret = -EIO;
    654		CHSC_MSG(0, "sci: response code=%x\n",
    655			 sci_area->response.code);
    656		goto out_free;
    657	}
    658	memcpy(&ci->scid, &sci_area->response, sci_area->response.length);
    659	if (copy_to_user(user_ci, ci, sizeof(*ci)))
    660		ret = -EFAULT;
    661	else
    662		ret = 0;
    663out_free:
    664	kfree(ci);
    665	free_page((unsigned long)sci_area);
    666	return ret;
    667}
    668
    669static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
    670{
    671	struct chsc_comp_list *ccl;
    672	int ret, ccode;
    673	struct {
    674		struct chsc_header request;
    675		u32 ctype : 8;
    676		u32 : 4;
    677		u32 fmt : 4;
    678		u32 : 16;
    679		u64 : 64;
    680		u32 list_parm[2];
    681		u64 : 64;
    682		struct chsc_header response;
    683		u8 data[PAGE_SIZE - 36];
    684	} __attribute__ ((packed)) *sccl_area;
    685	struct {
    686		u32 m : 1;
    687		u32 : 31;
    688		u32 cssid : 8;
    689		u32 : 16;
    690		u32 chpid : 8;
    691	} __attribute__ ((packed)) *chpid_parm;
    692	struct {
    693		u32 f_cssid : 8;
    694		u32 l_cssid : 8;
    695		u32 : 16;
    696		u32 res;
    697	} __attribute__ ((packed)) *cssids_parm;
    698
    699	sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
    700	if (!sccl_area)
    701		return -ENOMEM;
    702	ccl = kzalloc(sizeof(*ccl), GFP_KERNEL);
    703	if (!ccl) {
    704		ret = -ENOMEM;
    705		goto out_free;
    706	}
    707	if (copy_from_user(ccl, user_ccl, sizeof(*ccl))) {
    708		ret = -EFAULT;
    709		goto out_free;
    710	}
    711	sccl_area->request.length = 0x0020;
    712	sccl_area->request.code = 0x0030;
    713	sccl_area->fmt = ccl->req.fmt;
    714	sccl_area->ctype = ccl->req.ctype;
    715	switch (sccl_area->ctype) {
    716	case CCL_CU_ON_CHP:
    717	case CCL_IOP_CHP:
    718		chpid_parm = (void *)&sccl_area->list_parm;
    719		chpid_parm->m = ccl->req.chpid.m;
    720		chpid_parm->cssid = ccl->req.chpid.chp.cssid;
    721		chpid_parm->chpid = ccl->req.chpid.chp.id;
    722		break;
    723	case CCL_CSS_IMG:
    724	case CCL_CSS_IMG_CONF_CHAR:
    725		cssids_parm = (void *)&sccl_area->list_parm;
    726		cssids_parm->f_cssid = ccl->req.cssids.f_cssid;
    727		cssids_parm->l_cssid = ccl->req.cssids.l_cssid;
    728		break;
    729	}
    730	ccode = chsc(sccl_area);
    731	if (ccode != 0) {
    732		ret = -EIO;
    733		goto out_free;
    734	}
    735	if (sccl_area->response.code != 0x0001) {
    736		ret = -EIO;
    737		CHSC_MSG(0, "sccl: response code=%x\n",
    738			 sccl_area->response.code);
    739		goto out_free;
    740	}
    741	memcpy(&ccl->sccl, &sccl_area->response, sccl_area->response.length);
    742	if (copy_to_user(user_ccl, ccl, sizeof(*ccl)))
    743		ret = -EFAULT;
    744	else
    745		ret = 0;
    746out_free:
    747	kfree(ccl);
    748	free_page((unsigned long)sccl_area);
    749	return ret;
    750}
    751
    752static int chsc_ioctl_chpd(void __user *user_chpd)
    753{
    754	struct chsc_scpd *scpd_area;
    755	struct chsc_cpd_info *chpd;
    756	int ret;
    757
    758	chpd = kzalloc(sizeof(*chpd), GFP_KERNEL);
    759	scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
    760	if (!scpd_area || !chpd) {
    761		ret = -ENOMEM;
    762		goto out_free;
    763	}
    764	if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) {
    765		ret = -EFAULT;
    766		goto out_free;
    767	}
    768	ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt,
    769					       chpd->rfmt, chpd->c, chpd->m,
    770					       scpd_area);
    771	if (ret)
    772		goto out_free;
    773	memcpy(&chpd->chpdb, &scpd_area->response, scpd_area->response.length);
    774	if (copy_to_user(user_chpd, chpd, sizeof(*chpd)))
    775		ret = -EFAULT;
    776out_free:
    777	kfree(chpd);
    778	free_page((unsigned long)scpd_area);
    779	return ret;
    780}
    781
    782static int chsc_ioctl_dcal(void __user *user_dcal)
    783{
    784	struct chsc_dcal *dcal;
    785	int ret, ccode;
    786	struct {
    787		struct chsc_header request;
    788		u32 atype : 8;
    789		u32 : 4;
    790		u32 fmt : 4;
    791		u32 : 16;
    792		u32 res0[2];
    793		u32 list_parm[2];
    794		u32 res1[2];
    795		struct chsc_header response;
    796		u8 data[PAGE_SIZE - 36];
    797	} __attribute__ ((packed)) *sdcal_area;
    798
    799	sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
    800	if (!sdcal_area)
    801		return -ENOMEM;
    802	dcal = kzalloc(sizeof(*dcal), GFP_KERNEL);
    803	if (!dcal) {
    804		ret = -ENOMEM;
    805		goto out_free;
    806	}
    807	if (copy_from_user(dcal, user_dcal, sizeof(*dcal))) {
    808		ret = -EFAULT;
    809		goto out_free;
    810	}
    811	sdcal_area->request.length = 0x0020;
    812	sdcal_area->request.code = 0x0034;
    813	sdcal_area->atype = dcal->req.atype;
    814	sdcal_area->fmt = dcal->req.fmt;
    815	memcpy(&sdcal_area->list_parm, &dcal->req.list_parm,
    816	       sizeof(sdcal_area->list_parm));
    817
    818	ccode = chsc(sdcal_area);
    819	if (ccode != 0) {
    820		ret = -EIO;
    821		goto out_free;
    822	}
    823	if (sdcal_area->response.code != 0x0001) {
    824		ret = -EIO;
    825		CHSC_MSG(0, "sdcal: response code=%x\n",
    826			 sdcal_area->response.code);
    827		goto out_free;
    828	}
    829	memcpy(&dcal->sdcal, &sdcal_area->response,
    830	       sdcal_area->response.length);
    831	if (copy_to_user(user_dcal, dcal, sizeof(*dcal)))
    832		ret = -EFAULT;
    833	else
    834		ret = 0;
    835out_free:
    836	kfree(dcal);
    837	free_page((unsigned long)sdcal_area);
    838	return ret;
    839}
    840
    841static long chsc_ioctl(struct file *filp, unsigned int cmd,
    842		       unsigned long arg)
    843{
    844	void __user *argp;
    845
    846	CHSC_MSG(2, "chsc_ioctl called, cmd=%x\n", cmd);
    847	if (is_compat_task())
    848		argp = compat_ptr(arg);
    849	else
    850		argp = (void __user *)arg;
    851	switch (cmd) {
    852	case CHSC_START:
    853		return chsc_ioctl_start(argp);
    854	case CHSC_START_SYNC:
    855		return chsc_ioctl_start_sync(argp);
    856	case CHSC_INFO_CHANNEL_PATH:
    857		return chsc_ioctl_info_channel_path(argp);
    858	case CHSC_INFO_CU:
    859		return chsc_ioctl_info_cu(argp);
    860	case CHSC_INFO_SCH_CU:
    861		return chsc_ioctl_info_sch_cu(argp);
    862	case CHSC_INFO_CI:
    863		return chsc_ioctl_conf_info(argp);
    864	case CHSC_INFO_CCL:
    865		return chsc_ioctl_conf_comp_list(argp);
    866	case CHSC_INFO_CPD:
    867		return chsc_ioctl_chpd(argp);
    868	case CHSC_INFO_DCAL:
    869		return chsc_ioctl_dcal(argp);
    870	case CHSC_ON_CLOSE_SET:
    871		return chsc_ioctl_on_close_set(argp);
    872	case CHSC_ON_CLOSE_REMOVE:
    873		return chsc_ioctl_on_close_remove();
    874	default: /* unknown ioctl number */
    875		return -ENOIOCTLCMD;
    876	}
    877}
    878
    879static atomic_t chsc_ready_for_use = ATOMIC_INIT(1);
    880
    881static int chsc_open(struct inode *inode, struct file *file)
    882{
    883	if (!atomic_dec_and_test(&chsc_ready_for_use)) {
    884		atomic_inc(&chsc_ready_for_use);
    885		return -EBUSY;
    886	}
    887	return nonseekable_open(inode, file);
    888}
    889
    890static int chsc_release(struct inode *inode, struct file *filp)
    891{
    892	char dbf[13];
    893	int ret;
    894
    895	mutex_lock(&on_close_mutex);
    896	if (!on_close_chsc_area)
    897		goto out_unlock;
    898	init_completion(&on_close_request->completion);
    899	CHSC_LOG(0, "on_close");
    900	chsc_log_command(on_close_chsc_area);
    901	spin_lock_irq(&chsc_lock);
    902	ret = chsc_async(on_close_chsc_area, on_close_request);
    903	spin_unlock_irq(&chsc_lock);
    904	if (ret == -EINPROGRESS) {
    905		wait_for_completion(&on_close_request->completion);
    906		ret = chsc_examine_irb(on_close_request);
    907	}
    908	snprintf(dbf, sizeof(dbf), "relret:%d", ret);
    909	CHSC_LOG(0, dbf);
    910	free_page((unsigned long)on_close_chsc_area);
    911	on_close_chsc_area = NULL;
    912	kfree(on_close_request);
    913	on_close_request = NULL;
    914out_unlock:
    915	mutex_unlock(&on_close_mutex);
    916	atomic_inc(&chsc_ready_for_use);
    917	return 0;
    918}
    919
    920static const struct file_operations chsc_fops = {
    921	.owner = THIS_MODULE,
    922	.open = chsc_open,
    923	.release = chsc_release,
    924	.unlocked_ioctl = chsc_ioctl,
    925	.compat_ioctl = chsc_ioctl,
    926	.llseek = no_llseek,
    927};
    928
    929static struct miscdevice chsc_misc_device = {
    930	.minor = MISC_DYNAMIC_MINOR,
    931	.name = "chsc",
    932	.fops = &chsc_fops,
    933};
    934
    935static int __init chsc_misc_init(void)
    936{
    937	return misc_register(&chsc_misc_device);
    938}
    939
    940static void chsc_misc_cleanup(void)
    941{
    942	misc_deregister(&chsc_misc_device);
    943}
    944
    945static int __init chsc_sch_init(void)
    946{
    947	int ret;
    948
    949	ret = chsc_init_dbfs();
    950	if (ret)
    951		return ret;
    952	isc_register(CHSC_SCH_ISC);
    953	ret = chsc_init_sch_driver();
    954	if (ret)
    955		goto out_dbf;
    956	ret = chsc_misc_init();
    957	if (ret)
    958		goto out_driver;
    959	return ret;
    960out_driver:
    961	chsc_cleanup_sch_driver();
    962out_dbf:
    963	isc_unregister(CHSC_SCH_ISC);
    964	chsc_remove_dbfs();
    965	return ret;
    966}
    967
    968static void __exit chsc_sch_exit(void)
    969{
    970	chsc_misc_cleanup();
    971	chsc_cleanup_sch_driver();
    972	isc_unregister(CHSC_SCH_ISC);
    973	chsc_remove_dbfs();
    974}
    975
    976module_init(chsc_sch_init);
    977module_exit(chsc_sch_exit);