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_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 struct qin64 *qin = kmalloc (sizeof(struct qin64), GFP_DMA); 256 struct qout64 *qout = kmalloc (sizeof(struct qout64), GFP_DMA); 257 258 int diag_cc, rc, i; 259 unsigned long dummy, vmrc; 260 261 if ((qin == NULL) || (qout == NULL)) { 262 rc = -ENOMEM; 263 goto out_free; 264 } 265 266 /* initialize diag input parameters */ 267 qin->qopcode = DCSS_FINDSEGA; 268 qin->qoutptr = (unsigned long) qout; 269 qin->qoutlen = sizeof(struct qout64); 270 memcpy (qin->qname, seg->dcss_name, 8); 271 272 diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc); 273 274 if (diag_cc < 0) { 275 rc = diag_cc; 276 goto out_free; 277 } 278 if (diag_cc > 1) { 279 pr_warning("Querying a DCSS type failed with rc=%ld\n", vmrc); 280 rc = dcss_diag_translate_rc (vmrc); 281 goto out_free; 282 } 283 284 #ifdef CONFIG_64BIT 285 /* Only old format of output area of Diagnose x'64' is supported, 286 copy data for the new format. */ 287 if (segext_scode == DCSS_SEGEXT) { 288 struct qout64_old *qout_old; 289 qout_old = kzalloc(sizeof(struct qout64_old), GFP_DMA); 290 if (qout_old == NULL) { 291 rc = -ENOMEM; 292 goto out_free; 293 } 294 memcpy(qout_old, qout, sizeof(struct qout64_old)); 295 qout->segstart = (unsigned long) qout_old->segstart; 296 qout->segend = (unsigned long) qout_old->segend; 297 qout->segcnt = qout_old->segcnt; 298 qout->segrcnt = qout_old->segrcnt; 299 300 if (qout->segcnt > 6) 301 qout->segrcnt = 6; 302 for (i = 0; i < qout->segrcnt; i++) { 303 qout->range[i].start = 304 (unsigned long) qout_old->range[i].start; 305 qout->range[i].end = 306 (unsigned long) qout_old->range[i].end; 307 } 308 kfree(qout_old); 309 } 310 #endif 311 if (qout->segcnt > 6) { 312 rc = -ENOTSUPP; 313 goto out_free; 314 } 315 316 if (qout->segcnt == 1) { 317 seg->vm_segtype = qout->range[0].start & 0xff; 318 } else { 319 /* multi-part segment. only one type supported here: 320 - all parts are contiguous 321 - all parts are either EW or EN type 322 - maximum 6 parts allowed */ 323 unsigned long start = qout->segstart >> PAGE_SHIFT; 324 for (i=0; i<qout->segcnt; i++) { 325 if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) && 326 ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) { 327 rc = -ENOTSUPP; 328 goto out_free; 329 } 330 if (start != qout->range[i].start >> PAGE_SHIFT) { 331 rc = -ENOTSUPP; 332 goto out_free; 333 } 334 start = (qout->range[i].end >> PAGE_SHIFT) + 1; 335 } 336 seg->vm_segtype = SEG_TYPE_EWEN; 337 } 338 339 /* analyze diag output and update seg */ 340 seg->start_addr = qout->segstart; 341 seg->end = qout->segend; 342 343 memcpy (seg->range, qout->range, 6*sizeof(struct qrange)); 344 seg->segcnt = qout->segcnt; 345 346 rc = 0; 347 348 out_free: 349 kfree(qin); 350 kfree(qout); 351 return rc; 352 } 353 354 /* 355 * get info about a segment 356 * possible return values: 357 * -ENOSYS : we are not running on VM 358 * -EIO : could not perform query diagnose 359 * -ENOENT : no such segment 360 * -ENOTSUPP: multi-part segment cannot be used with linux 361 * -ENOMEM : out of memory 362 * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 363 */ 364 int 365 segment_type (char* name) 366 { 367 int rc; 368 struct dcss_segment seg; 369 370 if (!MACHINE_IS_VM) 371 return -ENOSYS; 372 373 dcss_mkname(name, seg.dcss_name); 374 rc = query_segment_type (&seg); 375 if (rc < 0) 376 return rc; 377 return seg.vm_segtype; 378 } 379 380 /* 381 * check if segment collides with other segments that are currently loaded 382 * returns 1 if this is the case, 0 if no collision was found 383 */ 384 static int 385 segment_overlaps_others (struct dcss_segment *seg) 386 { 387 struct list_head *l; 388 struct dcss_segment *tmp; 389 390 BUG_ON(!mutex_is_locked(&dcss_lock)); 391 list_for_each(l, &dcss_list) { 392 tmp = list_entry(l, struct dcss_segment, list); 393 if ((tmp->start_addr >> 20) > (seg->end >> 20)) 394 continue; 395 if ((tmp->end >> 20) < (seg->start_addr >> 20)) 396 continue; 397 if (seg == tmp) 398 continue; 399 return 1; 400 } 401 return 0; 402 } 403 404 /* 405 * real segment loading function, called from segment_load 406 */ 407 static int 408 __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end) 409 { 410 struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment), 411 GFP_DMA); 412 int rc, diag_cc; 413 unsigned long start_addr, end_addr, dummy; 414 415 if (seg == NULL) { 416 rc = -ENOMEM; 417 goto out; 418 } 419 dcss_mkname (name, seg->dcss_name); 420 rc = query_segment_type (seg); 421 if (rc < 0) 422 goto out_free; 423 424 if (loadshr_scode == DCSS_LOADSHRX) { 425 if (segment_overlaps_others(seg)) { 426 rc = -EBUSY; 427 goto out_free; 428 } 429 } 430 431 rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 432 433 if (rc) 434 goto out_free; 435 436 seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL); 437 if (seg->res == NULL) { 438 rc = -ENOMEM; 439 goto out_shared; 440 } 441 seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; 442 seg->res->start = seg->start_addr; 443 seg->res->end = seg->end; 444 memcpy(&seg->res_name, seg->dcss_name, 8); 445 EBCASC(seg->res_name, 8); 446 seg->res_name[8] = '\0'; 447 strncat(seg->res_name, " (DCSS)", 7); 448 seg->res->name = seg->res_name; 449 rc = seg->vm_segtype; 450 if (rc == SEG_TYPE_SC || 451 ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared)) 452 seg->res->flags |= IORESOURCE_READONLY; 453 if (request_resource(&iomem_resource, seg->res)) { 454 rc = -EBUSY; 455 kfree(seg->res); 456 goto out_shared; 457 } 458 459 if (do_nonshared) 460 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 461 &start_addr, &end_addr); 462 else 463 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 464 &start_addr, &end_addr); 465 if (diag_cc < 0) { 466 dcss_diag(&purgeseg_scode, seg->dcss_name, 467 &dummy, &dummy); 468 rc = diag_cc; 469 goto out_resource; 470 } 471 if (diag_cc > 1) { 472 pr_warning("Loading DCSS %s failed with rc=%ld\n", name, 473 end_addr); 474 rc = dcss_diag_translate_rc(end_addr); 475 dcss_diag(&purgeseg_scode, seg->dcss_name, 476 &dummy, &dummy); 477 goto out_resource; 478 } 479 seg->start_addr = start_addr; 480 seg->end = end_addr; 481 seg->do_nonshared = do_nonshared; 482 atomic_set(&seg->ref_count, 1); 483 list_add(&seg->list, &dcss_list); 484 *addr = seg->start_addr; 485 *end = seg->end; 486 if (do_nonshared) 487 pr_info("DCSS %s of range %p to %p and type %s loaded as " 488 "exclusive-writable\n", name, (void*) seg->start_addr, 489 (void*) seg->end, segtype_string[seg->vm_segtype]); 490 else { 491 pr_info("DCSS %s of range %p to %p and type %s loaded in " 492 "shared access mode\n", name, (void*) seg->start_addr, 493 (void*) seg->end, segtype_string[seg->vm_segtype]); 494 } 495 goto out; 496 out_resource: 497 release_resource(seg->res); 498 kfree(seg->res); 499 out_shared: 500 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 501 out_free: 502 kfree(seg); 503 out: 504 return rc; 505 } 506 507 /* 508 * this function loads a DCSS segment 509 * name : name of the DCSS 510 * do_nonshared : 0 indicates that the dcss should be shared with other linux images 511 * 1 indicates that the dcss should be exclusive for this linux image 512 * addr : will be filled with start address of the segment 513 * end : will be filled with end address of the segment 514 * return values: 515 * -ENOSYS : we are not running on VM 516 * -EIO : could not perform query or load diagnose 517 * -ENOENT : no such segment 518 * -ENOTSUPP: multi-part segment cannot be used with linux 519 * -ENOSPC : segment cannot be used (overlaps with storage) 520 * -EBUSY : segment can temporarily not be used (overlaps with dcss) 521 * -ERANGE : segment cannot be used (exceeds kernel mapping range) 522 * -EPERM : segment is currently loaded with incompatible permissions 523 * -ENOMEM : out of memory 524 * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 525 */ 526 int 527 segment_load (char *name, int do_nonshared, unsigned long *addr, 528 unsigned long *end) 529 { 530 struct dcss_segment *seg; 531 int rc; 532 533 if (!MACHINE_IS_VM) 534 return -ENOSYS; 535 536 mutex_lock(&dcss_lock); 537 seg = segment_by_name (name); 538 if (seg == NULL) 539 rc = __segment_load (name, do_nonshared, addr, end); 540 else { 541 if (do_nonshared == seg->do_nonshared) { 542 atomic_inc(&seg->ref_count); 543 *addr = seg->start_addr; 544 *end = seg->end; 545 rc = seg->vm_segtype; 546 } else { 547 *addr = *end = 0; 548 rc = -EPERM; 549 } 550 } 551 mutex_unlock(&dcss_lock); 552 return rc; 553 } 554 555 /* 556 * this function modifies the shared state of a DCSS segment. note that 557 * name : name of the DCSS 558 * do_nonshared : 0 indicates that the dcss should be shared with other linux images 559 * 1 indicates that the dcss should be exclusive for this linux image 560 * return values: 561 * -EIO : could not perform load diagnose (segment gone!) 562 * -ENOENT : no such segment (segment gone!) 563 * -EAGAIN : segment is in use by other exploiters, try later 564 * -EINVAL : no segment with the given name is currently loaded - name invalid 565 * -EBUSY : segment can temporarily not be used (overlaps with dcss) 566 * 0 : operation succeeded 567 */ 568 int 569 segment_modify_shared (char *name, int do_nonshared) 570 { 571 struct dcss_segment *seg; 572 unsigned long start_addr, end_addr, dummy; 573 int rc, diag_cc; 574 575 mutex_lock(&dcss_lock); 576 seg = segment_by_name (name); 577 if (seg == NULL) { 578 rc = -EINVAL; 579 goto out_unlock; 580 } 581 if (do_nonshared == seg->do_nonshared) { 582 pr_info("DCSS %s is already in the requested access " 583 "mode\n", name); 584 rc = 0; 585 goto out_unlock; 586 } 587 if (atomic_read (&seg->ref_count) != 1) { 588 pr_warning("DCSS %s is in use and cannot be reloaded\n", 589 name); 590 rc = -EAGAIN; 591 goto out_unlock; 592 } 593 release_resource(seg->res); 594 if (do_nonshared) 595 seg->res->flags &= ~IORESOURCE_READONLY; 596 else 597 if (seg->vm_segtype == SEG_TYPE_SR || 598 seg->vm_segtype == SEG_TYPE_ER) 599 seg->res->flags |= IORESOURCE_READONLY; 600 601 if (request_resource(&iomem_resource, seg->res)) { 602 pr_warning("DCSS %s overlaps with used memory resources " 603 "and cannot be reloaded\n", name); 604 rc = -EBUSY; 605 kfree(seg->res); 606 goto out_del_mem; 607 } 608 609 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 610 if (do_nonshared) 611 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 612 &start_addr, &end_addr); 613 else 614 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 615 &start_addr, &end_addr); 616 if (diag_cc < 0) { 617 rc = diag_cc; 618 goto out_del_res; 619 } 620 if (diag_cc > 1) { 621 pr_warning("Reloading DCSS %s failed with rc=%ld\n", name, 622 end_addr); 623 rc = dcss_diag_translate_rc(end_addr); 624 goto out_del_res; 625 } 626 seg->start_addr = start_addr; 627 seg->end = end_addr; 628 seg->do_nonshared = do_nonshared; 629 rc = 0; 630 goto out_unlock; 631 out_del_res: 632 release_resource(seg->res); 633 kfree(seg->res); 634 out_del_mem: 635 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 636 list_del(&seg->list); 637 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 638 kfree(seg); 639 out_unlock: 640 mutex_unlock(&dcss_lock); 641 return rc; 642 } 643 644 /* 645 * Decrease the use count of a DCSS segment and remove 646 * it from the address space if nobody is using it 647 * any longer. 648 */ 649 void 650 segment_unload(char *name) 651 { 652 unsigned long dummy; 653 struct dcss_segment *seg; 654 655 if (!MACHINE_IS_VM) 656 return; 657 658 mutex_lock(&dcss_lock); 659 seg = segment_by_name (name); 660 if (seg == NULL) { 661 pr_err("Unloading unknown DCSS %s failed\n", name); 662 goto out_unlock; 663 } 664 if (atomic_dec_return(&seg->ref_count) != 0) 665 goto out_unlock; 666 release_resource(seg->res); 667 kfree(seg->res); 668 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 669 list_del(&seg->list); 670 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 671 kfree(seg); 672 out_unlock: 673 mutex_unlock(&dcss_lock); 674 } 675 676 /* 677 * save segment content permanently 678 */ 679 void 680 segment_save(char *name) 681 { 682 struct dcss_segment *seg; 683 int startpfn = 0; 684 int endpfn = 0; 685 char cmd1[160]; 686 char cmd2[80]; 687 int i, response; 688 689 if (!MACHINE_IS_VM) 690 return; 691 692 mutex_lock(&dcss_lock); 693 seg = segment_by_name (name); 694 695 if (seg == NULL) { 696 pr_err("Saving unknown DCSS %s failed\n", name); 697 goto out; 698 } 699 700 startpfn = seg->start_addr >> PAGE_SHIFT; 701 endpfn = (seg->end) >> PAGE_SHIFT; 702 sprintf(cmd1, "DEFSEG %s", name); 703 for (i=0; i<seg->segcnt; i++) { 704 sprintf(cmd1+strlen(cmd1), " %lX-%lX %s", 705 seg->range[i].start >> PAGE_SHIFT, 706 seg->range[i].end >> PAGE_SHIFT, 707 segtype_string[seg->range[i].start & 0xff]); 708 } 709 sprintf(cmd2, "SAVESEG %s", name); 710 response = 0; 711 cpcmd(cmd1, NULL, 0, &response); 712 if (response) { 713 pr_err("Saving a DCSS failed with DEFSEG response code " 714 "%i\n", response); 715 goto out; 716 } 717 cpcmd(cmd2, NULL, 0, &response); 718 if (response) { 719 pr_err("Saving a DCSS failed with SAVESEG response code " 720 "%i\n", response); 721 goto out; 722 } 723 out: 724 mutex_unlock(&dcss_lock); 725 } 726 727 /* 728 * print appropriate error message for segment_load()/segment_type() 729 * return code 730 */ 731 void segment_warning(int rc, char *seg_name) 732 { 733 switch (rc) { 734 case -ENOENT: 735 pr_err("DCSS %s cannot be loaded or queried\n", seg_name); 736 break; 737 case -ENOSYS: 738 pr_err("DCSS %s cannot be loaded or queried without " 739 "z/VM\n", seg_name); 740 break; 741 case -EIO: 742 pr_err("Loading or querying DCSS %s resulted in a " 743 "hardware error\n", seg_name); 744 break; 745 case -ENOTSUPP: 746 pr_err("DCSS %s has multiple page ranges and cannot be " 747 "loaded or queried\n", seg_name); 748 break; 749 case -ENOSPC: 750 pr_err("DCSS %s overlaps with used storage and cannot " 751 "be loaded\n", seg_name); 752 break; 753 case -EBUSY: 754 pr_err("%s needs used memory resources and cannot be " 755 "loaded or queried\n", seg_name); 756 break; 757 case -EPERM: 758 pr_err("DCSS %s is already loaded in a different access " 759 "mode\n", seg_name); 760 break; 761 case -ENOMEM: 762 pr_err("There is not enough memory to load or query " 763 "DCSS %s\n", seg_name); 764 break; 765 case -ERANGE: 766 pr_err("DCSS %s exceeds the kernel mapping range (%lu) " 767 "and cannot be loaded\n", seg_name, VMEM_MAX_PHYS); 768 break; 769 default: 770 break; 771 } 772 } 773 774 EXPORT_SYMBOL(segment_load); 775 EXPORT_SYMBOL(segment_unload); 776 EXPORT_SYMBOL(segment_save); 777 EXPORT_SYMBOL(segment_type); 778 EXPORT_SYMBOL(segment_modify_shared); 779 EXPORT_SYMBOL(segment_warning); 780