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