1 /* 2 * File...........: arch/s390/mm/extmem.c 3 * Author(s)......: Carsten Otte <cotte@de.ibm.com> 4 * Rob M van der Heij <rvdheij@nl.ibm.com> 5 * Steven Shultz <shultzss@us.ibm.com> 6 * Bugreports.to..: <Linux390@de.ibm.com> 7 * (C) IBM Corporation 2002-2004 8 */ 9 10 #define KMSG_COMPONENT "extmem" 11 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 12 13 #include <linux/kernel.h> 14 #include <linux/string.h> 15 #include <linux/spinlock.h> 16 #include <linux/list.h> 17 #include <linux/slab.h> 18 #include <linux/module.h> 19 #include <linux/bootmem.h> 20 #include <linux/ctype.h> 21 #include <linux/ioport.h> 22 #include <asm/page.h> 23 #include <asm/pgtable.h> 24 #include <asm/ebcdic.h> 25 #include <asm/errno.h> 26 #include <asm/extmem.h> 27 #include <asm/cpcmd.h> 28 #include <asm/setup.h> 29 30 #define DCSS_LOADSHR 0x00 31 #define DCSS_LOADNSR 0x04 32 #define DCSS_PURGESEG 0x08 33 #define DCSS_FINDSEG 0x0c 34 #define DCSS_LOADNOLY 0x10 35 #define DCSS_SEGEXT 0x18 36 #define DCSS_LOADSHRX 0x20 37 #define DCSS_LOADNSRX 0x24 38 #define DCSS_FINDSEGX 0x2c 39 #define DCSS_SEGEXTX 0x38 40 #define DCSS_FINDSEGA 0x0c 41 42 struct qrange { 43 unsigned long start; /* last byte type */ 44 unsigned long end; /* last byte reserved */ 45 }; 46 47 struct qout64 { 48 unsigned long segstart; 49 unsigned long segend; 50 int segcnt; 51 int segrcnt; 52 struct qrange range[6]; 53 }; 54 55 #ifdef CONFIG_64BIT 56 struct qrange_old { 57 unsigned int start; /* last byte type */ 58 unsigned int end; /* last byte reserved */ 59 }; 60 61 /* output area format for the Diag x'64' old subcode x'18' */ 62 struct qout64_old { 63 int segstart; 64 int segend; 65 int segcnt; 66 int segrcnt; 67 struct qrange_old range[6]; 68 }; 69 #endif 70 71 struct qin64 { 72 char qopcode; 73 char rsrv1[3]; 74 char qrcode; 75 char rsrv2[3]; 76 char qname[8]; 77 unsigned int qoutptr; 78 short int qoutlen; 79 }; 80 81 struct dcss_segment { 82 struct list_head list; 83 char dcss_name[8]; 84 char res_name[15]; 85 unsigned long start_addr; 86 unsigned long end; 87 atomic_t ref_count; 88 int do_nonshared; 89 unsigned int vm_segtype; 90 struct qrange range[6]; 91 int segcnt; 92 struct resource *res; 93 }; 94 95 static DEFINE_MUTEX(dcss_lock); 96 static LIST_HEAD(dcss_list); 97 static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", 98 "EW/EN-MIXED" }; 99 static int loadshr_scode, loadnsr_scode, findseg_scode; 100 static int segext_scode, purgeseg_scode; 101 static int scode_set; 102 103 /* set correct Diag x'64' subcodes. */ 104 static int 105 dcss_set_subcodes(void) 106 { 107 #ifdef CONFIG_64BIT 108 char *name = kmalloc(8 * sizeof(char), GFP_KERNEL | GFP_DMA); 109 unsigned long rx, ry; 110 int rc; 111 112 if (name == NULL) 113 return -ENOMEM; 114 115 rx = (unsigned long) name; 116 ry = DCSS_FINDSEGX; 117 118 strcpy(name, "dummy"); 119 asm volatile( 120 " diag %0,%1,0x64\n" 121 "0: ipm %2\n" 122 " srl %2,28\n" 123 " j 2f\n" 124 "1: la %2,3\n" 125 "2:\n" 126 EX_TABLE(0b, 1b) 127 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); 128 129 kfree(name); 130 /* Diag x'64' new subcodes are supported, set to new subcodes */ 131 if (rc != 3) { 132 loadshr_scode = DCSS_LOADSHRX; 133 loadnsr_scode = DCSS_LOADNSRX; 134 purgeseg_scode = DCSS_PURGESEG; 135 findseg_scode = DCSS_FINDSEGX; 136 segext_scode = DCSS_SEGEXTX; 137 return 0; 138 } 139 #endif 140 /* Diag x'64' new subcodes are not supported, set to old subcodes */ 141 loadshr_scode = DCSS_LOADNOLY; 142 loadnsr_scode = DCSS_LOADNSR; 143 purgeseg_scode = DCSS_PURGESEG; 144 findseg_scode = DCSS_FINDSEG; 145 segext_scode = DCSS_SEGEXT; 146 return 0; 147 } 148 149 /* 150 * Create the 8 bytes, ebcdic VM segment name from 151 * an ascii name. 152 */ 153 static void 154 dcss_mkname(char *name, char *dcss_name) 155 { 156 int i; 157 158 for (i = 0; i < 8; i++) { 159 if (name[i] == '\0') 160 break; 161 dcss_name[i] = toupper(name[i]); 162 }; 163 for (; i < 8; i++) 164 dcss_name[i] = ' '; 165 ASCEBC(dcss_name, 8); 166 } 167 168 169 /* 170 * search all segments in dcss_list, and return the one 171 * namend *name. If not found, return NULL. 172 */ 173 static struct dcss_segment * 174 segment_by_name (char *name) 175 { 176 char dcss_name[9]; 177 struct list_head *l; 178 struct dcss_segment *tmp, *retval = NULL; 179 180 BUG_ON(!mutex_is_locked(&dcss_lock)); 181 dcss_mkname (name, dcss_name); 182 list_for_each (l, &dcss_list) { 183 tmp = list_entry (l, struct dcss_segment, list); 184 if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) { 185 retval = tmp; 186 break; 187 } 188 } 189 return retval; 190 } 191 192 193 /* 194 * Perform a function on a dcss segment. 195 */ 196 static inline int 197 dcss_diag(int *func, void *parameter, 198 unsigned long *ret1, unsigned long *ret2) 199 { 200 unsigned long rx, ry; 201 int rc; 202 203 if (scode_set == 0) { 204 rc = dcss_set_subcodes(); 205 if (rc < 0) 206 return rc; 207 scode_set = 1; 208 } 209 rx = (unsigned long) parameter; 210 ry = (unsigned long) *func; 211 212 #ifdef CONFIG_64BIT 213 /* 64-bit Diag x'64' new subcode, keep in 64-bit addressing mode */ 214 if (*func > DCSS_SEGEXT) 215 asm volatile( 216 " diag %0,%1,0x64\n" 217 " ipm %2\n" 218 " srl %2,28\n" 219 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); 220 /* 31-bit Diag x'64' old subcode, switch to 31-bit addressing mode */ 221 else 222 asm volatile( 223 " sam31\n" 224 " diag %0,%1,0x64\n" 225 " sam64\n" 226 " ipm %2\n" 227 " srl %2,28\n" 228 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); 229 #else 230 asm volatile( 231 " diag %0,%1,0x64\n" 232 " ipm %2\n" 233 " srl %2,28\n" 234 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); 235 #endif 236 *ret1 = rx; 237 *ret2 = ry; 238 return rc; 239 } 240 241 static inline int 242 dcss_diag_translate_rc (int vm_rc) { 243 if (vm_rc == 44) 244 return -ENOENT; 245 return -EIO; 246 } 247 248 249 /* do a diag to get info about a segment. 250 * fills start_address, end and vm_segtype fields 251 */ 252 static int 253 query_segment_type (struct dcss_segment *seg) 254 { 255 unsigned long dummy, vmrc; 256 int diag_cc, rc, i; 257 struct qout64 *qout; 258 struct qin64 *qin; 259 260 qin = kmalloc(sizeof(*qin), GFP_KERNEL | GFP_DMA); 261 qout = kmalloc(sizeof(*qout), GFP_KERNEL | GFP_DMA); 262 if ((qin == NULL) || (qout == NULL)) { 263 rc = -ENOMEM; 264 goto out_free; 265 } 266 267 /* initialize diag input parameters */ 268 qin->qopcode = DCSS_FINDSEGA; 269 qin->qoutptr = (unsigned long) qout; 270 qin->qoutlen = sizeof(struct qout64); 271 memcpy (qin->qname, seg->dcss_name, 8); 272 273 diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc); 274 275 if (diag_cc < 0) { 276 rc = diag_cc; 277 goto out_free; 278 } 279 if (diag_cc > 1) { 280 pr_warning("Querying a DCSS type failed with rc=%ld\n", vmrc); 281 rc = dcss_diag_translate_rc (vmrc); 282 goto out_free; 283 } 284 285 #ifdef CONFIG_64BIT 286 /* Only old format of output area of Diagnose x'64' is supported, 287 copy data for the new format. */ 288 if (segext_scode == DCSS_SEGEXT) { 289 struct qout64_old *qout_old; 290 qout_old = kzalloc(sizeof(*qout_old), GFP_KERNEL | GFP_DMA); 291 if (qout_old == NULL) { 292 rc = -ENOMEM; 293 goto out_free; 294 } 295 memcpy(qout_old, qout, sizeof(struct qout64_old)); 296 qout->segstart = (unsigned long) qout_old->segstart; 297 qout->segend = (unsigned long) qout_old->segend; 298 qout->segcnt = qout_old->segcnt; 299 qout->segrcnt = qout_old->segrcnt; 300 301 if (qout->segcnt > 6) 302 qout->segrcnt = 6; 303 for (i = 0; i < qout->segrcnt; i++) { 304 qout->range[i].start = 305 (unsigned long) qout_old->range[i].start; 306 qout->range[i].end = 307 (unsigned long) qout_old->range[i].end; 308 } 309 kfree(qout_old); 310 } 311 #endif 312 if (qout->segcnt > 6) { 313 rc = -EOPNOTSUPP; 314 goto out_free; 315 } 316 317 if (qout->segcnt == 1) { 318 seg->vm_segtype = qout->range[0].start & 0xff; 319 } else { 320 /* multi-part segment. only one type supported here: 321 - all parts are contiguous 322 - all parts are either EW or EN type 323 - maximum 6 parts allowed */ 324 unsigned long start = qout->segstart >> PAGE_SHIFT; 325 for (i=0; i<qout->segcnt; i++) { 326 if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) && 327 ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) { 328 rc = -EOPNOTSUPP; 329 goto out_free; 330 } 331 if (start != qout->range[i].start >> PAGE_SHIFT) { 332 rc = -EOPNOTSUPP; 333 goto out_free; 334 } 335 start = (qout->range[i].end >> PAGE_SHIFT) + 1; 336 } 337 seg->vm_segtype = SEG_TYPE_EWEN; 338 } 339 340 /* analyze diag output and update seg */ 341 seg->start_addr = qout->segstart; 342 seg->end = qout->segend; 343 344 memcpy (seg->range, qout->range, 6*sizeof(struct qrange)); 345 seg->segcnt = qout->segcnt; 346 347 rc = 0; 348 349 out_free: 350 kfree(qin); 351 kfree(qout); 352 return rc; 353 } 354 355 /* 356 * get info about a segment 357 * possible return values: 358 * -ENOSYS : we are not running on VM 359 * -EIO : could not perform query diagnose 360 * -ENOENT : no such segment 361 * -EOPNOTSUPP: multi-part segment cannot be used with linux 362 * -ENOMEM : out of memory 363 * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 364 */ 365 int 366 segment_type (char* name) 367 { 368 int rc; 369 struct dcss_segment seg; 370 371 if (!MACHINE_IS_VM) 372 return -ENOSYS; 373 374 dcss_mkname(name, seg.dcss_name); 375 rc = query_segment_type (&seg); 376 if (rc < 0) 377 return rc; 378 return seg.vm_segtype; 379 } 380 381 /* 382 * check if segment collides with other segments that are currently loaded 383 * returns 1 if this is the case, 0 if no collision was found 384 */ 385 static int 386 segment_overlaps_others (struct dcss_segment *seg) 387 { 388 struct list_head *l; 389 struct dcss_segment *tmp; 390 391 BUG_ON(!mutex_is_locked(&dcss_lock)); 392 list_for_each(l, &dcss_list) { 393 tmp = list_entry(l, struct dcss_segment, list); 394 if ((tmp->start_addr >> 20) > (seg->end >> 20)) 395 continue; 396 if ((tmp->end >> 20) < (seg->start_addr >> 20)) 397 continue; 398 if (seg == tmp) 399 continue; 400 return 1; 401 } 402 return 0; 403 } 404 405 /* 406 * real segment loading function, called from segment_load 407 */ 408 static int 409 __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end) 410 { 411 unsigned long start_addr, end_addr, dummy; 412 struct dcss_segment *seg; 413 int rc, diag_cc; 414 415 seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA); 416 if (seg == NULL) { 417 rc = -ENOMEM; 418 goto out; 419 } 420 dcss_mkname (name, seg->dcss_name); 421 rc = query_segment_type (seg); 422 if (rc < 0) 423 goto out_free; 424 425 if (loadshr_scode == DCSS_LOADSHRX) { 426 if (segment_overlaps_others(seg)) { 427 rc = -EBUSY; 428 goto out_free; 429 } 430 } 431 432 rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 433 434 if (rc) 435 goto out_free; 436 437 seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL); 438 if (seg->res == NULL) { 439 rc = -ENOMEM; 440 goto out_shared; 441 } 442 seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; 443 seg->res->start = seg->start_addr; 444 seg->res->end = seg->end; 445 memcpy(&seg->res_name, seg->dcss_name, 8); 446 EBCASC(seg->res_name, 8); 447 seg->res_name[8] = '\0'; 448 strncat(seg->res_name, " (DCSS)", 7); 449 seg->res->name = seg->res_name; 450 rc = seg->vm_segtype; 451 if (rc == SEG_TYPE_SC || 452 ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared)) 453 seg->res->flags |= IORESOURCE_READONLY; 454 if (request_resource(&iomem_resource, seg->res)) { 455 rc = -EBUSY; 456 kfree(seg->res); 457 goto out_shared; 458 } 459 460 if (do_nonshared) 461 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 462 &start_addr, &end_addr); 463 else 464 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 465 &start_addr, &end_addr); 466 if (diag_cc < 0) { 467 dcss_diag(&purgeseg_scode, seg->dcss_name, 468 &dummy, &dummy); 469 rc = diag_cc; 470 goto out_resource; 471 } 472 if (diag_cc > 1) { 473 pr_warning("Loading DCSS %s failed with rc=%ld\n", name, 474 end_addr); 475 rc = dcss_diag_translate_rc(end_addr); 476 dcss_diag(&purgeseg_scode, seg->dcss_name, 477 &dummy, &dummy); 478 goto out_resource; 479 } 480 seg->start_addr = start_addr; 481 seg->end = end_addr; 482 seg->do_nonshared = do_nonshared; 483 atomic_set(&seg->ref_count, 1); 484 list_add(&seg->list, &dcss_list); 485 *addr = seg->start_addr; 486 *end = seg->end; 487 if (do_nonshared) 488 pr_info("DCSS %s of range %p to %p and type %s loaded as " 489 "exclusive-writable\n", name, (void*) seg->start_addr, 490 (void*) seg->end, segtype_string[seg->vm_segtype]); 491 else { 492 pr_info("DCSS %s of range %p to %p and type %s loaded in " 493 "shared access mode\n", name, (void*) seg->start_addr, 494 (void*) seg->end, segtype_string[seg->vm_segtype]); 495 } 496 goto out; 497 out_resource: 498 release_resource(seg->res); 499 kfree(seg->res); 500 out_shared: 501 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 502 out_free: 503 kfree(seg); 504 out: 505 return rc; 506 } 507 508 /* 509 * this function loads a DCSS segment 510 * name : name of the DCSS 511 * do_nonshared : 0 indicates that the dcss should be shared with other linux images 512 * 1 indicates that the dcss should be exclusive for this linux image 513 * addr : will be filled with start address of the segment 514 * end : will be filled with end address of the segment 515 * return values: 516 * -ENOSYS : we are not running on VM 517 * -EIO : could not perform query or load diagnose 518 * -ENOENT : no such segment 519 * -EOPNOTSUPP: multi-part segment cannot be used with linux 520 * -ENOSPC : segment cannot be used (overlaps with storage) 521 * -EBUSY : segment can temporarily not be used (overlaps with dcss) 522 * -ERANGE : segment cannot be used (exceeds kernel mapping range) 523 * -EPERM : segment is currently loaded with incompatible permissions 524 * -ENOMEM : out of memory 525 * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 526 */ 527 int 528 segment_load (char *name, int do_nonshared, unsigned long *addr, 529 unsigned long *end) 530 { 531 struct dcss_segment *seg; 532 int rc; 533 534 if (!MACHINE_IS_VM) 535 return -ENOSYS; 536 537 mutex_lock(&dcss_lock); 538 seg = segment_by_name (name); 539 if (seg == NULL) 540 rc = __segment_load (name, do_nonshared, addr, end); 541 else { 542 if (do_nonshared == seg->do_nonshared) { 543 atomic_inc(&seg->ref_count); 544 *addr = seg->start_addr; 545 *end = seg->end; 546 rc = seg->vm_segtype; 547 } else { 548 *addr = *end = 0; 549 rc = -EPERM; 550 } 551 } 552 mutex_unlock(&dcss_lock); 553 return rc; 554 } 555 556 /* 557 * this function modifies the shared state of a DCSS segment. note that 558 * name : name of the DCSS 559 * do_nonshared : 0 indicates that the dcss should be shared with other linux images 560 * 1 indicates that the dcss should be exclusive for this linux image 561 * return values: 562 * -EIO : could not perform load diagnose (segment gone!) 563 * -ENOENT : no such segment (segment gone!) 564 * -EAGAIN : segment is in use by other exploiters, try later 565 * -EINVAL : no segment with the given name is currently loaded - name invalid 566 * -EBUSY : segment can temporarily not be used (overlaps with dcss) 567 * 0 : operation succeeded 568 */ 569 int 570 segment_modify_shared (char *name, int do_nonshared) 571 { 572 struct dcss_segment *seg; 573 unsigned long start_addr, end_addr, dummy; 574 int rc, diag_cc; 575 576 mutex_lock(&dcss_lock); 577 seg = segment_by_name (name); 578 if (seg == NULL) { 579 rc = -EINVAL; 580 goto out_unlock; 581 } 582 if (do_nonshared == seg->do_nonshared) { 583 pr_info("DCSS %s is already in the requested access " 584 "mode\n", name); 585 rc = 0; 586 goto out_unlock; 587 } 588 if (atomic_read (&seg->ref_count) != 1) { 589 pr_warning("DCSS %s is in use and cannot be reloaded\n", 590 name); 591 rc = -EAGAIN; 592 goto out_unlock; 593 } 594 release_resource(seg->res); 595 if (do_nonshared) 596 seg->res->flags &= ~IORESOURCE_READONLY; 597 else 598 if (seg->vm_segtype == SEG_TYPE_SR || 599 seg->vm_segtype == SEG_TYPE_ER) 600 seg->res->flags |= IORESOURCE_READONLY; 601 602 if (request_resource(&iomem_resource, seg->res)) { 603 pr_warning("DCSS %s overlaps with used memory resources " 604 "and cannot be reloaded\n", name); 605 rc = -EBUSY; 606 kfree(seg->res); 607 goto out_del_mem; 608 } 609 610 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 611 if (do_nonshared) 612 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 613 &start_addr, &end_addr); 614 else 615 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 616 &start_addr, &end_addr); 617 if (diag_cc < 0) { 618 rc = diag_cc; 619 goto out_del_res; 620 } 621 if (diag_cc > 1) { 622 pr_warning("Reloading DCSS %s failed with rc=%ld\n", name, 623 end_addr); 624 rc = dcss_diag_translate_rc(end_addr); 625 goto out_del_res; 626 } 627 seg->start_addr = start_addr; 628 seg->end = end_addr; 629 seg->do_nonshared = do_nonshared; 630 rc = 0; 631 goto out_unlock; 632 out_del_res: 633 release_resource(seg->res); 634 kfree(seg->res); 635 out_del_mem: 636 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 637 list_del(&seg->list); 638 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 639 kfree(seg); 640 out_unlock: 641 mutex_unlock(&dcss_lock); 642 return rc; 643 } 644 645 /* 646 * Decrease the use count of a DCSS segment and remove 647 * it from the address space if nobody is using it 648 * any longer. 649 */ 650 void 651 segment_unload(char *name) 652 { 653 unsigned long dummy; 654 struct dcss_segment *seg; 655 656 if (!MACHINE_IS_VM) 657 return; 658 659 mutex_lock(&dcss_lock); 660 seg = segment_by_name (name); 661 if (seg == NULL) { 662 pr_err("Unloading unknown DCSS %s failed\n", name); 663 goto out_unlock; 664 } 665 if (atomic_dec_return(&seg->ref_count) != 0) 666 goto out_unlock; 667 release_resource(seg->res); 668 kfree(seg->res); 669 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 670 list_del(&seg->list); 671 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 672 kfree(seg); 673 out_unlock: 674 mutex_unlock(&dcss_lock); 675 } 676 677 /* 678 * save segment content permanently 679 */ 680 void 681 segment_save(char *name) 682 { 683 struct dcss_segment *seg; 684 int startpfn = 0; 685 int endpfn = 0; 686 char cmd1[160]; 687 char cmd2[80]; 688 int i, response; 689 690 if (!MACHINE_IS_VM) 691 return; 692 693 mutex_lock(&dcss_lock); 694 seg = segment_by_name (name); 695 696 if (seg == NULL) { 697 pr_err("Saving unknown DCSS %s failed\n", name); 698 goto out; 699 } 700 701 startpfn = seg->start_addr >> PAGE_SHIFT; 702 endpfn = (seg->end) >> PAGE_SHIFT; 703 sprintf(cmd1, "DEFSEG %s", name); 704 for (i=0; i<seg->segcnt; i++) { 705 sprintf(cmd1+strlen(cmd1), " %lX-%lX %s", 706 seg->range[i].start >> PAGE_SHIFT, 707 seg->range[i].end >> PAGE_SHIFT, 708 segtype_string[seg->range[i].start & 0xff]); 709 } 710 sprintf(cmd2, "SAVESEG %s", name); 711 response = 0; 712 cpcmd(cmd1, NULL, 0, &response); 713 if (response) { 714 pr_err("Saving a DCSS failed with DEFSEG response code " 715 "%i\n", response); 716 goto out; 717 } 718 cpcmd(cmd2, NULL, 0, &response); 719 if (response) { 720 pr_err("Saving a DCSS failed with SAVESEG response code " 721 "%i\n", response); 722 goto out; 723 } 724 out: 725 mutex_unlock(&dcss_lock); 726 } 727 728 /* 729 * print appropriate error message for segment_load()/segment_type() 730 * return code 731 */ 732 void segment_warning(int rc, char *seg_name) 733 { 734 switch (rc) { 735 case -ENOENT: 736 pr_err("DCSS %s cannot be loaded or queried\n", seg_name); 737 break; 738 case -ENOSYS: 739 pr_err("DCSS %s cannot be loaded or queried without " 740 "z/VM\n", seg_name); 741 break; 742 case -EIO: 743 pr_err("Loading or querying DCSS %s resulted in a " 744 "hardware error\n", seg_name); 745 break; 746 case -EOPNOTSUPP: 747 pr_err("DCSS %s has multiple page ranges and cannot be " 748 "loaded or queried\n", seg_name); 749 break; 750 case -ENOSPC: 751 pr_err("DCSS %s overlaps with used storage and cannot " 752 "be loaded\n", seg_name); 753 break; 754 case -EBUSY: 755 pr_err("%s needs used memory resources and cannot be " 756 "loaded or queried\n", seg_name); 757 break; 758 case -EPERM: 759 pr_err("DCSS %s is already loaded in a different access " 760 "mode\n", seg_name); 761 break; 762 case -ENOMEM: 763 pr_err("There is not enough memory to load or query " 764 "DCSS %s\n", seg_name); 765 break; 766 case -ERANGE: 767 pr_err("DCSS %s exceeds the kernel mapping range (%lu) " 768 "and cannot be loaded\n", seg_name, VMEM_MAX_PHYS); 769 break; 770 default: 771 break; 772 } 773 } 774 775 EXPORT_SYMBOL(segment_load); 776 EXPORT_SYMBOL(segment_unload); 777 EXPORT_SYMBOL(segment_save); 778 EXPORT_SYMBOL(segment_type); 779 EXPORT_SYMBOL(segment_modify_shared); 780 EXPORT_SYMBOL(segment_warning); 781