diff options
Diffstat (limited to 'arch/s390/mm/extmem.c')
-rw-r--r-- | arch/s390/mm/extmem.c | 251 |
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 | ||
48 | struct qrange { | 52 | struct 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 | ||
53 | struct qout64 { | 57 | struct 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 | ||
66 | struct 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' */ | ||
72 | struct 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 | ||
61 | struct qin64 { | 81 | struct qin64 { |
62 | char qopcode; | 82 | char qopcode; |
@@ -86,6 +106,55 @@ static DEFINE_MUTEX(dcss_lock); | |||
86 | static LIST_HEAD(dcss_list); | 106 | static LIST_HEAD(dcss_list); |
87 | static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", | 107 | static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", |
88 | "EW/EN-MIXED" }; | 108 | "EW/EN-MIXED" }; |
109 | static int loadshr_scode, loadnsr_scode, findseg_scode; | ||
110 | static int segext_scode, purgeseg_scode; | ||
111 | static int scode_set; | ||
112 | |||
113 | /* set correct Diag x'64' subcodes. */ | ||
114 | static int | ||
115 | dcss_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 | */ |
137 | static inline int | 206 | static inline int |
138 | dcss_diag (__u8 func, void *parameter, | 207 | dcss_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 | */ | ||
395 | static int | ||
396 | segment_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 | */ |
274 | static int | 418 | static 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 | |||
423 | segment_modify_shared (char *name, int do_nonshared) | 583 | segment_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); |
515 | out_unlock: | 688 | out_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]); |