linux/arch/s390/mm/extmem.c
<<
>>
Prefs
   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
  43struct qrange {
  44        unsigned long  start; /* last byte type */
  45        unsigned long  end;   /* last byte reserved */
  46};
  47
  48struct qout64 {
  49        unsigned long segstart;
  50        unsigned long segend;
  51        int segcnt;
  52        int segrcnt;
  53        struct qrange range[6];
  54};
  55
  56struct 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' */
  62struct qout64_old {
  63        int segstart;
  64        int segend;
  65        int segcnt;
  66        int segrcnt;
  67        struct qrange_old range[6];
  68};
  69
  70struct 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
  80struct dcss_segment {
  81        struct list_head list;
  82        char dcss_name[8];
  83        char res_name[16];
  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
  94static DEFINE_MUTEX(dcss_lock);
  95static LIST_HEAD(dcss_list);
  96static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
  97                                        "EW/EN-MIXED" };
  98static int loadshr_scode, loadnsr_scode;
  99static int segext_scode, purgeseg_scode;
 100static int scode_set;
 101
 102/* set correct Diag x'64' subcodes. */
 103static int
 104dcss_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 */
 149static void
 150dcss_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 */
 169static struct dcss_segment *
 170segment_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 */
 192static inline int
 193dcss_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
 230static inline int
 231dcss_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 */
 241static int
 242query_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 */
 352int
 353segment_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 */
 372static int
 373segment_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 */
 395static 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        strlcat(seg->res_name, " (DCSS)", sizeof(seg->res_name));
 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 */
 514int
 515segment_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 */
 556int
 557segment_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 */
 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