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