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