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/page.h> 22#include <asm/pgtable.h> 23#include <asm/ebcdic.h> 24#include <asm/errno.h> 25#include <asm/extmem.h> 26#include <asm/cpcmd.h> 27#include <asm/setup.h> 28 29#define DCSS_LOADSHR 0x00 30#define DCSS_LOADNSR 0x04 31#define DCSS_PURGESEG 0x08 32#define DCSS_FINDSEG 0x0c 33#define DCSS_LOADNOLY 0x10 34#define DCSS_SEGEXT 0x18 35#define DCSS_LOADSHRX 0x20 36#define DCSS_LOADNSRX 0x24 37#define DCSS_FINDSEGX 0x2c 38#define DCSS_SEGEXTX 0x38 39#define DCSS_FINDSEGA 0x0c 40 41struct qrange { 42 unsigned long start; /* last byte type */ 43 unsigned long end; /* last byte reserved */ 44}; 45 46struct qout64 { 47 unsigned long segstart; 48 unsigned long segend; 49 int segcnt; 50 int segrcnt; 51 struct qrange range[6]; 52}; 53 54struct qrange_old { 55 unsigned int start; /* last byte type */ 56 unsigned int end; /* last byte reserved */ 57}; 58 59/* output area format for the Diag x'64' old subcode x'18' */ 60struct qout64_old { 61 int segstart; 62 int segend; 63 int segcnt; 64 int segrcnt; 65 struct qrange_old range[6]; 66}; 67 68struct qin64 { 69 char qopcode; 70 char rsrv1[3]; 71 char qrcode; 72 char rsrv2[3]; 73 char qname[8]; 74 unsigned int qoutptr; 75 short int qoutlen; 76}; 77 78struct dcss_segment { 79 struct list_head list; 80 char dcss_name[8]; 81 char res_name[15]; 82 unsigned long start_addr; 83 unsigned long end; 84 atomic_t ref_count; 85 int do_nonshared; 86 unsigned int vm_segtype; 87 struct qrange range[6]; 88 int segcnt; 89 struct resource *res; 90}; 91 92static DEFINE_MUTEX(dcss_lock); 93static LIST_HEAD(dcss_list); 94static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", 95 "EW/EN-MIXED" }; 96static int loadshr_scode, loadnsr_scode, findseg_scode; 97static int segext_scode, purgeseg_scode; 98static int scode_set; 99 100/* set correct Diag x'64' subcodes. */ 101static int 102dcss_set_subcodes(void) 103{ 104 char *name = kmalloc(8 * sizeof(char), GFP_KERNEL | GFP_DMA); 105 unsigned long rx, ry; 106 int rc; 107 108 if (name == NULL) 109 return -ENOMEM; 110 111 rx = (unsigned long) name; 112 ry = DCSS_FINDSEGX; 113 114 strcpy(name, "dummy"); 115 asm volatile( 116 " diag %0,%1,0x64\n" 117 "0: ipm %2\n" 118 " srl %2,28\n" 119 " j 2f\n" 120 "1: la %2,3\n" 121 "2:\n" 122 EX_TABLE(0b, 1b) 123 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); 124 125 kfree(name); 126 /* Diag x'64' new subcodes are supported, set to new subcodes */ 127 if (rc != 3) { 128 loadshr_scode = DCSS_LOADSHRX; 129 loadnsr_scode = DCSS_LOADNSRX; 130 purgeseg_scode = DCSS_PURGESEG; 131 findseg_scode = DCSS_FINDSEGX; 132 segext_scode = DCSS_SEGEXTX; 133 return 0; 134 } 135 /* Diag x'64' new subcodes are not supported, set to old subcodes */ 136 loadshr_scode = DCSS_LOADNOLY; 137 loadnsr_scode = DCSS_LOADNSR; 138 purgeseg_scode = DCSS_PURGESEG; 139 findseg_scode = DCSS_FINDSEG; 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 */ 148static void 149dcss_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 */ 168static struct dcss_segment * 169segment_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 */ 191static inline int 192dcss_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 if (*func > DCSS_SEGEXT) 209 asm volatile( 210 " diag %0,%1,0x64\n" 211 " ipm %2\n" 212 " srl %2,28\n" 213 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); 214 /* 31-bit Diag x'64' old subcode, switch to 31-bit addressing mode */ 215 else 216 asm volatile( 217 " sam31\n" 218 " diag %0,%1,0x64\n" 219 " sam64\n" 220 " ipm %2\n" 221 " srl %2,28\n" 222 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); 223 *ret1 = rx; 224 *ret2 = ry; 225 return rc; 226} 227 228static inline int 229dcss_diag_translate_rc (int vm_rc) { 230 if (vm_rc == 44) 231 return -ENOENT; 232 return -EIO; 233} 234 235 236/* do a diag to get info about a segment. 237 * fills start_address, end and vm_segtype fields 238 */ 239static int 240query_segment_type (struct dcss_segment *seg) 241{ 242 unsigned long dummy, vmrc; 243 int diag_cc, rc, i; 244 struct qout64 *qout; 245 struct qin64 *qin; 246 247 qin = kmalloc(sizeof(*qin), GFP_KERNEL | GFP_DMA); 248 qout = kmalloc(sizeof(*qout), GFP_KERNEL | GFP_DMA); 249 if ((qin == NULL) || (qout == NULL)) { 250 rc = -ENOMEM; 251 goto out_free; 252 } 253 254 /* initialize diag input parameters */ 255 qin->qopcode = DCSS_FINDSEGA; 256 qin->qoutptr = (unsigned long) qout; 257 qin->qoutlen = sizeof(struct qout64); 258 memcpy (qin->qname, seg->dcss_name, 8); 259 260 diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc); 261 262 if (diag_cc < 0) { 263 rc = diag_cc; 264 goto out_free; 265 } 266 if (diag_cc > 1) { 267 pr_warning("Querying a DCSS type failed with rc=%ld\n", vmrc); 268 rc = dcss_diag_translate_rc (vmrc); 269 goto out_free; 270 } 271 272 /* Only old format of output area of Diagnose x'64' is supported, 273 copy data for the new format. */ 274 if (segext_scode == DCSS_SEGEXT) { 275 struct qout64_old *qout_old; 276 qout_old = kzalloc(sizeof(*qout_old), GFP_KERNEL | GFP_DMA); 277 if (qout_old == NULL) { 278 rc = -ENOMEM; 279 goto out_free; 280 } 281 memcpy(qout_old, qout, sizeof(struct qout64_old)); 282 qout->segstart = (unsigned long) qout_old->segstart; 283 qout->segend = (unsigned long) qout_old->segend; 284 qout->segcnt = qout_old->segcnt; 285 qout->segrcnt = qout_old->segrcnt; 286 287 if (qout->segcnt > 6) 288 qout->segrcnt = 6; 289 for (i = 0; i < qout->segrcnt; i++) { 290 qout->range[i].start = 291 (unsigned long) qout_old->range[i].start; 292 qout->range[i].end = 293 (unsigned long) qout_old->range[i].end; 294 } 295 kfree(qout_old); 296 } 297 if (qout->segcnt > 6) { 298 rc = -EOPNOTSUPP; 299 goto out_free; 300 } 301 302 if (qout->segcnt == 1) { 303 seg->vm_segtype = qout->range[0].start & 0xff; 304 } else { 305 /* multi-part segment. only one type supported here: 306 - all parts are contiguous 307 - all parts are either EW or EN type 308 - maximum 6 parts allowed */ 309 unsigned long start = qout->segstart >> PAGE_SHIFT; 310 for (i=0; i<qout->segcnt; i++) { 311 if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) && 312 ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) { 313 rc = -EOPNOTSUPP; 314 goto out_free; 315 } 316 if (start != qout->range[i].start >> PAGE_SHIFT) { 317 rc = -EOPNOTSUPP; 318 goto out_free; 319 } 320 start = (qout->range[i].end >> PAGE_SHIFT) + 1; 321 } 322 seg->vm_segtype = SEG_TYPE_EWEN; 323 } 324 325 /* analyze diag output and update seg */ 326 seg->start_addr = qout->segstart; 327 seg->end = qout->segend; 328 329 memcpy (seg->range, qout->range, 6*sizeof(struct qrange)); 330 seg->segcnt = qout->segcnt; 331 332 rc = 0; 333 334 out_free: 335 kfree(qin); 336 kfree(qout); 337 return rc; 338} 339 340/* 341 * get info about a segment 342 * possible return values: 343 * -ENOSYS : we are not running on VM 344 * -EIO : could not perform query diagnose 345 * -ENOENT : no such segment 346 * -EOPNOTSUPP: multi-part segment cannot be used with linux 347 * -ENOMEM : out of memory 348 * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 349 */ 350int 351segment_type (char* name) 352{ 353 int rc; 354 struct dcss_segment seg; 355 356 if (!MACHINE_IS_VM) 357 return -ENOSYS; 358 359 dcss_mkname(name, seg.dcss_name); 360 rc = query_segment_type (&seg); 361 if (rc < 0) 362 return rc; 363 return seg.vm_segtype; 364} 365 366/* 367 * check if segment collides with other segments that are currently loaded 368 * returns 1 if this is the case, 0 if no collision was found 369 */ 370static int 371segment_overlaps_others (struct dcss_segment *seg) 372{ 373 struct list_head *l; 374 struct dcss_segment *tmp; 375 376 BUG_ON(!mutex_is_locked(&dcss_lock)); 377 list_for_each(l, &dcss_list) { 378 tmp = list_entry(l, struct dcss_segment, list); 379 if ((tmp->start_addr >> 20) > (seg->end >> 20)) 380 continue; 381 if ((tmp->end >> 20) < (seg->start_addr >> 20)) 382 continue; 383 if (seg == tmp) 384 continue; 385 return 1; 386 } 387 return 0; 388} 389 390/* 391 * real segment loading function, called from segment_load 392 */ 393static int 394__segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end) 395{ 396 unsigned long start_addr, end_addr, dummy; 397 struct dcss_segment *seg; 398 int rc, diag_cc; 399 400 start_addr = end_addr = 0; 401 seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA); 402 if (seg == NULL) { 403 rc = -ENOMEM; 404 goto out; 405 } 406 dcss_mkname (name, seg->dcss_name); 407 rc = query_segment_type (seg); 408 if (rc < 0) 409 goto out_free; 410 411 if (loadshr_scode == DCSS_LOADSHRX) { 412 if (segment_overlaps_others(seg)) { 413 rc = -EBUSY; 414 goto out_free; 415 } 416 } 417 418 rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 419 420 if (rc) 421 goto out_free; 422 423 seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL); 424 if (seg->res == NULL) { 425 rc = -ENOMEM; 426 goto out_shared; 427 } 428 seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; 429 seg->res->start = seg->start_addr; 430 seg->res->end = seg->end; 431 memcpy(&seg->res_name, seg->dcss_name, 8); 432 EBCASC(seg->res_name, 8); 433 seg->res_name[8] = '\0'; 434 strncat(seg->res_name, " (DCSS)", 7); 435 seg->res->name = seg->res_name; 436 rc = seg->vm_segtype; 437 if (rc == SEG_TYPE_SC || 438 ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared)) 439 seg->res->flags |= IORESOURCE_READONLY; 440 if (request_resource(&iomem_resource, seg->res)) { 441 rc = -EBUSY; 442 kfree(seg->res); 443 goto out_shared; 444 } 445 446 if (do_nonshared) 447 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 448 &start_addr, &end_addr); 449 else 450 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 451 &start_addr, &end_addr); 452 if (diag_cc < 0) { 453 dcss_diag(&purgeseg_scode, seg->dcss_name, 454 &dummy, &dummy); 455 rc = diag_cc; 456 goto out_resource; 457 } 458 if (diag_cc > 1) { 459 pr_warning("Loading DCSS %s failed with rc=%ld\n", name, 460 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 */ 513int 514segment_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 */ 555int 556segment_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_warning("DCSS %s is in use and cannot be reloaded\n", 577 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_warning("DCSS %s overlaps with used memory resources " 591 "and cannot be reloaded\n", 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_warning("Reloading DCSS %s failed with rc=%ld\n", name, 610 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 */ 637void 638segment_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); 660out_unlock: 661 mutex_unlock(&dcss_lock); 662} 663 664/* 665 * save segment content permanently 666 */ 667void 668segment_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 } 707out: 708 mutex_unlock(&dcss_lock); 709} 710 711/* 712 * print appropriate error message for segment_load()/segment_type() 713 * return code 714 */ 715void 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 758EXPORT_SYMBOL(segment_load); 759EXPORT_SYMBOL(segment_unload); 760EXPORT_SYMBOL(segment_save); 761EXPORT_SYMBOL(segment_type); 762EXPORT_SYMBOL(segment_modify_shared); 763EXPORT_SYMBOL(segment_warning); 764