blob: 62b0b16fe3d3e60405fef99f5c28acfbfaaa7a10 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/s390/cio/chsc.c
3 * S/390 common I/O routines -- channel subsystem call
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Cornelia Huckc820de32008-07-14 09:58:45 +02005 * Copyright IBM Corp. 1999,2008
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * Author(s): Ingo Adlung (adlung@de.ibm.com)
Cornelia Huck4ce3b302006-01-14 13:21:04 -08007 * Cornelia Huck (cornelia.huck@de.ibm.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * Arnd Bergmann (arndb@de.ibm.com)
9 */
10
11#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/slab.h>
13#include <linux/init.h>
14#include <linux/device.h>
15
16#include <asm/cio.h>
Peter Oberparleitere5854a52007-04-27 16:01:31 +020017#include <asm/chpid.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018
Cornelia Huckc1156182008-07-14 09:58:46 +020019#include "../s390mach.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include "css.h"
21#include "cio.h"
22#include "cio_debug.h"
23#include "ioasm.h"
Peter Oberparleitere6b6e102007-04-27 16:01:28 +020024#include "chp.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include "chsc.h"
26
Linus Torvalds1da177e2005-04-16 15:20:36 -070027static void *sei_page;
28
Cornelia Huckb9c9a212008-02-05 16:50:34 +010029static int chsc_error_from_response(int response)
30{
31 switch (response) {
32 case 0x0001:
33 return 0;
34 case 0x0002:
35 case 0x0003:
36 case 0x0006:
37 case 0x0007:
38 case 0x0008:
39 case 0x000a:
40 return -EINVAL;
41 case 0x0004:
42 return -EOPNOTSUPP;
43 default:
44 return -EIO;
45 }
46}
47
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020048struct chsc_ssd_area {
49 struct chsc_header request;
50 u16 :10;
51 u16 ssid:2;
52 u16 :4;
53 u16 f_sch; /* first subchannel */
54 u16 :16;
55 u16 l_sch; /* last subchannel */
56 u32 :32;
57 struct chsc_header response;
58 u32 :32;
59 u8 sch_valid : 1;
60 u8 dev_valid : 1;
61 u8 st : 3; /* subchannel type */
62 u8 zeroes : 3;
63 u8 unit_addr; /* unit address */
64 u16 devno; /* device number */
65 u8 path_mask;
66 u8 fla_valid_mask;
67 u16 sch; /* subchannel */
68 u8 chpid[8]; /* chpids 0-7 */
69 u16 fla[8]; /* full link addresses 0-7 */
70} __attribute__ ((packed));
71
72int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
Linus Torvalds1da177e2005-04-16 15:20:36 -070073{
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020074 unsigned long page;
75 struct chsc_ssd_area *ssd_area;
76 int ccode;
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 int ret;
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020078 int i;
79 int mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020081 page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 if (!page)
83 return -ENOMEM;
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020084 ssd_area = (struct chsc_ssd_area *) page;
85 ssd_area->request.length = 0x0010;
86 ssd_area->request.code = 0x0004;
87 ssd_area->ssid = schid.ssid;
88 ssd_area->f_sch = schid.sch_no;
89 ssd_area->l_sch = schid.sch_no;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +020090
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020091 ccode = chsc(ssd_area);
92 /* Check response. */
93 if (ccode > 0) {
94 ret = (ccode == 3) ? -ENODEV : -EBUSY;
95 goto out_free;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 }
Cornelia Huckb9c9a212008-02-05 16:50:34 +010097 ret = chsc_error_from_response(ssd_area->response.code);
98 if (ret != 0) {
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020099 CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n",
100 schid.ssid, schid.sch_no,
101 ssd_area->response.code);
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200102 goto out_free;
103 }
104 if (!ssd_area->sch_valid) {
105 ret = -ENODEV;
106 goto out_free;
107 }
108 /* Copy data */
109 ret = 0;
110 memset(ssd, 0, sizeof(struct chsc_ssd_info));
Cornelia Huckb279a4f2008-01-26 14:10:45 +0100111 if ((ssd_area->st != SUBCHANNEL_TYPE_IO) &&
112 (ssd_area->st != SUBCHANNEL_TYPE_MSG))
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200113 goto out_free;
114 ssd->path_mask = ssd_area->path_mask;
115 ssd->fla_valid_mask = ssd_area->fla_valid_mask;
116 for (i = 0; i < 8; i++) {
117 mask = 0x80 >> i;
118 if (ssd_area->path_mask & mask) {
119 chp_id_init(&ssd->chpid[i]);
120 ssd->chpid[i].id = ssd_area->chpid[i];
121 }
122 if (ssd_area->fla_valid_mask & mask)
123 ssd->fla[i] = ssd_area->fla[i];
124 }
125out_free:
126 free_page(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 return ret;
128}
129
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100130static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131{
Cornelia Huck2ec22982006-12-08 15:54:26 +0100132 spin_lock_irq(sch->lock);
Cornelia Huckc820de32008-07-14 09:58:45 +0200133 if (sch->driver && sch->driver->chp_event)
134 if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 goto out_unreg;
Cornelia Huck2ec22982006-12-08 15:54:26 +0100136 spin_unlock_irq(sch->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 return 0;
Stefan Bader387b7342007-04-27 16:01:33 +0200138
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139out_unreg:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 sch->lpm = 0;
Stefan Bader387b7342007-04-27 16:01:33 +0200141 spin_unlock_irq(sch->lock);
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200142 css_schedule_eval(sch->schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 return 0;
144}
145
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200146void chsc_chp_offline(struct chp_id chpid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147{
148 char dbf_txt[15];
149
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200150 sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 CIO_TRACE_EVENT(2, dbf_txt);
152
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200153 if (chp_get_status(chpid) <= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 return;
Cornelia Huck22806dc2008-04-17 07:45:59 +0200155 /* Wait until previous actions have settled. */
156 css_wait_for_slow_path();
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100157 for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158}
159
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100160static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161{
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800162 struct schib schib;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800163 /*
164 * We don't know the device yet, but since a path
165 * may be available now to the device we'll have
166 * to do recognition again.
167 * Since we don't have any idea about which chpid
168 * that beast may be on we'll have to do a stsch
169 * on all devices, grr...
170 */
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800171 if (stsch_err(schid, &schib))
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800172 /* We're through */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200173 return -ENXIO;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800174
175 /* Put it on the slow path. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200176 css_schedule_eval(schid);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800177 return 0;
178}
179
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100180static int __s390_process_res_acc(struct subchannel *sch, void *data)
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800181{
Cornelia Huck2ec22982006-12-08 15:54:26 +0100182 spin_lock_irq(sch->lock);
Cornelia Huckc820de32008-07-14 09:58:45 +0200183 if (sch->driver && sch->driver->chp_event)
184 sch->driver->chp_event(sch, data, CHP_ONLINE);
Cornelia Huck2ec22982006-12-08 15:54:26 +0100185 spin_unlock_irq(sch->lock);
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100186
Peter Oberparleiterdd9963f2006-09-20 15:59:54 +0200187 return 0;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800188}
189
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200190static void s390_process_res_acc (struct res_acc_data *res_data)
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800191{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 char dbf_txt[15];
193
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200194 sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid,
195 res_data->chpid.id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 CIO_TRACE_EVENT( 2, dbf_txt);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800197 if (res_data->fla != 0) {
198 sprintf(dbf_txt, "fla%x", res_data->fla);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 CIO_TRACE_EVENT( 2, dbf_txt);
200 }
Cornelia Huck22806dc2008-04-17 07:45:59 +0200201 /* Wait until previous actions have settled. */
202 css_wait_for_slow_path();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 /*
204 * I/O resources may have become accessible.
205 * Scan through all subchannels that may be concerned and
206 * do a validation on those.
207 * The more information we have (info), the less scanning
208 * will we have to do.
209 */
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100210 for_each_subchannel_staged(__s390_process_res_acc,
211 s390_process_res_acc_new_sch, res_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212}
213
214static int
215__get_chpid_from_lir(void *data)
216{
217 struct lir {
218 u8 iq;
219 u8 ic;
220 u16 sci;
221 /* incident-node descriptor */
222 u32 indesc[28];
223 /* attached-node descriptor */
224 u32 andesc[28];
225 /* incident-specific information */
226 u32 isinfo[28];
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100227 } __attribute__ ((packed)) *lir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
Cornelia Huck12975ae2006-10-11 15:31:47 +0200229 lir = data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 if (!(lir->iq&0x80))
231 /* NULL link incident record */
232 return -EINVAL;
233 if (!(lir->indesc[0]&0xc0000000))
234 /* node descriptor not valid */
235 return -EINVAL;
236 if (!(lir->indesc[0]&0x10000000))
237 /* don't handle device-type nodes - FIXME */
238 return -EINVAL;
239 /* Byte 3 contains the chpid. Could also be CTCA, but we don't care */
240
241 return (u16) (lir->indesc[0]&0x000000ff);
242}
243
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100244struct chsc_sei_area {
245 struct chsc_header request;
246 u32 reserved1;
247 u32 reserved2;
248 u32 reserved3;
249 struct chsc_header response;
250 u32 reserved4;
251 u8 flags;
252 u8 vf; /* validity flags */
253 u8 rs; /* reporting source */
254 u8 cc; /* content code */
255 u16 fla; /* full link address */
256 u16 rsid; /* reporting source id */
257 u32 reserved5;
258 u32 reserved6;
259 u8 ccdf[4096 - 16 - 24]; /* content-code dependent field */
260 /* ccdf has to be big enough for a link-incident record */
261} __attribute__ ((packed));
262
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200263static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264{
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200265 struct chp_id chpid;
266 int id;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100267
268 CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n",
269 sei_area->rs, sei_area->rsid);
270 if (sei_area->rs != 4)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200271 return;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200272 id = __get_chpid_from_lir(sei_area->ccdf);
273 if (id < 0)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100274 CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n");
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200275 else {
276 chp_id_init(&chpid);
277 chpid.id = id;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200278 chsc_chp_offline(chpid);
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200279 }
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100280}
281
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200282static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100283{
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800284 struct res_acc_data res_data;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200285 struct chp_id chpid;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100286 int status;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100287
288 CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, "
289 "rs_id=%04x)\n", sei_area->rs, sei_area->rsid);
290 if (sei_area->rs != 4)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200291 return;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200292 chp_id_init(&chpid);
293 chpid.id = sei_area->rsid;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100294 /* allocate a new channel path structure, if needed */
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200295 status = chp_get_status(chpid);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100296 if (status < 0)
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200297 chp_new(chpid);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100298 else if (!status)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200299 return;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100300 memset(&res_data, 0, sizeof(struct res_acc_data));
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200301 res_data.chpid = chpid;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100302 if ((sei_area->vf & 0xc0) != 0) {
303 res_data.fla = sei_area->fla;
304 if ((sei_area->vf & 0xc0) == 0xc0)
305 /* full link address */
306 res_data.fla_mask = 0xffff;
307 else
308 /* link address */
309 res_data.fla_mask = 0xff00;
310 }
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200311 s390_process_res_acc(&res_data);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100312}
313
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200314struct chp_config_data {
315 u8 map[32];
316 u8 op;
317 u8 pc;
318};
319
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200320static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200321{
322 struct chp_config_data *data;
323 struct chp_id chpid;
324 int num;
325
326 CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n");
327 if (sei_area->rs != 0)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200328 return;
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200329 data = (struct chp_config_data *) &(sei_area->ccdf);
330 chp_id_init(&chpid);
331 for (num = 0; num <= __MAX_CHPID; num++) {
332 if (!chp_test_bit(data->map, num))
333 continue;
334 chpid.id = num;
335 printk(KERN_WARNING "cio: processing configure event %d for "
336 "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id);
337 switch (data->op) {
338 case 0:
339 chp_cfg_schedule(chpid, 1);
340 break;
341 case 1:
342 chp_cfg_schedule(chpid, 0);
343 break;
344 case 2:
345 chp_cfg_cancel_deconfigure(chpid);
346 break;
347 }
348 }
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200349}
350
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200351static void chsc_process_sei(struct chsc_sei_area *sei_area)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100352{
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100353 /* Check if we might have lost some information. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200354 if (sei_area->flags & 0x40) {
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100355 CIO_CRW_EVENT(2, "chsc: event overflow\n");
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200356 css_schedule_eval_all();
357 }
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100358 /* which kind of information was stored? */
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100359 switch (sei_area->cc) {
360 case 1: /* link incident*/
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200361 chsc_process_sei_link_incident(sei_area);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100362 break;
363 case 2: /* i/o resource accessibiliy */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200364 chsc_process_sei_res_acc(sei_area);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100365 break;
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200366 case 8: /* channel-path-configuration notification */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200367 chsc_process_sei_chp_config(sei_area);
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200368 break;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100369 default: /* other stuff */
370 CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
371 sei_area->cc);
372 break;
373 }
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100374}
375
Cornelia Huckc1156182008-07-14 09:58:46 +0200376static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100377{
378 struct chsc_sei_area *sei_area;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
Cornelia Huckc1156182008-07-14 09:58:46 +0200380 if (overflow) {
381 css_schedule_eval_all();
382 return;
383 }
384 CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, "
385 "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
386 crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
387 crw0->erc, crw0->rsid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 if (!sei_page)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200389 return;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100390 /* Access to sei_page is serialized through machine check handler
391 * thread, so no need for locking. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 sei_area = sei_page;
393
Cornelia Huckc1156182008-07-14 09:58:46 +0200394 CIO_TRACE_EVENT(2, "prcss");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 memset(sei_area, 0, sizeof(*sei_area));
Cornelia Huck495a5b42006-03-24 03:15:14 -0800397 sei_area->request.length = 0x0010;
398 sei_area->request.code = 0x000e;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100399 if (chsc(sei_area))
400 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100402 if (sei_area->response.code == 0x0001) {
403 CIO_CRW_EVENT(4, "chsc: sei successful\n");
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200404 chsc_process_sei(sei_area);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100405 } else {
406 CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 sei_area->response.code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 break;
409 }
410 } while (sei_area->flags & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411}
412
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200413void chsc_chp_online(struct chp_id chpid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 char dbf_txt[15];
Cornelia Huckc820de32008-07-14 09:58:45 +0200416 struct res_acc_data res_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200418 sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 CIO_TRACE_EVENT(2, dbf_txt);
420
Cornelia Huck22806dc2008-04-17 07:45:59 +0200421 if (chp_get_status(chpid) != 0) {
Cornelia Huckc820de32008-07-14 09:58:45 +0200422 memset(&res_data, 0, sizeof(struct res_acc_data));
423 res_data.chpid = chpid;
Cornelia Huck22806dc2008-04-17 07:45:59 +0200424 /* Wait until previous actions have settled. */
425 css_wait_for_slow_path();
Cornelia Huckc820de32008-07-14 09:58:45 +0200426 for_each_subchannel_staged(__s390_process_res_acc, NULL,
427 &res_data);
Cornelia Huck22806dc2008-04-17 07:45:59 +0200428 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429}
430
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200431static void __s390_subchannel_vary_chpid(struct subchannel *sch,
432 struct chp_id chpid, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 unsigned long flags;
Cornelia Huckc820de32008-07-14 09:58:45 +0200435 struct res_acc_data res_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
Cornelia Huckc820de32008-07-14 09:58:45 +0200437 memset(&res_data, 0, sizeof(struct res_acc_data));
438 res_data.chpid = chpid;
Cornelia Huck2ec22982006-12-08 15:54:26 +0100439 spin_lock_irqsave(sch->lock, flags);
Cornelia Huckc820de32008-07-14 09:58:45 +0200440 if (sch->driver && sch->driver->chp_event)
441 sch->driver->chp_event(sch, &res_data,
442 on ? CHP_VARY_ON : CHP_VARY_OFF);
Cornelia Huck2ec22982006-12-08 15:54:26 +0100443 spin_unlock_irqrestore(sch->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444}
445
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100446static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447{
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100448 struct chp_id *chpid = data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449
450 __s390_subchannel_vary_chpid(sch, *chpid, 0);
451 return 0;
452}
453
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100454static int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455{
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100456 struct chp_id *chpid = data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
458 __s390_subchannel_vary_chpid(sch, *chpid, 1);
459 return 0;
460}
461
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800462static int
463__s390_vary_chpid_on(struct subchannel_id schid, void *data)
464{
465 struct schib schib;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800466
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800467 if (stsch_err(schid, &schib))
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800468 /* We're through */
469 return -ENXIO;
470 /* Put it on the slow path. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200471 css_schedule_eval(schid);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800472 return 0;
473}
474
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200475/**
476 * chsc_chp_vary - propagate channel-path vary operation to subchannels
477 * @chpid: channl-path ID
478 * @on: non-zero for vary online, zero for vary offline
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 */
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200480int chsc_chp_vary(struct chp_id chpid, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481{
Cornelia Huck22806dc2008-04-17 07:45:59 +0200482 /* Wait until previous actions have settled. */
483 css_wait_for_slow_path();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 /*
485 * Redo PathVerification on the devices the chpid connects to
486 */
487
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800488 if (on)
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100489 for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
490 __s390_vary_chpid_on, &chpid);
491 else
492 for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
493 NULL, &chpid);
494
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 return 0;
496}
497
Cornelia Huck495a5b42006-03-24 03:15:14 -0800498static void
499chsc_remove_cmg_attr(struct channel_subsystem *css)
500{
501 int i;
502
503 for (i = 0; i <= __MAX_CHPID; i++) {
504 if (!css->chps[i])
505 continue;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200506 chp_remove_cmg_attr(css->chps[i]);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800507 }
508}
509
510static int
511chsc_add_cmg_attr(struct channel_subsystem *css)
512{
513 int i, ret;
514
515 ret = 0;
516 for (i = 0; i <= __MAX_CHPID; i++) {
517 if (!css->chps[i])
518 continue;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200519 ret = chp_add_cmg_attr(css->chps[i]);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800520 if (ret)
521 goto cleanup;
522 }
523 return ret;
524cleanup:
525 for (--i; i >= 0; i--) {
526 if (!css->chps[i])
527 continue;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200528 chp_remove_cmg_attr(css->chps[i]);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800529 }
530 return ret;
531}
532
Cornelia Huck495a5b42006-03-24 03:15:14 -0800533static int
534__chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
535{
536 struct {
537 struct chsc_header request;
538 u32 operation_code : 2;
539 u32 : 30;
540 u32 key : 4;
541 u32 : 28;
542 u32 zeroes1;
543 u32 cub_addr1;
544 u32 zeroes2;
545 u32 cub_addr2;
546 u32 reserved[13];
547 struct chsc_header response;
548 u32 status : 8;
549 u32 : 4;
550 u32 fmt : 4;
551 u32 : 16;
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100552 } __attribute__ ((packed)) *secm_area;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800553 int ret, ccode;
554
555 secm_area = page;
556 secm_area->request.length = 0x0050;
557 secm_area->request.code = 0x0016;
558
559 secm_area->key = PAGE_DEFAULT_KEY;
560 secm_area->cub_addr1 = (u64)(unsigned long)css->cub_addr1;
561 secm_area->cub_addr2 = (u64)(unsigned long)css->cub_addr2;
562
563 secm_area->operation_code = enable ? 0 : 1;
564
565 ccode = chsc(secm_area);
566 if (ccode > 0)
567 return (ccode == 3) ? -ENODEV : -EBUSY;
568
569 switch (secm_area->response.code) {
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100570 case 0x0102:
571 case 0x0103:
Cornelia Huck495a5b42006-03-24 03:15:14 -0800572 ret = -EINVAL;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800573 default:
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100574 ret = chsc_error_from_response(secm_area->response.code);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800575 }
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100576 if (ret != 0)
577 CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n",
578 secm_area->response.code);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800579 return ret;
580}
581
582int
583chsc_secm(struct channel_subsystem *css, int enable)
584{
585 void *secm_area;
586 int ret;
587
588 secm_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
589 if (!secm_area)
590 return -ENOMEM;
591
Cornelia Huck495a5b42006-03-24 03:15:14 -0800592 if (enable && !css->cm_enabled) {
593 css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
594 css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
595 if (!css->cub_addr1 || !css->cub_addr2) {
596 free_page((unsigned long)css->cub_addr1);
597 free_page((unsigned long)css->cub_addr2);
598 free_page((unsigned long)secm_area);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800599 return -ENOMEM;
600 }
601 }
602 ret = __chsc_do_secm(css, enable, secm_area);
603 if (!ret) {
604 css->cm_enabled = enable;
605 if (css->cm_enabled) {
606 ret = chsc_add_cmg_attr(css);
607 if (ret) {
608 memset(secm_area, 0, PAGE_SIZE);
609 __chsc_do_secm(css, 0, secm_area);
610 css->cm_enabled = 0;
611 }
612 } else
613 chsc_remove_cmg_attr(css);
614 }
Cornelia Huck8c4941c2007-04-27 16:01:38 +0200615 if (!css->cm_enabled) {
Cornelia Huck495a5b42006-03-24 03:15:14 -0800616 free_page((unsigned long)css->cub_addr1);
617 free_page((unsigned long)css->cub_addr2);
618 }
Cornelia Huck495a5b42006-03-24 03:15:14 -0800619 free_page((unsigned long)secm_area);
620 return ret;
621}
622
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200623int chsc_determine_channel_path_description(struct chp_id chpid,
624 struct channel_path_desc *desc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625{
626 int ccode, ret;
627
628 struct {
629 struct chsc_header request;
630 u32 : 24;
631 u32 first_chpid : 8;
632 u32 : 24;
633 u32 last_chpid : 8;
634 u32 zeroes1;
635 struct chsc_header response;
636 u32 zeroes2;
637 struct channel_path_desc desc;
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100638 } __attribute__ ((packed)) *scpd_area;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
640 scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
641 if (!scpd_area)
642 return -ENOMEM;
643
Cornelia Huck495a5b42006-03-24 03:15:14 -0800644 scpd_area->request.length = 0x0010;
645 scpd_area->request.code = 0x0002;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200647 scpd_area->first_chpid = chpid.id;
648 scpd_area->last_chpid = chpid.id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
650 ccode = chsc(scpd_area);
651 if (ccode > 0) {
652 ret = (ccode == 3) ? -ENODEV : -EBUSY;
653 goto out;
654 }
655
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100656 ret = chsc_error_from_response(scpd_area->response.code);
657 if (ret == 0)
658 /* Success. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 memcpy(desc, &scpd_area->desc,
660 sizeof(struct channel_path_desc));
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100661 else
662 CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 scpd_area->response.code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664out:
665 free_page((unsigned long)scpd_area);
666 return ret;
667}
668
Cornelia Huck495a5b42006-03-24 03:15:14 -0800669static void
670chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
671 struct cmg_chars *chars)
672{
673 switch (chp->cmg) {
674 case 2:
675 case 3:
676 chp->cmg_chars = kmalloc(sizeof(struct cmg_chars),
677 GFP_KERNEL);
678 if (chp->cmg_chars) {
679 int i, mask;
680 struct cmg_chars *cmg_chars;
681
682 cmg_chars = chp->cmg_chars;
683 for (i = 0; i < NR_MEASUREMENT_CHARS; i++) {
684 mask = 0x80 >> (i + 3);
685 if (cmcv & mask)
686 cmg_chars->values[i] = chars->values[i];
687 else
688 cmg_chars->values[i] = 0;
689 }
690 }
691 break;
692 default:
693 /* No cmg-dependent data. */
694 break;
695 }
696}
697
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200698int chsc_get_channel_measurement_chars(struct channel_path *chp)
Cornelia Huck495a5b42006-03-24 03:15:14 -0800699{
700 int ccode, ret;
701
702 struct {
703 struct chsc_header request;
704 u32 : 24;
705 u32 first_chpid : 8;
706 u32 : 24;
707 u32 last_chpid : 8;
708 u32 zeroes1;
709 struct chsc_header response;
710 u32 zeroes2;
711 u32 not_valid : 1;
712 u32 shared : 1;
713 u32 : 22;
714 u32 chpid : 8;
715 u32 cmcv : 5;
716 u32 : 11;
717 u32 cmgq : 8;
718 u32 cmg : 8;
719 u32 zeroes3;
720 u32 data[NR_MEASUREMENT_CHARS];
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100721 } __attribute__ ((packed)) *scmc_area;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800722
723 scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
724 if (!scmc_area)
725 return -ENOMEM;
726
727 scmc_area->request.length = 0x0010;
728 scmc_area->request.code = 0x0022;
729
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200730 scmc_area->first_chpid = chp->chpid.id;
731 scmc_area->last_chpid = chp->chpid.id;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800732
733 ccode = chsc(scmc_area);
734 if (ccode > 0) {
735 ret = (ccode == 3) ? -ENODEV : -EBUSY;
736 goto out;
737 }
738
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100739 ret = chsc_error_from_response(scmc_area->response.code);
740 if (ret == 0) {
741 /* Success. */
Cornelia Huck495a5b42006-03-24 03:15:14 -0800742 if (!scmc_area->not_valid) {
743 chp->cmg = scmc_area->cmg;
744 chp->shared = scmc_area->shared;
745 chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
746 (struct cmg_chars *)
747 &scmc_area->data);
748 } else {
749 chp->cmg = -1;
750 chp->shared = -1;
751 }
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100752 } else {
753 CIO_CRW_EVENT(2, "chsc: scmc failed (rc=%04x)\n",
Cornelia Huck495a5b42006-03-24 03:15:14 -0800754 scmc_area->response.code);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800755 }
756out:
757 free_page((unsigned long)scmc_area);
758 return ret;
759}
760
Cornelia Huck4434a382007-07-27 12:29:21 +0200761int __init chsc_alloc_sei_area(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762{
Cornelia Huckc1156182008-07-14 09:58:46 +0200763 int ret;
764
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
Cornelia Huckc1156182008-07-14 09:58:46 +0200766 if (!sei_page) {
Cornelia Hucke556bbb2007-07-27 12:29:19 +0200767 CIO_MSG_EVENT(0, "Can't allocate page for processing of "
768 "chsc machine checks!\n");
Cornelia Huckc1156182008-07-14 09:58:46 +0200769 return -ENOMEM;
770 }
771 ret = s390_register_crw_handler(CRW_RSC_CSS, chsc_process_crw);
772 if (ret)
773 kfree(sei_page);
774 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775}
776
Cornelia Huck4434a382007-07-27 12:29:21 +0200777void __init chsc_free_sei_area(void)
778{
Cornelia Huckc1156182008-07-14 09:58:46 +0200779 s390_unregister_crw_handler(CRW_RSC_CSS);
Cornelia Huck4434a382007-07-27 12:29:21 +0200780 kfree(sei_page);
781}
782
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800783int __init
784chsc_enable_facility(int operation_code)
785{
786 int ret;
787 struct {
788 struct chsc_header request;
789 u8 reserved1:4;
790 u8 format:4;
791 u8 reserved2;
792 u16 operation_code;
793 u32 reserved3;
794 u32 reserved4;
795 u32 operation_data_area[252];
796 struct chsc_header response;
797 u32 reserved5:4;
798 u32 format2:4;
799 u32 reserved6:24;
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100800 } __attribute__ ((packed)) *sda_area;
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800801
802 sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA);
803 if (!sda_area)
804 return -ENOMEM;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800805 sda_area->request.length = 0x0400;
806 sda_area->request.code = 0x0031;
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800807 sda_area->operation_code = operation_code;
808
809 ret = chsc(sda_area);
810 if (ret > 0) {
811 ret = (ret == 3) ? -ENODEV : -EBUSY;
812 goto out;
813 }
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100814
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800815 switch (sda_area->response.code) {
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100816 case 0x0101:
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800817 ret = -EOPNOTSUPP;
818 break;
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100819 default:
820 ret = chsc_error_from_response(sda_area->response.code);
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800821 }
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100822 if (ret != 0)
823 CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n",
824 operation_code, sda_area->response.code);
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800825 out:
826 free_page((unsigned long)sda_area);
827 return ret;
828}
829
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830struct css_general_char css_general_characteristics;
831struct css_chsc_char css_chsc_characteristics;
832
833int __init
834chsc_determine_css_characteristics(void)
835{
836 int result;
837 struct {
838 struct chsc_header request;
839 u32 reserved1;
840 u32 reserved2;
841 u32 reserved3;
842 struct chsc_header response;
843 u32 reserved4;
844 u32 general_char[510];
845 u32 chsc_char[518];
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100846 } __attribute__ ((packed)) *scsc_area;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
848 scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100849 if (!scsc_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851
Cornelia Huck495a5b42006-03-24 03:15:14 -0800852 scsc_area->request.length = 0x0010;
853 scsc_area->request.code = 0x0010;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854
855 result = chsc(scsc_area);
856 if (result) {
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100857 result = (result == 3) ? -ENODEV : -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 goto exit;
859 }
860
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100861 result = chsc_error_from_response(scsc_area->response.code);
862 if (result == 0) {
863 memcpy(&css_general_characteristics, scsc_area->general_char,
864 sizeof(css_general_characteristics));
865 memcpy(&css_chsc_characteristics, scsc_area->chsc_char,
866 sizeof(css_chsc_characteristics));
867 } else
868 CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n",
869 scsc_area->response.code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870exit:
871 free_page ((unsigned long) scsc_area);
872 return result;
873}
874
875EXPORT_SYMBOL_GPL(css_general_characteristics);
876EXPORT_SYMBOL_GPL(css_chsc_characteristics);
Martin Schwidefskyd2fec592008-07-14 09:58:56 +0200877
878int chsc_sstpc(void *page, unsigned int op, u16 ctrl)
879{
880 struct {
881 struct chsc_header request;
882 unsigned int rsvd0;
883 unsigned int op : 8;
884 unsigned int rsvd1 : 8;
885 unsigned int ctrl : 16;
886 unsigned int rsvd2[5];
887 struct chsc_header response;
888 unsigned int rsvd3[7];
889 } __attribute__ ((packed)) *rr;
890 int rc;
891
892 memset(page, 0, PAGE_SIZE);
893 rr = page;
894 rr->request.length = 0x0020;
895 rr->request.code = 0x0033;
896 rr->op = op;
897 rr->ctrl = ctrl;
898 rc = chsc(rr);
899 if (rc)
900 return -EIO;
901 rc = (rr->response.code == 0x0001) ? 0 : -EIO;
902 return rc;
903}
904
905int chsc_sstpi(void *page, void *result, size_t size)
906{
907 struct {
908 struct chsc_header request;
909 unsigned int rsvd0[3];
910 struct chsc_header response;
911 char data[size];
912 } __attribute__ ((packed)) *rr;
913 int rc;
914
915 memset(page, 0, PAGE_SIZE);
916 rr = page;
917 rr->request.length = 0x0010;
918 rr->request.code = 0x0038;
919 rc = chsc(rr);
920 if (rc)
921 return -EIO;
922 memcpy(result, &rr->data, size);
923 return (rr->response.code == 0x0001) ? 0 : -EIO;
924}
925