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