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 start_addr = end_addr = 0; 416 seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA); 417 if (seg == NULL) { 418 rc = -ENOMEM; 419 goto out; 420 } 421 dcss_mkname (name, seg->dcss_name); 422 rc = query_segment_type (seg); 423 if (rc < 0) 424 goto out_free; 425 426 if (loadshr_scode == DCSS_LOADSHRX) { 427 if (segment_overlaps_others(seg)) { 428 rc = -EBUSY; 429 goto out_free; 430 } 431 } 432 433 rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 434 435 if (rc) 436 goto out_free; 437 438 seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL); 439 if (seg->res == NULL) { 440 rc = -ENOMEM; 441 goto out_shared; 442 } 443 seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; 444 seg->res->start = seg->start_addr; 445 seg->res->end = seg->end; 446 memcpy(&seg->res_name, seg->dcss_name, 8); 447 EBCASC(seg->res_name, 8); 448 seg->res_name[8] = '\0'; 449 strncat(seg->res_name, " (DCSS)", 7); 450 seg->res->name = seg->res_name; 451 rc = seg->vm_segtype; 452 if (rc == SEG_TYPE_SC || 453 ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared)) 454 seg->res->flags |= IORESOURCE_READONLY; 455 if (request_resource(&iomem_resource, seg->res)) { 456 rc = -EBUSY; 457 kfree(seg->res); 458 goto out_shared; 459 } 460 461 if (do_nonshared) 462 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 463 &start_addr, &end_addr); 464 else 465 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 466 &start_addr, &end_addr); 467 if (diag_cc < 0) { 468 dcss_diag(&purgeseg_scode, seg->dcss_name, 469 &dummy, &dummy); 470 rc = diag_cc; 471 goto out_resource; 472 } 473 if (diag_cc > 1) { 474 pr_warning("Loading DCSS %s failed with rc=%ld\n", name, 475 end_addr); 476 rc = dcss_diag_translate_rc(end_addr); 477 dcss_diag(&purgeseg_scode, seg->dcss_name, 478 &dummy, &dummy); 479 goto out_resource; 480 } 481 seg->start_addr = start_addr; 482 seg->end = end_addr; 483 seg->do_nonshared = do_nonshared; 484 atomic_set(&seg->ref_count, 1); 485 list_add(&seg->list, &dcss_list); 486 *addr = seg->start_addr; 487 *end = seg->end; 488 if (do_nonshared) 489 pr_info("DCSS %s of range %p to %p and type %s loaded as " 490 "exclusive-writable\n", name, (void*) seg->start_addr, 491 (void*) seg->end, segtype_string[seg->vm_segtype]); 492 else { 493 pr_info("DCSS %s of range %p to %p and type %s loaded in " 494 "shared access mode\n", name, (void*) seg->start_addr, 495 (void*) seg->end, segtype_string[seg->vm_segtype]); 496 } 497 goto out; 498 out_resource: 499 release_resource(seg->res); 500 kfree(seg->res); 501 out_shared: 502 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 503 out_free: 504 kfree(seg); 505 out: 506 return rc; 507 } 508 509 /* 510 * this function loads a DCSS segment 511 * name : name of the DCSS 512 * do_nonshared : 0 indicates that the dcss should be shared with other linux images 513 * 1 indicates that the dcss should be exclusive for this linux image 514 * addr : will be filled with start address of the segment 515 * end : will be filled with end address of the segment 516 * return values: 517 * -ENOSYS : we are not running on VM 518 * -EIO : could not perform query or load diagnose 519 * -ENOENT : no such segment 520 * -EOPNOTSUPP: multi-part segment cannot be used with linux 521 * -ENOSPC : segment cannot be used (overlaps with storage) 522 * -EBUSY : segment can temporarily not be used (overlaps with dcss) 523 * -ERANGE : segment cannot be used (exceeds kernel mapping range) 524 * -EPERM : segment is currently loaded with incompatible permissions 525 * -ENOMEM : out of memory 526 * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 527 */ 528 int 529 segment_load (char *name, int do_nonshared, unsigned long *addr, 530 unsigned long *end) 531 { 532 struct dcss_segment *seg; 533 int rc; 534 535 if (!MACHINE_IS_VM) 536 return -ENOSYS; 537 538 mutex_lock(&dcss_lock); 539 seg = segment_by_name (name); 540 if (seg == NULL) 541 rc = __segment_load (name, do_nonshared, addr, end); 542 else { 543 if (do_nonshared == seg->do_nonshared) { 544 atomic_inc(&seg->ref_count); 545 *addr = seg->start_addr; 546 *end = seg->end; 547 rc = seg->vm_segtype; 548 } else { 549 *addr = *end = 0; 550 rc = -EPERM; 551 } 552 } 553 mutex_unlock(&dcss_lock); 554 return rc; 555 } 556 557 /* 558 * this function modifies the shared state of a DCSS segment. note that 559 * name : name of the DCSS 560 * do_nonshared : 0 indicates that the dcss should be shared with other linux images 561 * 1 indicates that the dcss should be exclusive for this linux image 562 * return values: 563 * -EIO : could not perform load diagnose (segment gone!) 564 * -ENOENT : no such segment (segment gone!) 565 * -EAGAIN : segment is in use by other exploiters, try later 566 * -EINVAL : no segment with the given name is currently loaded - name invalid 567 * -EBUSY : segment can temporarily not be used (overlaps with dcss) 568 * 0 : operation succeeded 569 */ 570 int 571 segment_modify_shared (char *name, int do_nonshared) 572 { 573 struct dcss_segment *seg; 574 unsigned long start_addr, end_addr, dummy; 575 int rc, diag_cc; 576 577 start_addr = end_addr = 0; 578 mutex_lock(&dcss_lock); 579 seg = segment_by_name (name); 580 if (seg == NULL) { 581 rc = -EINVAL; 582 goto out_unlock; 583 } 584 if (do_nonshared == seg->do_nonshared) { 585 pr_info("DCSS %s is already in the requested access " 586 "mode\n", name); 587 rc = 0; 588 goto out_unlock; 589 } 590 if (atomic_read (&seg->ref_count) != 1) { 591 pr_warning("DCSS %s is in use and cannot be reloaded\n", 592 name); 593 rc = -EAGAIN; 594 goto out_unlock; 595 } 596 release_resource(seg->res); 597 if (do_nonshared) 598 seg->res->flags &= ~IORESOURCE_READONLY; 599 else 600 if (seg->vm_segtype == SEG_TYPE_SR || 601 seg->vm_segtype == SEG_TYPE_ER) 602 seg->res->flags |= IORESOURCE_READONLY; 603 604 if (request_resource(&iomem_resource, seg->res)) { 605 pr_warning("DCSS %s overlaps with used memory resources " 606 "and cannot be reloaded\n", name); 607 rc = -EBUSY; 608 kfree(seg->res); 609 goto out_del_mem; 610 } 611 612 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 613 if (do_nonshared) 614 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 615 &start_addr, &end_addr); 616 else 617 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 618 &start_addr, &end_addr); 619 if (diag_cc < 0) { 620 rc = diag_cc; 621 goto out_del_res; 622 } 623 if (diag_cc > 1) { 624 pr_warning("Reloading DCSS %s failed with rc=%ld\n", name, 625 end_addr); 626 rc = dcss_diag_translate_rc(end_addr); 627 goto out_del_res; 628 } 629 seg->start_addr = start_addr; 630 seg->end = end_addr; 631 seg->do_nonshared = do_nonshared; 632 rc = 0; 633 goto out_unlock; 634 out_del_res: 635 release_resource(seg->res); 636 kfree(seg->res); 637 out_del_mem: 638 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 639 list_del(&seg->list); 640 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 641 kfree(seg); 642 out_unlock: 643 mutex_unlock(&dcss_lock); 644 return rc; 645 } 646 647 /* 648 * Decrease the use count of a DCSS segment and remove 649 * it from the address space if nobody is using it 650 * any longer. 651 */ 652 void 653 segment_unload(char *name) 654 { 655 unsigned long dummy; 656 struct dcss_segment *seg; 657 658 if (!MACHINE_IS_VM) 659 return; 660 661 mutex_lock(&dcss_lock); 662 seg = segment_by_name (name); 663 if (seg == NULL) { 664 pr_err("Unloading unknown DCSS %s failed\n", name); 665 goto out_unlock; 666 } 667 if (atomic_dec_return(&seg->ref_count) != 0) 668 goto out_unlock; 669 release_resource(seg->res); 670 kfree(seg->res); 671 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 672 list_del(&seg->list); 673 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 674 kfree(seg); 675 out_unlock: 676 mutex_unlock(&dcss_lock); 677 } 678 679 /* 680 * save segment content permanently 681 */ 682 void 683 segment_save(char *name) 684 { 685 struct dcss_segment *seg; 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 sprintf(cmd1, "DEFSEG %s", name); 702 for (i=0; i<seg->segcnt; i++) { 703 sprintf(cmd1+strlen(cmd1), " %lX-%lX %s", 704 seg->range[i].start >> PAGE_SHIFT, 705 seg->range[i].end >> PAGE_SHIFT, 706 segtype_string[seg->range[i].start & 0xff]); 707 } 708 sprintf(cmd2, "SAVESEG %s", name); 709 response = 0; 710 cpcmd(cmd1, NULL, 0, &response); 711 if (response) { 712 pr_err("Saving a DCSS failed with DEFSEG response code " 713 "%i\n", response); 714 goto out; 715 } 716 cpcmd(cmd2, NULL, 0, &response); 717 if (response) { 718 pr_err("Saving a DCSS failed with SAVESEG response code " 719 "%i\n", response); 720 goto out; 721 } 722 out: 723 mutex_unlock(&dcss_lock); 724 } 725 726 /* 727 * print appropriate error message for segment_load()/segment_type() 728 * return code 729 */ 730 void segment_warning(int rc, char *seg_name) 731 { 732 switch (rc) { 733 case -ENOENT: 734 pr_err("DCSS %s cannot be loaded or queried\n", seg_name); 735 break; 736 case -ENOSYS: 737 pr_err("DCSS %s cannot be loaded or queried without " 738 "z/VM\n", seg_name); 739 break; 740 case -EIO: 741 pr_err("Loading or querying DCSS %s resulted in a " 742 "hardware error\n", seg_name); 743 break; 744 case -EOPNOTSUPP: 745 pr_err("DCSS %s has multiple page ranges and cannot be " 746 "loaded or queried\n", seg_name); 747 break; 748 case -ENOSPC: 749 pr_err("DCSS %s overlaps with used storage and cannot " 750 "be loaded\n", seg_name); 751 break; 752 case -EBUSY: 753 pr_err("%s needs used memory resources and cannot be " 754 "loaded or queried\n", seg_name); 755 break; 756 case -EPERM: 757 pr_err("DCSS %s is already loaded in a different access " 758 "mode\n", seg_name); 759 break; 760 case -ENOMEM: 761 pr_err("There is not enough memory to load or query " 762 "DCSS %s\n", seg_name); 763 break; 764 case -ERANGE: 765 pr_err("DCSS %s exceeds the kernel mapping range (%lu) " 766 "and cannot be loaded\n", seg_name, VMEM_MAX_PHYS); 767 break; 768 default: 769 break; 770 } 771 } 772 773 EXPORT_SYMBOL(segment_load); 774 EXPORT_SYMBOL(segment_unload); 775 EXPORT_SYMBOL(segment_save); 776 EXPORT_SYMBOL(segment_type); 777 EXPORT_SYMBOL(segment_modify_shared); 778 EXPORT_SYMBOL(segment_warning); 779