aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/mm/extmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/mm/extmem.c')
-rw-r--r--arch/s390/mm/extmem.c251
1 files changed, 212 insertions, 39 deletions
diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c
index f231f5ec74b6..580fc64cc735 100644
--- a/arch/s390/mm/extmem.c
+++ b/arch/s390/mm/extmem.c
@@ -43,20 +43,40 @@
43#define DCSS_FINDSEG 0x0c 43#define DCSS_FINDSEG 0x0c
44#define DCSS_LOADNOLY 0x10 44#define DCSS_LOADNOLY 0x10
45#define DCSS_SEGEXT 0x18 45#define DCSS_SEGEXT 0x18
46#define DCSS_LOADSHRX 0x20
47#define DCSS_LOADNSRX 0x24
48#define DCSS_FINDSEGX 0x2c
49#define DCSS_SEGEXTX 0x38
46#define DCSS_FINDSEGA 0x0c 50#define DCSS_FINDSEGA 0x0c
47 51
48struct qrange { 52struct qrange {
49 unsigned int start; // 3byte start address, 1 byte type 53 unsigned long start; /* last byte type */
50 unsigned int end; // 3byte end address, 1 byte reserved 54 unsigned long end; /* last byte reserved */
51}; 55};
52 56
53struct qout64 { 57struct qout64 {
58 unsigned long segstart;
59 unsigned long segend;
60 int segcnt;
61 int segrcnt;
62 struct qrange range[6];
63};
64
65#ifdef CONFIG_64BIT
66struct qrange_old {
67 unsigned int start; /* last byte type */
68 unsigned int end; /* last byte reserved */
69};
70
71/* output area format for the Diag x'64' old subcode x'18' */
72struct qout64_old {
54 int segstart; 73 int segstart;
55 int segend; 74 int segend;
56 int segcnt; 75 int segcnt;
57 int segrcnt; 76 int segrcnt;
58 struct qrange range[6]; 77 struct qrange_old range[6];
59}; 78};
79#endif
60 80
61struct qin64 { 81struct qin64 {
62 char qopcode; 82 char qopcode;
@@ -86,6 +106,55 @@ static DEFINE_MUTEX(dcss_lock);
86static LIST_HEAD(dcss_list); 106static LIST_HEAD(dcss_list);
87static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", 107static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
88 "EW/EN-MIXED" }; 108 "EW/EN-MIXED" };
109static int loadshr_scode, loadnsr_scode, findseg_scode;
110static int segext_scode, purgeseg_scode;
111static int scode_set;
112
113/* set correct Diag x'64' subcodes. */
114static int
115dcss_set_subcodes(void)
116{
117#ifdef CONFIG_64BIT
118 char *name = kmalloc(8 * sizeof(char), GFP_DMA);
119 unsigned long rx, ry;
120 int rc;
121
122 if (name == NULL)
123 return -ENOMEM;
124
125 rx = (unsigned long) name;
126 ry = DCSS_FINDSEGX;
127
128 strcpy(name, "dummy");
129 asm volatile(
130 " diag %0,%1,0x64\n"
131 "0: ipm %2\n"
132 " srl %2,28\n"
133 " j 2f\n"
134 "1: la %2,3\n"
135 "2:\n"
136 EX_TABLE(0b, 1b)
137 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
138
139 kfree(name);
140 /* Diag x'64' new subcodes are supported, set to new subcodes */
141 if (rc != 3) {
142 loadshr_scode = DCSS_LOADSHRX;
143 loadnsr_scode = DCSS_LOADNSRX;
144 purgeseg_scode = DCSS_PURGESEG;
145 findseg_scode = DCSS_FINDSEGX;
146 segext_scode = DCSS_SEGEXTX;
147 return 0;
148 }
149#endif
150 /* Diag x'64' new subcodes are not supported, set to old subcodes */
151 loadshr_scode = DCSS_LOADNOLY;
152 loadnsr_scode = DCSS_LOADNSR;
153 purgeseg_scode = DCSS_PURGESEG;
154 findseg_scode = DCSS_FINDSEG;
155 segext_scode = DCSS_SEGEXT;
156 return 0;
157}
89 158
90/* 159/*
91 * Create the 8 bytes, ebcdic VM segment name from 160 * Create the 8 bytes, ebcdic VM segment name from
@@ -135,25 +204,45 @@ segment_by_name (char *name)
135 * Perform a function on a dcss segment. 204 * Perform a function on a dcss segment.
136 */ 205 */
137static inline int 206static inline int
138dcss_diag (__u8 func, void *parameter, 207dcss_diag(int *func, void *parameter,
139 unsigned long *ret1, unsigned long *ret2) 208 unsigned long *ret1, unsigned long *ret2)
140{ 209{
141 unsigned long rx, ry; 210 unsigned long rx, ry;
142 int rc; 211 int rc;
143 212
213 if (scode_set == 0) {
214 rc = dcss_set_subcodes();
215 if (rc < 0)
216 return rc;
217 scode_set = 1;
218 }
144 rx = (unsigned long) parameter; 219 rx = (unsigned long) parameter;
145 ry = (unsigned long) func; 220 ry = (unsigned long) *func;
146 asm volatile( 221
147#ifdef CONFIG_64BIT 222#ifdef CONFIG_64BIT
148 " sam31\n" 223 /* 64-bit Diag x'64' new subcode, keep in 64-bit addressing mode */
149 " diag %0,%1,0x64\n" 224 if (*func > DCSS_SEGEXT)
150 " sam64\n" 225 asm volatile(
226 " diag %0,%1,0x64\n"
227 " ipm %2\n"
228 " srl %2,28\n"
229 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
230 /* 31-bit Diag x'64' old subcode, switch to 31-bit addressing mode */
231 else
232 asm volatile(
233 " sam31\n"
234 " diag %0,%1,0x64\n"
235 " sam64\n"
236 " ipm %2\n"
237 " srl %2,28\n"
238 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
151#else 239#else
240 asm volatile(
152 " diag %0,%1,0x64\n" 241 " diag %0,%1,0x64\n"
153#endif
154 " ipm %2\n" 242 " ipm %2\n"
155 " srl %2,28\n" 243 " srl %2,28\n"
156 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); 244 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
245#endif
157 *ret1 = rx; 246 *ret1 = rx;
158 *ret2 = ry; 247 *ret2 = ry;
159 return rc; 248 return rc;
@@ -190,14 +279,45 @@ query_segment_type (struct dcss_segment *seg)
190 qin->qoutlen = sizeof(struct qout64); 279 qin->qoutlen = sizeof(struct qout64);
191 memcpy (qin->qname, seg->dcss_name, 8); 280 memcpy (qin->qname, seg->dcss_name, 8);
192 281
193 diag_cc = dcss_diag (DCSS_SEGEXT, qin, &dummy, &vmrc); 282 diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc);
194 283
284 if (diag_cc < 0) {
285 rc = diag_cc;
286 goto out_free;
287 }
195 if (diag_cc > 1) { 288 if (diag_cc > 1) {
196 PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc); 289 PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc);
197 rc = dcss_diag_translate_rc (vmrc); 290 rc = dcss_diag_translate_rc (vmrc);
198 goto out_free; 291 goto out_free;
199 } 292 }
200 293
294#ifdef CONFIG_64BIT
295 /* Only old format of output area of Diagnose x'64' is supported,
296 copy data for the new format. */
297 if (segext_scode == DCSS_SEGEXT) {
298 struct qout64_old *qout_old;
299 qout_old = kzalloc(sizeof(struct qout64_old), GFP_DMA);
300 if (qout_old == NULL) {
301 rc = -ENOMEM;
302 goto out_free;
303 }
304 memcpy(qout_old, qout, sizeof(struct qout64_old));
305 qout->segstart = (unsigned long) qout_old->segstart;
306 qout->segend = (unsigned long) qout_old->segend;
307 qout->segcnt = qout_old->segcnt;
308 qout->segrcnt = qout_old->segrcnt;
309
310 if (qout->segcnt > 6)
311 qout->segrcnt = 6;
312 for (i = 0; i < qout->segrcnt; i++) {
313 qout->range[i].start =
314 (unsigned long) qout_old->range[i].start;
315 qout->range[i].end =
316 (unsigned long) qout_old->range[i].end;
317 }
318 kfree(qout_old);
319 }
320#endif
201 if (qout->segcnt > 6) { 321 if (qout->segcnt > 6) {
202 rc = -ENOTSUPP; 322 rc = -ENOTSUPP;
203 goto out_free; 323 goto out_free;
@@ -269,6 +389,30 @@ segment_type (char* name)
269} 389}
270 390
271/* 391/*
392 * check if segment collides with other segments that are currently loaded
393 * returns 1 if this is the case, 0 if no collision was found
394 */
395static int
396segment_overlaps_others (struct dcss_segment *seg)
397{
398 struct list_head *l;
399 struct dcss_segment *tmp;
400
401 BUG_ON(!mutex_is_locked(&dcss_lock));
402 list_for_each(l, &dcss_list) {
403 tmp = list_entry(l, struct dcss_segment, list);
404 if ((tmp->start_addr >> 20) > (seg->end >> 20))
405 continue;
406 if ((tmp->end >> 20) < (seg->start_addr >> 20))
407 continue;
408 if (seg == tmp)
409 continue;
410 return 1;
411 }
412 return 0;
413}
414
415/*
272 * real segment loading function, called from segment_load 416 * real segment loading function, called from segment_load
273 */ 417 */
274static int 418static int
@@ -276,7 +420,8 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
276{ 420{
277 struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment), 421 struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment),
278 GFP_DMA); 422 GFP_DMA);
279 int dcss_command, rc, diag_cc; 423 int rc, diag_cc;
424 unsigned long start_addr, end_addr, dummy;
280 425
281 if (seg == NULL) { 426 if (seg == NULL) {
282 rc = -ENOMEM; 427 rc = -ENOMEM;
@@ -287,6 +432,13 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
287 if (rc < 0) 432 if (rc < 0)
288 goto out_free; 433 goto out_free;
289 434
435 if (loadshr_scode == DCSS_LOADSHRX) {
436 if (segment_overlaps_others(seg)) {
437 rc = -EBUSY;
438 goto out_free;
439 }
440 }
441
290 rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 442 rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
291 443
292 if (rc) 444 if (rc)
@@ -316,20 +468,28 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
316 } 468 }
317 469
318 if (do_nonshared) 470 if (do_nonshared)
319 dcss_command = DCSS_LOADNSR; 471 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
472 &start_addr, &end_addr);
320 else 473 else
321 dcss_command = DCSS_LOADNOLY; 474 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
322 475 &start_addr, &end_addr);
323 diag_cc = dcss_diag(dcss_command, seg->dcss_name, 476 if (diag_cc < 0) {
324 &seg->start_addr, &seg->end); 477 dcss_diag(&purgeseg_scode, seg->dcss_name,
478 &dummy, &dummy);
479 rc = diag_cc;
480 goto out_resource;
481 }
325 if (diag_cc > 1) { 482 if (diag_cc > 1) {
326 PRINT_WARN ("segment_load: could not load segment %s - " 483 PRINT_WARN ("segment_load: could not load segment %s - "
327 "diag returned error (%ld)\n",name,seg->end); 484 "diag returned error (%ld)\n",
328 rc = dcss_diag_translate_rc (seg->end); 485 name, end_addr);
329 dcss_diag(DCSS_PURGESEG, seg->dcss_name, 486 rc = dcss_diag_translate_rc(end_addr);
330 &seg->start_addr, &seg->end); 487 dcss_diag(&purgeseg_scode, seg->dcss_name,
488 &dummy, &dummy);
331 goto out_resource; 489 goto out_resource;
332 } 490 }
491 seg->start_addr = start_addr;
492 seg->end = end_addr;
333 seg->do_nonshared = do_nonshared; 493 seg->do_nonshared = do_nonshared;
334 atomic_set(&seg->ref_count, 1); 494 atomic_set(&seg->ref_count, 1);
335 list_add(&seg->list, &dcss_list); 495 list_add(&seg->list, &dcss_list);
@@ -423,8 +583,8 @@ int
423segment_modify_shared (char *name, int do_nonshared) 583segment_modify_shared (char *name, int do_nonshared)
424{ 584{
425 struct dcss_segment *seg; 585 struct dcss_segment *seg;
426 unsigned long dummy; 586 unsigned long start_addr, end_addr, dummy;
427 int dcss_command, rc, diag_cc; 587 int rc, diag_cc;
428 588
429 mutex_lock(&dcss_lock); 589 mutex_lock(&dcss_lock);
430 seg = segment_by_name (name); 590 seg = segment_by_name (name);
@@ -445,38 +605,51 @@ segment_modify_shared (char *name, int do_nonshared)
445 goto out_unlock; 605 goto out_unlock;
446 } 606 }
447 release_resource(seg->res); 607 release_resource(seg->res);
448 if (do_nonshared) { 608 if (do_nonshared)
449 dcss_command = DCSS_LOADNSR;
450 seg->res->flags &= ~IORESOURCE_READONLY; 609 seg->res->flags &= ~IORESOURCE_READONLY;
451 } else { 610 else
452 dcss_command = DCSS_LOADNOLY;
453 if (seg->vm_segtype == SEG_TYPE_SR || 611 if (seg->vm_segtype == SEG_TYPE_SR ||
454 seg->vm_segtype == SEG_TYPE_ER) 612 seg->vm_segtype == SEG_TYPE_ER)
455 seg->res->flags |= IORESOURCE_READONLY; 613 seg->res->flags |= IORESOURCE_READONLY;
456 } 614
457 if (request_resource(&iomem_resource, seg->res)) { 615 if (request_resource(&iomem_resource, seg->res)) {
458 PRINT_WARN("segment_modify_shared: could not reload segment %s" 616 PRINT_WARN("segment_modify_shared: could not reload segment %s"
459 " - overlapping resources\n", name); 617 " - overlapping resources\n", name);
460 rc = -EBUSY; 618 rc = -EBUSY;
461 kfree(seg->res); 619 kfree(seg->res);
462 goto out_del; 620 goto out_del_mem;
621 }
622
623 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
624 if (do_nonshared)
625 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
626 &start_addr, &end_addr);
627 else
628 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
629 &start_addr, &end_addr);
630 if (diag_cc < 0) {
631 rc = diag_cc;
632 goto out_del_res;
463 } 633 }
464 dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
465 diag_cc = dcss_diag(dcss_command, seg->dcss_name,
466 &seg->start_addr, &seg->end);
467 if (diag_cc > 1) { 634 if (diag_cc > 1) {
468 PRINT_WARN ("segment_modify_shared: could not reload segment %s" 635 PRINT_WARN ("segment_modify_shared: could not reload segment %s"
469 " - diag returned error (%ld)\n",name,seg->end); 636 " - diag returned error (%ld)\n",
470 rc = dcss_diag_translate_rc (seg->end); 637 name, end_addr);
471 goto out_del; 638 rc = dcss_diag_translate_rc(end_addr);
639 goto out_del_res;
472 } 640 }
641 seg->start_addr = start_addr;
642 seg->end = end_addr;
473 seg->do_nonshared = do_nonshared; 643 seg->do_nonshared = do_nonshared;
474 rc = 0; 644 rc = 0;
475 goto out_unlock; 645 goto out_unlock;
476 out_del: 646 out_del_res:
647 release_resource(seg->res);
648 kfree(seg->res);
649 out_del_mem:
477 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 650 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
478 list_del(&seg->list); 651 list_del(&seg->list);
479 dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); 652 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
480 kfree(seg); 653 kfree(seg);
481 out_unlock: 654 out_unlock:
482 mutex_unlock(&dcss_lock); 655 mutex_unlock(&dcss_lock);
@@ -510,7 +683,7 @@ segment_unload(char *name)
510 kfree(seg->res); 683 kfree(seg->res);
511 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 684 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
512 list_del(&seg->list); 685 list_del(&seg->list);
513 dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); 686 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
514 kfree(seg); 687 kfree(seg);
515out_unlock: 688out_unlock:
516 mutex_unlock(&dcss_lock); 689 mutex_unlock(&dcss_lock);
@@ -545,7 +718,7 @@ segment_save(char *name)
545 endpfn = (seg->end) >> PAGE_SHIFT; 718 endpfn = (seg->end) >> PAGE_SHIFT;
546 sprintf(cmd1, "DEFSEG %s", name); 719 sprintf(cmd1, "DEFSEG %s", name);
547 for (i=0; i<seg->segcnt; i++) { 720 for (i=0; i<seg->segcnt; i++) {
548 sprintf(cmd1+strlen(cmd1), " %X-%X %s", 721 sprintf(cmd1+strlen(cmd1), " %lX-%lX %s",
549 seg->range[i].start >> PAGE_SHIFT, 722 seg->range[i].start >> PAGE_SHIFT,
550 seg->range[i].end >> PAGE_SHIFT, 723 seg->range[i].end >> PAGE_SHIFT,
551 segtype_string[seg->range[i].start & 0xff]); 724 segtype_string[seg->range[i].start & 0xff]);