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