| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  *  drivers/s390/cio/chsc.c | 
 | 3 |  *   S/390 common I/O routines -- channel subsystem call | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4 |  * | 
| Cornelia Huck | c820de3 | 2008-07-14 09:58:45 +0200 | [diff] [blame] | 5 |  *    Copyright IBM Corp. 1999,2008 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6 |  *    Author(s): Ingo Adlung (adlung@de.ibm.com) | 
| Cornelia Huck | 4ce3b30 | 2006-01-14 13:21:04 -0800 | [diff] [blame] | 7 |  *		 Cornelia Huck (cornelia.huck@de.ibm.com) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 8 |  *		 Arnd Bergmann (arndb@de.ibm.com) | 
 | 9 |  */ | 
 | 10 |  | 
| Michael Ernst | e6d5a42 | 2008-12-25 13:39:36 +0100 | [diff] [blame] | 11 | #define KMSG_COMPONENT "cio" | 
 | 12 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | 
 | 13 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 14 | #include <linux/module.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 15 | #include <linux/slab.h> | 
 | 16 | #include <linux/init.h> | 
 | 17 | #include <linux/device.h> | 
 | 18 |  | 
 | 19 | #include <asm/cio.h> | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 20 | #include <asm/chpid.h> | 
| Cornelia Huck | 9d92a7e | 2008-07-14 09:59:05 +0200 | [diff] [blame] | 21 | #include <asm/chsc.h> | 
| Heiko Carstens | f5daba1 | 2009-03-26 15:24:01 +0100 | [diff] [blame] | 22 | #include <asm/crw.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 23 |  | 
 | 24 | #include "css.h" | 
 | 25 | #include "cio.h" | 
 | 26 | #include "cio_debug.h" | 
 | 27 | #include "ioasm.h" | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 28 | #include "chp.h" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 29 | #include "chsc.h" | 
 | 30 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | static void *sei_page; | 
| Sebastian Ott | 818c272 | 2010-04-22 17:17:03 +0200 | [diff] [blame] | 32 | static DEFINE_SPINLOCK(sda_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 33 |  | 
| Cornelia Huck | dae3984 | 2008-07-17 17:16:47 +0200 | [diff] [blame] | 34 | /** | 
 | 35 |  * chsc_error_from_response() - convert a chsc response to an error | 
 | 36 |  * @response: chsc response code | 
 | 37 |  * | 
 | 38 |  * Returns an appropriate Linux error code for @response. | 
 | 39 |  */ | 
 | 40 | int chsc_error_from_response(int response) | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 41 | { | 
 | 42 | 	switch (response) { | 
 | 43 | 	case 0x0001: | 
 | 44 | 		return 0; | 
 | 45 | 	case 0x0002: | 
 | 46 | 	case 0x0003: | 
 | 47 | 	case 0x0006: | 
 | 48 | 	case 0x0007: | 
 | 49 | 	case 0x0008: | 
 | 50 | 	case 0x000a: | 
 | 51 | 		return -EINVAL; | 
 | 52 | 	case 0x0004: | 
 | 53 | 		return -EOPNOTSUPP; | 
 | 54 | 	default: | 
 | 55 | 		return -EIO; | 
 | 56 | 	} | 
 | 57 | } | 
| Cornelia Huck | dae3984 | 2008-07-17 17:16:47 +0200 | [diff] [blame] | 58 | EXPORT_SYMBOL_GPL(chsc_error_from_response); | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 59 |  | 
| Peter Oberparleiter | 7ad6a24 | 2007-04-27 16:01:35 +0200 | [diff] [blame] | 60 | struct chsc_ssd_area { | 
 | 61 | 	struct chsc_header request; | 
 | 62 | 	u16 :10; | 
 | 63 | 	u16 ssid:2; | 
 | 64 | 	u16 :4; | 
 | 65 | 	u16 f_sch;	  /* first subchannel */ | 
 | 66 | 	u16 :16; | 
 | 67 | 	u16 l_sch;	  /* last subchannel */ | 
 | 68 | 	u32 :32; | 
 | 69 | 	struct chsc_header response; | 
 | 70 | 	u32 :32; | 
 | 71 | 	u8 sch_valid : 1; | 
 | 72 | 	u8 dev_valid : 1; | 
 | 73 | 	u8 st	     : 3; /* subchannel type */ | 
 | 74 | 	u8 zeroes    : 3; | 
 | 75 | 	u8  unit_addr;	  /* unit address */ | 
 | 76 | 	u16 devno;	  /* device number */ | 
 | 77 | 	u8 path_mask; | 
 | 78 | 	u8 fla_valid_mask; | 
 | 79 | 	u16 sch;	  /* subchannel */ | 
 | 80 | 	u8 chpid[8];	  /* chpids 0-7 */ | 
 | 81 | 	u16 fla[8];	  /* full link addresses 0-7 */ | 
 | 82 | } __attribute__ ((packed)); | 
 | 83 |  | 
 | 84 | int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 85 | { | 
| Peter Oberparleiter | 7ad6a24 | 2007-04-27 16:01:35 +0200 | [diff] [blame] | 86 | 	unsigned long page; | 
 | 87 | 	struct chsc_ssd_area *ssd_area; | 
 | 88 | 	int ccode; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 89 | 	int ret; | 
| Peter Oberparleiter | 7ad6a24 | 2007-04-27 16:01:35 +0200 | [diff] [blame] | 90 | 	int i; | 
 | 91 | 	int mask; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 92 |  | 
| Peter Oberparleiter | 7ad6a24 | 2007-04-27 16:01:35 +0200 | [diff] [blame] | 93 | 	page = get_zeroed_page(GFP_KERNEL | GFP_DMA); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 94 | 	if (!page) | 
 | 95 | 		return -ENOMEM; | 
| Peter Oberparleiter | 7ad6a24 | 2007-04-27 16:01:35 +0200 | [diff] [blame] | 96 | 	ssd_area = (struct chsc_ssd_area *) page; | 
 | 97 | 	ssd_area->request.length = 0x0010; | 
 | 98 | 	ssd_area->request.code = 0x0004; | 
 | 99 | 	ssd_area->ssid = schid.ssid; | 
 | 100 | 	ssd_area->f_sch = schid.sch_no; | 
 | 101 | 	ssd_area->l_sch = schid.sch_no; | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 102 |  | 
| Peter Oberparleiter | 7ad6a24 | 2007-04-27 16:01:35 +0200 | [diff] [blame] | 103 | 	ccode = chsc(ssd_area); | 
 | 104 | 	/* Check response. */ | 
 | 105 | 	if (ccode > 0) { | 
 | 106 | 		ret = (ccode == 3) ? -ENODEV : -EBUSY; | 
 | 107 | 		goto out_free; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 108 | 	} | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 109 | 	ret = chsc_error_from_response(ssd_area->response.code); | 
 | 110 | 	if (ret != 0) { | 
| Peter Oberparleiter | 7ad6a24 | 2007-04-27 16:01:35 +0200 | [diff] [blame] | 111 | 		CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n", | 
 | 112 | 			      schid.ssid, schid.sch_no, | 
 | 113 | 			      ssd_area->response.code); | 
| Peter Oberparleiter | 7ad6a24 | 2007-04-27 16:01:35 +0200 | [diff] [blame] | 114 | 		goto out_free; | 
 | 115 | 	} | 
 | 116 | 	if (!ssd_area->sch_valid) { | 
 | 117 | 		ret = -ENODEV; | 
 | 118 | 		goto out_free; | 
 | 119 | 	} | 
 | 120 | 	/* Copy data */ | 
 | 121 | 	ret = 0; | 
 | 122 | 	memset(ssd, 0, sizeof(struct chsc_ssd_info)); | 
| Cornelia Huck | b279a4f | 2008-01-26 14:10:45 +0100 | [diff] [blame] | 123 | 	if ((ssd_area->st != SUBCHANNEL_TYPE_IO) && | 
 | 124 | 	    (ssd_area->st != SUBCHANNEL_TYPE_MSG)) | 
| Peter Oberparleiter | 7ad6a24 | 2007-04-27 16:01:35 +0200 | [diff] [blame] | 125 | 		goto out_free; | 
 | 126 | 	ssd->path_mask = ssd_area->path_mask; | 
 | 127 | 	ssd->fla_valid_mask = ssd_area->fla_valid_mask; | 
 | 128 | 	for (i = 0; i < 8; i++) { | 
 | 129 | 		mask = 0x80 >> i; | 
 | 130 | 		if (ssd_area->path_mask & mask) { | 
 | 131 | 			chp_id_init(&ssd->chpid[i]); | 
 | 132 | 			ssd->chpid[i].id = ssd_area->chpid[i]; | 
 | 133 | 		} | 
 | 134 | 		if (ssd_area->fla_valid_mask & mask) | 
 | 135 | 			ssd->fla[i] = ssd_area->fla[i]; | 
 | 136 | 	} | 
 | 137 | out_free: | 
 | 138 | 	free_page(page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 139 | 	return ret; | 
 | 140 | } | 
 | 141 |  | 
| Peter Oberparleiter | e82a156 | 2008-01-26 14:10:48 +0100 | [diff] [blame] | 142 | static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 143 | { | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 144 | 	spin_lock_irq(sch->lock); | 
| Cornelia Huck | c820de3 | 2008-07-14 09:58:45 +0200 | [diff] [blame] | 145 | 	if (sch->driver && sch->driver->chp_event) | 
 | 146 | 		if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 147 | 			goto out_unreg; | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 148 | 	spin_unlock_irq(sch->lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 149 | 	return 0; | 
| Stefan Bader | 387b734 | 2007-04-27 16:01:33 +0200 | [diff] [blame] | 150 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 151 | out_unreg: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 152 | 	sch->lpm = 0; | 
| Stefan Bader | 387b734 | 2007-04-27 16:01:33 +0200 | [diff] [blame] | 153 | 	spin_unlock_irq(sch->lock); | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 154 | 	css_schedule_eval(sch->schid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 155 | 	return 0; | 
 | 156 | } | 
 | 157 |  | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 158 | void chsc_chp_offline(struct chp_id chpid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 159 | { | 
 | 160 | 	char dbf_txt[15]; | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 161 | 	struct chp_link link; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 162 |  | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 163 | 	sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 164 | 	CIO_TRACE_EVENT(2, dbf_txt); | 
 | 165 |  | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 166 | 	if (chp_get_status(chpid) <= 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 167 | 		return; | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 168 | 	memset(&link, 0, sizeof(struct chp_link)); | 
 | 169 | 	link.chpid = chpid; | 
| Cornelia Huck | 22806dc | 2008-04-17 07:45:59 +0200 | [diff] [blame] | 170 | 	/* Wait until previous actions have settled. */ | 
 | 171 | 	css_wait_for_slow_path(); | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 172 | 	for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &link); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 173 | } | 
 | 174 |  | 
| Peter Oberparleiter | e82a156 | 2008-01-26 14:10:48 +0100 | [diff] [blame] | 175 | static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 176 | { | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 177 | 	struct schib schib; | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 178 | 	/* | 
 | 179 | 	 * We don't know the device yet, but since a path | 
 | 180 | 	 * may be available now to the device we'll have | 
 | 181 | 	 * to do recognition again. | 
 | 182 | 	 * Since we don't have any idea about which chpid | 
 | 183 | 	 * that beast may be on we'll have to do a stsch | 
 | 184 | 	 * on all devices, grr... | 
 | 185 | 	 */ | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 186 | 	if (stsch_err(schid, &schib)) | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 187 | 		/* We're through */ | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 188 | 		return -ENXIO; | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 189 |  | 
 | 190 | 	/* Put it on the slow path. */ | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 191 | 	css_schedule_eval(schid); | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 192 | 	return 0; | 
 | 193 | } | 
 | 194 |  | 
| Peter Oberparleiter | e82a156 | 2008-01-26 14:10:48 +0100 | [diff] [blame] | 195 | static int __s390_process_res_acc(struct subchannel *sch, void *data) | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 196 | { | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 197 | 	spin_lock_irq(sch->lock); | 
| Cornelia Huck | c820de3 | 2008-07-14 09:58:45 +0200 | [diff] [blame] | 198 | 	if (sch->driver && sch->driver->chp_event) | 
 | 199 | 		sch->driver->chp_event(sch, data, CHP_ONLINE); | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 200 | 	spin_unlock_irq(sch->lock); | 
| Peter Oberparleiter | e82a156 | 2008-01-26 14:10:48 +0100 | [diff] [blame] | 201 |  | 
| Peter Oberparleiter | dd9963f | 2006-09-20 15:59:54 +0200 | [diff] [blame] | 202 | 	return 0; | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 203 | } | 
 | 204 |  | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 205 | static void s390_process_res_acc(struct chp_link *link) | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 206 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 207 | 	char dbf_txt[15]; | 
 | 208 |  | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 209 | 	sprintf(dbf_txt, "accpr%x.%02x", link->chpid.cssid, | 
 | 210 | 		link->chpid.id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 211 | 	CIO_TRACE_EVENT( 2, dbf_txt); | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 212 | 	if (link->fla != 0) { | 
 | 213 | 		sprintf(dbf_txt, "fla%x", link->fla); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 214 | 		CIO_TRACE_EVENT( 2, dbf_txt); | 
 | 215 | 	} | 
| Cornelia Huck | 22806dc | 2008-04-17 07:45:59 +0200 | [diff] [blame] | 216 | 	/* Wait until previous actions have settled. */ | 
 | 217 | 	css_wait_for_slow_path(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 218 | 	/* | 
 | 219 | 	 * I/O resources may have become accessible. | 
 | 220 | 	 * Scan through all subchannels that may be concerned and | 
 | 221 | 	 * do a validation on those. | 
 | 222 | 	 * The more information we have (info), the less scanning | 
 | 223 | 	 * will we have to do. | 
 | 224 | 	 */ | 
| Peter Oberparleiter | e82a156 | 2008-01-26 14:10:48 +0100 | [diff] [blame] | 225 | 	for_each_subchannel_staged(__s390_process_res_acc, | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 226 | 				   s390_process_res_acc_new_sch, link); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 227 | } | 
 | 228 |  | 
 | 229 | static int | 
 | 230 | __get_chpid_from_lir(void *data) | 
 | 231 | { | 
 | 232 | 	struct lir { | 
 | 233 | 		u8  iq; | 
 | 234 | 		u8  ic; | 
 | 235 | 		u16 sci; | 
 | 236 | 		/* incident-node descriptor */ | 
 | 237 | 		u32 indesc[28]; | 
 | 238 | 		/* attached-node descriptor */ | 
 | 239 | 		u32 andesc[28]; | 
 | 240 | 		/* incident-specific information */ | 
 | 241 | 		u32 isinfo[28]; | 
| Peter Oberparleiter | 0f008aa | 2007-02-05 21:17:40 +0100 | [diff] [blame] | 242 | 	} __attribute__ ((packed)) *lir; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 243 |  | 
| Cornelia Huck | 12975ae | 2006-10-11 15:31:47 +0200 | [diff] [blame] | 244 | 	lir = data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 245 | 	if (!(lir->iq&0x80)) | 
 | 246 | 		/* NULL link incident record */ | 
 | 247 | 		return -EINVAL; | 
 | 248 | 	if (!(lir->indesc[0]&0xc0000000)) | 
 | 249 | 		/* node descriptor not valid */ | 
 | 250 | 		return -EINVAL; | 
 | 251 | 	if (!(lir->indesc[0]&0x10000000)) | 
 | 252 | 		/* don't handle device-type nodes - FIXME */ | 
 | 253 | 		return -EINVAL; | 
 | 254 | 	/* Byte 3 contains the chpid. Could also be CTCA, but we don't care */ | 
 | 255 |  | 
 | 256 | 	return (u16) (lir->indesc[0]&0x000000ff); | 
 | 257 | } | 
 | 258 |  | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 259 | struct chsc_sei_area { | 
 | 260 | 	struct chsc_header request; | 
 | 261 | 	u32 reserved1; | 
 | 262 | 	u32 reserved2; | 
 | 263 | 	u32 reserved3; | 
 | 264 | 	struct chsc_header response; | 
 | 265 | 	u32 reserved4; | 
 | 266 | 	u8  flags; | 
 | 267 | 	u8  vf;		/* validity flags */ | 
 | 268 | 	u8  rs;		/* reporting source */ | 
 | 269 | 	u8  cc;		/* content code */ | 
 | 270 | 	u16 fla;	/* full link address */ | 
 | 271 | 	u16 rsid;	/* reporting source id */ | 
 | 272 | 	u32 reserved5; | 
 | 273 | 	u32 reserved6; | 
 | 274 | 	u8 ccdf[4096 - 16 - 24];	/* content-code dependent field */ | 
 | 275 | 	/* ccdf has to be big enough for a link-incident record */ | 
 | 276 | } __attribute__ ((packed)); | 
 | 277 |  | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 278 | static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 279 | { | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 280 | 	struct chp_id chpid; | 
 | 281 | 	int id; | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 282 |  | 
 | 283 | 	CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n", | 
 | 284 | 		      sei_area->rs, sei_area->rsid); | 
 | 285 | 	if (sei_area->rs != 4) | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 286 | 		return; | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 287 | 	id = __get_chpid_from_lir(sei_area->ccdf); | 
 | 288 | 	if (id < 0) | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 289 | 		CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n"); | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 290 | 	else { | 
 | 291 | 		chp_id_init(&chpid); | 
 | 292 | 		chpid.id = id; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 293 | 		chsc_chp_offline(chpid); | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 294 | 	} | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 295 | } | 
 | 296 |  | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 297 | static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 298 | { | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 299 | 	struct chp_link link; | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 300 | 	struct chp_id chpid; | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 301 | 	int status; | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 302 |  | 
 | 303 | 	CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, " | 
 | 304 | 		      "rs_id=%04x)\n", sei_area->rs, sei_area->rsid); | 
 | 305 | 	if (sei_area->rs != 4) | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 306 | 		return; | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 307 | 	chp_id_init(&chpid); | 
 | 308 | 	chpid.id = sei_area->rsid; | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 309 | 	/* allocate a new channel path structure, if needed */ | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 310 | 	status = chp_get_status(chpid); | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 311 | 	if (status < 0) | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 312 | 		chp_new(chpid); | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 313 | 	else if (!status) | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 314 | 		return; | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 315 | 	memset(&link, 0, sizeof(struct chp_link)); | 
 | 316 | 	link.chpid = chpid; | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 317 | 	if ((sei_area->vf & 0xc0) != 0) { | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 318 | 		link.fla = sei_area->fla; | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 319 | 		if ((sei_area->vf & 0xc0) == 0xc0) | 
 | 320 | 			/* full link address */ | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 321 | 			link.fla_mask = 0xffff; | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 322 | 		else | 
 | 323 | 			/* link address */ | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 324 | 			link.fla_mask = 0xff00; | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 325 | 	} | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 326 | 	s390_process_res_acc(&link); | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 327 | } | 
 | 328 |  | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 329 | struct chp_config_data { | 
 | 330 | 	u8 map[32]; | 
 | 331 | 	u8 op; | 
 | 332 | 	u8 pc; | 
 | 333 | }; | 
 | 334 |  | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 335 | static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 336 | { | 
 | 337 | 	struct chp_config_data *data; | 
 | 338 | 	struct chp_id chpid; | 
 | 339 | 	int num; | 
| Michael Ernst | e6d5a42 | 2008-12-25 13:39:36 +0100 | [diff] [blame] | 340 | 	char *events[3] = {"configure", "deconfigure", "cancel deconfigure"}; | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 341 |  | 
 | 342 | 	CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n"); | 
 | 343 | 	if (sei_area->rs != 0) | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 344 | 		return; | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 345 | 	data = (struct chp_config_data *) &(sei_area->ccdf); | 
 | 346 | 	chp_id_init(&chpid); | 
 | 347 | 	for (num = 0; num <= __MAX_CHPID; num++) { | 
 | 348 | 		if (!chp_test_bit(data->map, num)) | 
 | 349 | 			continue; | 
 | 350 | 		chpid.id = num; | 
| Michael Ernst | e6d5a42 | 2008-12-25 13:39:36 +0100 | [diff] [blame] | 351 | 		pr_notice("Processing %s for channel path %x.%02x\n", | 
 | 352 | 			  events[data->op], chpid.cssid, chpid.id); | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 353 | 		switch (data->op) { | 
 | 354 | 		case 0: | 
 | 355 | 			chp_cfg_schedule(chpid, 1); | 
 | 356 | 			break; | 
 | 357 | 		case 1: | 
 | 358 | 			chp_cfg_schedule(chpid, 0); | 
 | 359 | 			break; | 
 | 360 | 		case 2: | 
 | 361 | 			chp_cfg_cancel_deconfigure(chpid); | 
 | 362 | 			break; | 
 | 363 | 		} | 
 | 364 | 	} | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 365 | } | 
 | 366 |  | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 367 | static void chsc_process_sei(struct chsc_sei_area *sei_area) | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 368 | { | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 369 | 	/* Check if we might have lost some information. */ | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 370 | 	if (sei_area->flags & 0x40) { | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 371 | 		CIO_CRW_EVENT(2, "chsc: event overflow\n"); | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 372 | 		css_schedule_eval_all(); | 
 | 373 | 	} | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 374 | 	/* which kind of information was stored? */ | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 375 | 	switch (sei_area->cc) { | 
 | 376 | 	case 1: /* link incident*/ | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 377 | 		chsc_process_sei_link_incident(sei_area); | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 378 | 		break; | 
 | 379 | 	case 2: /* i/o resource accessibiliy */ | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 380 | 		chsc_process_sei_res_acc(sei_area); | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 381 | 		break; | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 382 | 	case 8: /* channel-path-configuration notification */ | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 383 | 		chsc_process_sei_chp_config(sei_area); | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 384 | 		break; | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 385 | 	default: /* other stuff */ | 
 | 386 | 		CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", | 
 | 387 | 			      sei_area->cc); | 
 | 388 | 		break; | 
 | 389 | 	} | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 390 | } | 
 | 391 |  | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 392 | static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 393 | { | 
 | 394 | 	struct chsc_sei_area *sei_area; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 395 |  | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 396 | 	if (overflow) { | 
 | 397 | 		css_schedule_eval_all(); | 
 | 398 | 		return; | 
 | 399 | 	} | 
 | 400 | 	CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, " | 
 | 401 | 		      "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", | 
 | 402 | 		      crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc, | 
 | 403 | 		      crw0->erc, crw0->rsid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 404 | 	if (!sei_page) | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 405 | 		return; | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 406 | 	/* Access to sei_page is serialized through machine check handler | 
 | 407 | 	 * thread, so no need for locking. */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 408 | 	sei_area = sei_page; | 
 | 409 |  | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 410 | 	CIO_TRACE_EVENT(2, "prcss"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 411 | 	do { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 412 | 		memset(sei_area, 0, sizeof(*sei_area)); | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 413 | 		sei_area->request.length = 0x0010; | 
 | 414 | 		sei_area->request.code = 0x000e; | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 415 | 		if (chsc(sei_area)) | 
 | 416 | 			break; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 417 |  | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 418 | 		if (sei_area->response.code == 0x0001) { | 
 | 419 | 			CIO_CRW_EVENT(4, "chsc: sei successful\n"); | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 420 | 			chsc_process_sei(sei_area); | 
| Peter Oberparleiter | 184357a | 2007-02-05 21:17:42 +0100 | [diff] [blame] | 421 | 		} else { | 
 | 422 | 			CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 423 | 				      sei_area->response.code); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 424 | 			break; | 
 | 425 | 		} | 
 | 426 | 	} while (sei_area->flags & 0x80); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 427 | } | 
 | 428 |  | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 429 | void chsc_chp_online(struct chp_id chpid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 430 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 431 | 	char dbf_txt[15]; | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 432 | 	struct chp_link link; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 433 |  | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 434 | 	sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 435 | 	CIO_TRACE_EVENT(2, dbf_txt); | 
 | 436 |  | 
| Cornelia Huck | 22806dc | 2008-04-17 07:45:59 +0200 | [diff] [blame] | 437 | 	if (chp_get_status(chpid) != 0) { | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 438 | 		memset(&link, 0, sizeof(struct chp_link)); | 
 | 439 | 		link.chpid = chpid; | 
| Cornelia Huck | 22806dc | 2008-04-17 07:45:59 +0200 | [diff] [blame] | 440 | 		/* Wait until previous actions have settled. */ | 
 | 441 | 		css_wait_for_slow_path(); | 
| Cornelia Huck | c820de3 | 2008-07-14 09:58:45 +0200 | [diff] [blame] | 442 | 		for_each_subchannel_staged(__s390_process_res_acc, NULL, | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 443 | 					   &link); | 
| Cornelia Huck | 22806dc | 2008-04-17 07:45:59 +0200 | [diff] [blame] | 444 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 445 | } | 
 | 446 |  | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 447 | static void __s390_subchannel_vary_chpid(struct subchannel *sch, | 
 | 448 | 					 struct chp_id chpid, int on) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 449 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 450 | 	unsigned long flags; | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 451 | 	struct chp_link link; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 452 |  | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 453 | 	memset(&link, 0, sizeof(struct chp_link)); | 
 | 454 | 	link.chpid = chpid; | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 455 | 	spin_lock_irqsave(sch->lock, flags); | 
| Cornelia Huck | c820de3 | 2008-07-14 09:58:45 +0200 | [diff] [blame] | 456 | 	if (sch->driver && sch->driver->chp_event) | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 457 | 		sch->driver->chp_event(sch, &link, | 
| Cornelia Huck | c820de3 | 2008-07-14 09:58:45 +0200 | [diff] [blame] | 458 | 				       on ? CHP_VARY_ON : CHP_VARY_OFF); | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 459 | 	spin_unlock_irqrestore(sch->lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 460 | } | 
 | 461 |  | 
| Peter Oberparleiter | e82a156 | 2008-01-26 14:10:48 +0100 | [diff] [blame] | 462 | static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 463 | { | 
| Peter Oberparleiter | e82a156 | 2008-01-26 14:10:48 +0100 | [diff] [blame] | 464 | 	struct chp_id *chpid = data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 465 |  | 
 | 466 | 	__s390_subchannel_vary_chpid(sch, *chpid, 0); | 
 | 467 | 	return 0; | 
 | 468 | } | 
 | 469 |  | 
| Peter Oberparleiter | e82a156 | 2008-01-26 14:10:48 +0100 | [diff] [blame] | 470 | static int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 471 | { | 
| Peter Oberparleiter | e82a156 | 2008-01-26 14:10:48 +0100 | [diff] [blame] | 472 | 	struct chp_id *chpid = data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 473 |  | 
 | 474 | 	__s390_subchannel_vary_chpid(sch, *chpid, 1); | 
 | 475 | 	return 0; | 
 | 476 | } | 
 | 477 |  | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 478 | static int | 
 | 479 | __s390_vary_chpid_on(struct subchannel_id schid, void *data) | 
 | 480 | { | 
 | 481 | 	struct schib schib; | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 482 |  | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 483 | 	if (stsch_err(schid, &schib)) | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 484 | 		/* We're through */ | 
 | 485 | 		return -ENXIO; | 
 | 486 | 	/* Put it on the slow path. */ | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 487 | 	css_schedule_eval(schid); | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 488 | 	return 0; | 
 | 489 | } | 
 | 490 |  | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 491 | /** | 
 | 492 |  * chsc_chp_vary - propagate channel-path vary operation to subchannels | 
 | 493 |  * @chpid: channl-path ID | 
 | 494 |  * @on: non-zero for vary online, zero for vary offline | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 495 |  */ | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 496 | int chsc_chp_vary(struct chp_id chpid, int on) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 497 | { | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 498 | 	struct chp_link link; | 
 | 499 |  | 
 | 500 | 	memset(&link, 0, sizeof(struct chp_link)); | 
 | 501 | 	link.chpid = chpid; | 
| Cornelia Huck | 22806dc | 2008-04-17 07:45:59 +0200 | [diff] [blame] | 502 | 	/* Wait until previous actions have settled. */ | 
 | 503 | 	css_wait_for_slow_path(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 504 | 	/* | 
 | 505 | 	 * Redo PathVerification on the devices the chpid connects to | 
 | 506 | 	 */ | 
 | 507 |  | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 508 | 	if (on) | 
| Peter Oberparleiter | e82a156 | 2008-01-26 14:10:48 +0100 | [diff] [blame] | 509 | 		for_each_subchannel_staged(s390_subchannel_vary_chpid_on, | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 510 | 					   __s390_vary_chpid_on, &link); | 
| Peter Oberparleiter | e82a156 | 2008-01-26 14:10:48 +0100 | [diff] [blame] | 511 | 	else | 
 | 512 | 		for_each_subchannel_staged(s390_subchannel_vary_chpid_off, | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 513 | 					   NULL, &link); | 
| Peter Oberparleiter | e82a156 | 2008-01-26 14:10:48 +0100 | [diff] [blame] | 514 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 515 | 	return 0; | 
 | 516 | } | 
 | 517 |  | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 518 | static void | 
 | 519 | chsc_remove_cmg_attr(struct channel_subsystem *css) | 
 | 520 | { | 
 | 521 | 	int i; | 
 | 522 |  | 
 | 523 | 	for (i = 0; i <= __MAX_CHPID; i++) { | 
 | 524 | 		if (!css->chps[i]) | 
 | 525 | 			continue; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 526 | 		chp_remove_cmg_attr(css->chps[i]); | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 527 | 	} | 
 | 528 | } | 
 | 529 |  | 
 | 530 | static int | 
 | 531 | chsc_add_cmg_attr(struct channel_subsystem *css) | 
 | 532 | { | 
 | 533 | 	int i, ret; | 
 | 534 |  | 
 | 535 | 	ret = 0; | 
 | 536 | 	for (i = 0; i <= __MAX_CHPID; i++) { | 
 | 537 | 		if (!css->chps[i]) | 
 | 538 | 			continue; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 539 | 		ret = chp_add_cmg_attr(css->chps[i]); | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 540 | 		if (ret) | 
 | 541 | 			goto cleanup; | 
 | 542 | 	} | 
 | 543 | 	return ret; | 
 | 544 | cleanup: | 
 | 545 | 	for (--i; i >= 0; i--) { | 
 | 546 | 		if (!css->chps[i]) | 
 | 547 | 			continue; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 548 | 		chp_remove_cmg_attr(css->chps[i]); | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 549 | 	} | 
 | 550 | 	return ret; | 
 | 551 | } | 
 | 552 |  | 
| Sebastian Ott | dcbd16d | 2009-06-16 10:30:22 +0200 | [diff] [blame] | 553 | int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 554 | { | 
 | 555 | 	struct { | 
 | 556 | 		struct chsc_header request; | 
 | 557 | 		u32 operation_code : 2; | 
 | 558 | 		u32 : 30; | 
 | 559 | 		u32 key : 4; | 
 | 560 | 		u32 : 28; | 
 | 561 | 		u32 zeroes1; | 
 | 562 | 		u32 cub_addr1; | 
 | 563 | 		u32 zeroes2; | 
 | 564 | 		u32 cub_addr2; | 
 | 565 | 		u32 reserved[13]; | 
 | 566 | 		struct chsc_header response; | 
 | 567 | 		u32 status : 8; | 
 | 568 | 		u32 : 4; | 
 | 569 | 		u32 fmt : 4; | 
 | 570 | 		u32 : 16; | 
| Peter Oberparleiter | 0f008aa | 2007-02-05 21:17:40 +0100 | [diff] [blame] | 571 | 	} __attribute__ ((packed)) *secm_area; | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 572 | 	int ret, ccode; | 
 | 573 |  | 
 | 574 | 	secm_area = page; | 
 | 575 | 	secm_area->request.length = 0x0050; | 
 | 576 | 	secm_area->request.code = 0x0016; | 
 | 577 |  | 
| Heiko Carstens | d1bf859 | 2010-02-26 22:37:30 +0100 | [diff] [blame] | 578 | 	secm_area->key = PAGE_DEFAULT_KEY >> 4; | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 579 | 	secm_area->cub_addr1 = (u64)(unsigned long)css->cub_addr1; | 
 | 580 | 	secm_area->cub_addr2 = (u64)(unsigned long)css->cub_addr2; | 
 | 581 |  | 
 | 582 | 	secm_area->operation_code = enable ? 0 : 1; | 
 | 583 |  | 
 | 584 | 	ccode = chsc(secm_area); | 
 | 585 | 	if (ccode > 0) | 
 | 586 | 		return (ccode == 3) ? -ENODEV : -EBUSY; | 
 | 587 |  | 
 | 588 | 	switch (secm_area->response.code) { | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 589 | 	case 0x0102: | 
 | 590 | 	case 0x0103: | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 591 | 		ret = -EINVAL; | 
| Sebastian Ott | 17e7d87 | 2009-03-26 15:24:17 +0100 | [diff] [blame] | 592 | 		break; | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 593 | 	default: | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 594 | 		ret = chsc_error_from_response(secm_area->response.code); | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 595 | 	} | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 596 | 	if (ret != 0) | 
 | 597 | 		CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n", | 
 | 598 | 			      secm_area->response.code); | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 599 | 	return ret; | 
 | 600 | } | 
 | 601 |  | 
 | 602 | int | 
 | 603 | chsc_secm(struct channel_subsystem *css, int enable) | 
 | 604 | { | 
 | 605 | 	void  *secm_area; | 
 | 606 | 	int ret; | 
 | 607 |  | 
 | 608 | 	secm_area = (void *)get_zeroed_page(GFP_KERNEL |  GFP_DMA); | 
 | 609 | 	if (!secm_area) | 
 | 610 | 		return -ENOMEM; | 
 | 611 |  | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 612 | 	if (enable && !css->cm_enabled) { | 
 | 613 | 		css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 
 | 614 | 		css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 
 | 615 | 		if (!css->cub_addr1 || !css->cub_addr2) { | 
 | 616 | 			free_page((unsigned long)css->cub_addr1); | 
 | 617 | 			free_page((unsigned long)css->cub_addr2); | 
 | 618 | 			free_page((unsigned long)secm_area); | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 619 | 			return -ENOMEM; | 
 | 620 | 		} | 
 | 621 | 	} | 
 | 622 | 	ret = __chsc_do_secm(css, enable, secm_area); | 
 | 623 | 	if (!ret) { | 
 | 624 | 		css->cm_enabled = enable; | 
 | 625 | 		if (css->cm_enabled) { | 
 | 626 | 			ret = chsc_add_cmg_attr(css); | 
 | 627 | 			if (ret) { | 
 | 628 | 				memset(secm_area, 0, PAGE_SIZE); | 
 | 629 | 				__chsc_do_secm(css, 0, secm_area); | 
 | 630 | 				css->cm_enabled = 0; | 
 | 631 | 			} | 
 | 632 | 		} else | 
 | 633 | 			chsc_remove_cmg_attr(css); | 
 | 634 | 	} | 
| Cornelia Huck | 8c4941c | 2007-04-27 16:01:38 +0200 | [diff] [blame] | 635 | 	if (!css->cm_enabled) { | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 636 | 		free_page((unsigned long)css->cub_addr1); | 
 | 637 | 		free_page((unsigned long)css->cub_addr2); | 
 | 638 | 	} | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 639 | 	free_page((unsigned long)secm_area); | 
 | 640 | 	return ret; | 
 | 641 | } | 
 | 642 |  | 
| Cornelia Huck | 9d92a7e | 2008-07-14 09:59:05 +0200 | [diff] [blame] | 643 | int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, | 
 | 644 | 				     int c, int m, | 
 | 645 | 				     struct chsc_response_struct *resp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 646 | { | 
 | 647 | 	int ccode, ret; | 
 | 648 |  | 
 | 649 | 	struct { | 
 | 650 | 		struct chsc_header request; | 
| Cornelia Huck | 9d92a7e | 2008-07-14 09:59:05 +0200 | [diff] [blame] | 651 | 		u32 : 2; | 
 | 652 | 		u32 m : 1; | 
 | 653 | 		u32 c : 1; | 
 | 654 | 		u32 fmt : 4; | 
 | 655 | 		u32 cssid : 8; | 
 | 656 | 		u32 : 4; | 
 | 657 | 		u32 rfmt : 4; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 658 | 		u32 first_chpid : 8; | 
 | 659 | 		u32 : 24; | 
 | 660 | 		u32 last_chpid : 8; | 
 | 661 | 		u32 zeroes1; | 
 | 662 | 		struct chsc_header response; | 
| Cornelia Huck | 9d92a7e | 2008-07-14 09:59:05 +0200 | [diff] [blame] | 663 | 		u8 data[PAGE_SIZE - 20]; | 
| Peter Oberparleiter | 0f008aa | 2007-02-05 21:17:40 +0100 | [diff] [blame] | 664 | 	} __attribute__ ((packed)) *scpd_area; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 665 |  | 
| Cornelia Huck | 9d92a7e | 2008-07-14 09:59:05 +0200 | [diff] [blame] | 666 | 	if ((rfmt == 1) && !css_general_characteristics.fcs) | 
 | 667 | 		return -EINVAL; | 
 | 668 | 	if ((rfmt == 2) && !css_general_characteristics.cib) | 
 | 669 | 		return -EINVAL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 670 | 	scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 
 | 671 | 	if (!scpd_area) | 
 | 672 | 		return -ENOMEM; | 
 | 673 |  | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 674 | 	scpd_area->request.length = 0x0010; | 
 | 675 | 	scpd_area->request.code = 0x0002; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 676 |  | 
| Cornelia Huck | 9d92a7e | 2008-07-14 09:59:05 +0200 | [diff] [blame] | 677 | 	scpd_area->cssid = chpid.cssid; | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 678 | 	scpd_area->first_chpid = chpid.id; | 
 | 679 | 	scpd_area->last_chpid = chpid.id; | 
| Cornelia Huck | 9d92a7e | 2008-07-14 09:59:05 +0200 | [diff] [blame] | 680 | 	scpd_area->m = m; | 
 | 681 | 	scpd_area->c = c; | 
 | 682 | 	scpd_area->fmt = fmt; | 
 | 683 | 	scpd_area->rfmt = rfmt; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 684 |  | 
 | 685 | 	ccode = chsc(scpd_area); | 
 | 686 | 	if (ccode > 0) { | 
 | 687 | 		ret = (ccode == 3) ? -ENODEV : -EBUSY; | 
 | 688 | 		goto out; | 
 | 689 | 	} | 
 | 690 |  | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 691 | 	ret = chsc_error_from_response(scpd_area->response.code); | 
 | 692 | 	if (ret == 0) | 
 | 693 | 		/* Success. */ | 
| Cornelia Huck | 9d92a7e | 2008-07-14 09:59:05 +0200 | [diff] [blame] | 694 | 		memcpy(resp, &scpd_area->response, scpd_area->response.length); | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 695 | 	else | 
 | 696 | 		CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 697 | 			      scpd_area->response.code); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 698 | out: | 
 | 699 | 	free_page((unsigned long)scpd_area); | 
 | 700 | 	return ret; | 
 | 701 | } | 
| Cornelia Huck | 9d92a7e | 2008-07-14 09:59:05 +0200 | [diff] [blame] | 702 | EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc); | 
 | 703 |  | 
 | 704 | int chsc_determine_base_channel_path_desc(struct chp_id chpid, | 
 | 705 | 					  struct channel_path_desc *desc) | 
 | 706 | { | 
 | 707 | 	struct chsc_response_struct *chsc_resp; | 
 | 708 | 	int ret; | 
 | 709 |  | 
 | 710 | 	chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL); | 
 | 711 | 	if (!chsc_resp) | 
 | 712 | 		return -ENOMEM; | 
 | 713 | 	ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, chsc_resp); | 
 | 714 | 	if (ret) | 
 | 715 | 		goto out_free; | 
 | 716 | 	memcpy(desc, &chsc_resp->data, chsc_resp->length); | 
 | 717 | out_free: | 
 | 718 | 	kfree(chsc_resp); | 
 | 719 | 	return ret; | 
 | 720 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 721 |  | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 722 | static void | 
 | 723 | chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, | 
 | 724 | 			  struct cmg_chars *chars) | 
 | 725 | { | 
 | 726 | 	switch (chp->cmg) { | 
 | 727 | 	case 2: | 
 | 728 | 	case 3: | 
 | 729 | 		chp->cmg_chars = kmalloc(sizeof(struct cmg_chars), | 
 | 730 | 					 GFP_KERNEL); | 
 | 731 | 		if (chp->cmg_chars) { | 
 | 732 | 			int i, mask; | 
 | 733 | 			struct cmg_chars *cmg_chars; | 
 | 734 |  | 
 | 735 | 			cmg_chars = chp->cmg_chars; | 
 | 736 | 			for (i = 0; i < NR_MEASUREMENT_CHARS; i++) { | 
 | 737 | 				mask = 0x80 >> (i + 3); | 
 | 738 | 				if (cmcv & mask) | 
 | 739 | 					cmg_chars->values[i] = chars->values[i]; | 
 | 740 | 				else | 
 | 741 | 					cmg_chars->values[i] = 0; | 
 | 742 | 			} | 
 | 743 | 		} | 
 | 744 | 		break; | 
 | 745 | 	default: | 
 | 746 | 		/* No cmg-dependent data. */ | 
 | 747 | 		break; | 
 | 748 | 	} | 
 | 749 | } | 
 | 750 |  | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 751 | int chsc_get_channel_measurement_chars(struct channel_path *chp) | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 752 | { | 
 | 753 | 	int ccode, ret; | 
 | 754 |  | 
 | 755 | 	struct { | 
 | 756 | 		struct chsc_header request; | 
 | 757 | 		u32 : 24; | 
 | 758 | 		u32 first_chpid : 8; | 
 | 759 | 		u32 : 24; | 
 | 760 | 		u32 last_chpid : 8; | 
 | 761 | 		u32 zeroes1; | 
 | 762 | 		struct chsc_header response; | 
 | 763 | 		u32 zeroes2; | 
 | 764 | 		u32 not_valid : 1; | 
 | 765 | 		u32 shared : 1; | 
 | 766 | 		u32 : 22; | 
 | 767 | 		u32 chpid : 8; | 
 | 768 | 		u32 cmcv : 5; | 
 | 769 | 		u32 : 11; | 
 | 770 | 		u32 cmgq : 8; | 
 | 771 | 		u32 cmg : 8; | 
 | 772 | 		u32 zeroes3; | 
 | 773 | 		u32 data[NR_MEASUREMENT_CHARS]; | 
| Peter Oberparleiter | 0f008aa | 2007-02-05 21:17:40 +0100 | [diff] [blame] | 774 | 	} __attribute__ ((packed)) *scmc_area; | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 775 |  | 
 | 776 | 	scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 
 | 777 | 	if (!scmc_area) | 
 | 778 | 		return -ENOMEM; | 
 | 779 |  | 
 | 780 | 	scmc_area->request.length = 0x0010; | 
 | 781 | 	scmc_area->request.code = 0x0022; | 
 | 782 |  | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 783 | 	scmc_area->first_chpid = chp->chpid.id; | 
 | 784 | 	scmc_area->last_chpid = chp->chpid.id; | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 785 |  | 
 | 786 | 	ccode = chsc(scmc_area); | 
 | 787 | 	if (ccode > 0) { | 
 | 788 | 		ret = (ccode == 3) ? -ENODEV : -EBUSY; | 
 | 789 | 		goto out; | 
 | 790 | 	} | 
 | 791 |  | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 792 | 	ret = chsc_error_from_response(scmc_area->response.code); | 
 | 793 | 	if (ret == 0) { | 
 | 794 | 		/* Success. */ | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 795 | 		if (!scmc_area->not_valid) { | 
 | 796 | 			chp->cmg = scmc_area->cmg; | 
 | 797 | 			chp->shared = scmc_area->shared; | 
 | 798 | 			chsc_initialize_cmg_chars(chp, scmc_area->cmcv, | 
 | 799 | 						  (struct cmg_chars *) | 
 | 800 | 						  &scmc_area->data); | 
 | 801 | 		} else { | 
 | 802 | 			chp->cmg = -1; | 
 | 803 | 			chp->shared = -1; | 
 | 804 | 		} | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 805 | 	} else { | 
 | 806 | 		CIO_CRW_EVENT(2, "chsc: scmc failed (rc=%04x)\n", | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 807 | 			      scmc_area->response.code); | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 808 | 	} | 
 | 809 | out: | 
 | 810 | 	free_page((unsigned long)scmc_area); | 
 | 811 | 	return ret; | 
 | 812 | } | 
 | 813 |  | 
| Cornelia Huck | 4434a38 | 2007-07-27 12:29:21 +0200 | [diff] [blame] | 814 | int __init chsc_alloc_sei_area(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 815 | { | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 816 | 	int ret; | 
 | 817 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 818 | 	sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 819 | 	if (!sei_page) { | 
| Cornelia Huck | e556bbb | 2007-07-27 12:29:19 +0200 | [diff] [blame] | 820 | 		CIO_MSG_EVENT(0, "Can't allocate page for processing of " | 
 | 821 | 			      "chsc machine checks!\n"); | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 822 | 		return -ENOMEM; | 
 | 823 | 	} | 
| Heiko Carstens | f5daba1 | 2009-03-26 15:24:01 +0100 | [diff] [blame] | 824 | 	ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw); | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 825 | 	if (ret) | 
 | 826 | 		kfree(sei_page); | 
 | 827 | 	return ret; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 828 | } | 
 | 829 |  | 
| Cornelia Huck | 4434a38 | 2007-07-27 12:29:21 +0200 | [diff] [blame] | 830 | void __init chsc_free_sei_area(void) | 
 | 831 | { | 
| Heiko Carstens | f5daba1 | 2009-03-26 15:24:01 +0100 | [diff] [blame] | 832 | 	crw_unregister_handler(CRW_RSC_CSS); | 
| Cornelia Huck | 4434a38 | 2007-07-27 12:29:21 +0200 | [diff] [blame] | 833 | 	kfree(sei_page); | 
 | 834 | } | 
 | 835 |  | 
| Sebastian Ott | 818c272 | 2010-04-22 17:17:03 +0200 | [diff] [blame] | 836 | int chsc_enable_facility(int operation_code) | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 837 | { | 
 | 838 | 	int ret; | 
| Sebastian Ott | 818c272 | 2010-04-22 17:17:03 +0200 | [diff] [blame] | 839 | 	static struct { | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 840 | 		struct chsc_header request; | 
 | 841 | 		u8 reserved1:4; | 
 | 842 | 		u8 format:4; | 
 | 843 | 		u8 reserved2; | 
 | 844 | 		u16 operation_code; | 
 | 845 | 		u32 reserved3; | 
 | 846 | 		u32 reserved4; | 
 | 847 | 		u32 operation_data_area[252]; | 
 | 848 | 		struct chsc_header response; | 
 | 849 | 		u32 reserved5:4; | 
 | 850 | 		u32 format2:4; | 
 | 851 | 		u32 reserved6:24; | 
| Sebastian Ott | 818c272 | 2010-04-22 17:17:03 +0200 | [diff] [blame] | 852 | 	} __attribute__ ((packed, aligned(4096))) sda_area; | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 853 |  | 
| Sebastian Ott | 818c272 | 2010-04-22 17:17:03 +0200 | [diff] [blame] | 854 | 	spin_lock(&sda_lock); | 
 | 855 | 	memset(&sda_area, 0, sizeof(sda_area)); | 
 | 856 | 	sda_area.request.length = 0x0400; | 
 | 857 | 	sda_area.request.code = 0x0031; | 
 | 858 | 	sda_area.operation_code = operation_code; | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 859 |  | 
| Sebastian Ott | 818c272 | 2010-04-22 17:17:03 +0200 | [diff] [blame] | 860 | 	ret = chsc(&sda_area); | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 861 | 	if (ret > 0) { | 
 | 862 | 		ret = (ret == 3) ? -ENODEV : -EBUSY; | 
 | 863 | 		goto out; | 
 | 864 | 	} | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 865 |  | 
| Sebastian Ott | 818c272 | 2010-04-22 17:17:03 +0200 | [diff] [blame] | 866 | 	switch (sda_area.response.code) { | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 867 | 	case 0x0101: | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 868 | 		ret = -EOPNOTSUPP; | 
 | 869 | 		break; | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 870 | 	default: | 
| Sebastian Ott | 818c272 | 2010-04-22 17:17:03 +0200 | [diff] [blame] | 871 | 		ret = chsc_error_from_response(sda_area.response.code); | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 872 | 	} | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 873 | 	if (ret != 0) | 
 | 874 | 		CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n", | 
| Sebastian Ott | 818c272 | 2010-04-22 17:17:03 +0200 | [diff] [blame] | 875 | 			      operation_code, sda_area.response.code); | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 876 |  out: | 
| Sebastian Ott | 818c272 | 2010-04-22 17:17:03 +0200 | [diff] [blame] | 877 | 	spin_unlock(&sda_lock); | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 878 | 	return ret; | 
 | 879 | } | 
 | 880 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 881 | struct css_general_char css_general_characteristics; | 
 | 882 | struct css_chsc_char css_chsc_characteristics; | 
 | 883 |  | 
 | 884 | int __init | 
 | 885 | chsc_determine_css_characteristics(void) | 
 | 886 | { | 
 | 887 | 	int result; | 
 | 888 | 	struct { | 
 | 889 | 		struct chsc_header request; | 
 | 890 | 		u32 reserved1; | 
 | 891 | 		u32 reserved2; | 
 | 892 | 		u32 reserved3; | 
 | 893 | 		struct chsc_header response; | 
 | 894 | 		u32 reserved4; | 
 | 895 | 		u32 general_char[510]; | 
 | 896 | 		u32 chsc_char[518]; | 
| Peter Oberparleiter | 0f008aa | 2007-02-05 21:17:40 +0100 | [diff] [blame] | 897 | 	} __attribute__ ((packed)) *scsc_area; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 898 |  | 
 | 899 | 	scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 900 | 	if (!scsc_area) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 901 | 		return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 902 |  | 
| Cornelia Huck | 495a5b4 | 2006-03-24 03:15:14 -0800 | [diff] [blame] | 903 | 	scsc_area->request.length = 0x0010; | 
 | 904 | 	scsc_area->request.code = 0x0010; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 905 |  | 
 | 906 | 	result = chsc(scsc_area); | 
 | 907 | 	if (result) { | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 908 | 		result = (result == 3) ? -ENODEV : -EBUSY; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 909 | 		goto exit; | 
 | 910 | 	} | 
 | 911 |  | 
| Cornelia Huck | b9c9a21 | 2008-02-05 16:50:34 +0100 | [diff] [blame] | 912 | 	result = chsc_error_from_response(scsc_area->response.code); | 
 | 913 | 	if (result == 0) { | 
 | 914 | 		memcpy(&css_general_characteristics, scsc_area->general_char, | 
 | 915 | 		       sizeof(css_general_characteristics)); | 
 | 916 | 		memcpy(&css_chsc_characteristics, scsc_area->chsc_char, | 
 | 917 | 		       sizeof(css_chsc_characteristics)); | 
 | 918 | 	} else | 
 | 919 | 		CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n", | 
 | 920 | 			      scsc_area->response.code); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 921 | exit: | 
 | 922 | 	free_page ((unsigned long) scsc_area); | 
 | 923 | 	return result; | 
 | 924 | } | 
 | 925 |  | 
 | 926 | EXPORT_SYMBOL_GPL(css_general_characteristics); | 
 | 927 | EXPORT_SYMBOL_GPL(css_chsc_characteristics); | 
| Martin Schwidefsky | d2fec59 | 2008-07-14 09:58:56 +0200 | [diff] [blame] | 928 |  | 
 | 929 | int chsc_sstpc(void *page, unsigned int op, u16 ctrl) | 
 | 930 | { | 
 | 931 | 	struct { | 
 | 932 | 		struct chsc_header request; | 
 | 933 | 		unsigned int rsvd0; | 
 | 934 | 		unsigned int op : 8; | 
 | 935 | 		unsigned int rsvd1 : 8; | 
 | 936 | 		unsigned int ctrl : 16; | 
 | 937 | 		unsigned int rsvd2[5]; | 
 | 938 | 		struct chsc_header response; | 
 | 939 | 		unsigned int rsvd3[7]; | 
 | 940 | 	} __attribute__ ((packed)) *rr; | 
 | 941 | 	int rc; | 
 | 942 |  | 
 | 943 | 	memset(page, 0, PAGE_SIZE); | 
 | 944 | 	rr = page; | 
 | 945 | 	rr->request.length = 0x0020; | 
 | 946 | 	rr->request.code = 0x0033; | 
 | 947 | 	rr->op = op; | 
 | 948 | 	rr->ctrl = ctrl; | 
 | 949 | 	rc = chsc(rr); | 
 | 950 | 	if (rc) | 
 | 951 | 		return -EIO; | 
 | 952 | 	rc = (rr->response.code == 0x0001) ? 0 : -EIO; | 
 | 953 | 	return rc; | 
 | 954 | } | 
 | 955 |  | 
 | 956 | int chsc_sstpi(void *page, void *result, size_t size) | 
 | 957 | { | 
 | 958 | 	struct { | 
 | 959 | 		struct chsc_header request; | 
 | 960 | 		unsigned int rsvd0[3]; | 
 | 961 | 		struct chsc_header response; | 
 | 962 | 		char data[size]; | 
 | 963 | 	} __attribute__ ((packed)) *rr; | 
 | 964 | 	int rc; | 
 | 965 |  | 
 | 966 | 	memset(page, 0, PAGE_SIZE); | 
 | 967 | 	rr = page; | 
 | 968 | 	rr->request.length = 0x0010; | 
 | 969 | 	rr->request.code = 0x0038; | 
 | 970 | 	rc = chsc(rr); | 
 | 971 | 	if (rc) | 
 | 972 | 		return -EIO; | 
 | 973 | 	memcpy(result, &rr->data, size); | 
 | 974 | 	return (rr->response.code == 0x0001) ? 0 : -EIO; | 
 | 975 | } | 
 | 976 |  |