root/arch/s390/mm/extmem.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dcss_mkname
  2. segment_by_name
  3. dcss_diag
  4. dcss_diag_translate_rc
  5. query_segment_type
  6. segment_type
  7. segment_overlaps_others
  8. __segment_load
  9. segment_load
  10. segment_modify_shared
  11. segment_unload
  12. segment_save
  13. segment_warning

   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/memblock.h>
  20 #include <linux/ctype.h>
  21 #include <linux/ioport.h>
  22 #include <linux/refcount.h>
  23 #include <asm/diag.h>
  24 #include <asm/page.h>
  25 #include <asm/pgtable.h>
  26 #include <asm/ebcdic.h>
  27 #include <asm/errno.h>
  28 #include <asm/extmem.h>
  29 #include <asm/cpcmd.h>
  30 #include <asm/setup.h>
  31 
  32 #define DCSS_PURGESEG   0x08
  33 #define DCSS_LOADSHRX   0x20
  34 #define DCSS_LOADNSRX   0x24
  35 #define DCSS_FINDSEGX   0x2c
  36 #define DCSS_SEGEXTX    0x38
  37 #define DCSS_FINDSEGA   0x0c
  38 
  39 struct qrange {
  40         unsigned long  start; /* last byte type */
  41         unsigned long  end;   /* last byte reserved */
  42 };
  43 
  44 struct qout64 {
  45         unsigned long segstart;
  46         unsigned long segend;
  47         int segcnt;
  48         int segrcnt;
  49         struct qrange range[6];
  50 };
  51 
  52 struct qin64 {
  53         char qopcode;
  54         char rsrv1[3];
  55         char qrcode;
  56         char rsrv2[3];
  57         char qname[8];
  58         unsigned int qoutptr;
  59         short int qoutlen;
  60 };
  61 
  62 struct dcss_segment {
  63         struct list_head list;
  64         char dcss_name[8];
  65         char res_name[16];
  66         unsigned long start_addr;
  67         unsigned long end;
  68         refcount_t ref_count;
  69         int do_nonshared;
  70         unsigned int vm_segtype;
  71         struct qrange range[6];
  72         int segcnt;
  73         struct resource *res;
  74 };
  75 
  76 static DEFINE_MUTEX(dcss_lock);
  77 static LIST_HEAD(dcss_list);
  78 static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
  79                                         "EW/EN-MIXED" };
  80 static int loadshr_scode = DCSS_LOADSHRX;
  81 static int loadnsr_scode = DCSS_LOADNSRX;
  82 static int purgeseg_scode = DCSS_PURGESEG;
  83 static int segext_scode = DCSS_SEGEXTX;
  84 
  85 /*
  86  * Create the 8 bytes, ebcdic VM segment name from
  87  * an ascii name.
  88  */
  89 static void
  90 dcss_mkname(char *name, char *dcss_name)
  91 {
  92         int i;
  93 
  94         for (i = 0; i < 8; i++) {
  95                 if (name[i] == '\0')
  96                         break;
  97                 dcss_name[i] = toupper(name[i]);
  98         }
  99         for (; i < 8; i++)
 100                 dcss_name[i] = ' ';
 101         ASCEBC(dcss_name, 8);
 102 }
 103 
 104 
 105 /*
 106  * search all segments in dcss_list, and return the one
 107  * namend *name. If not found, return NULL.
 108  */
 109 static struct dcss_segment *
 110 segment_by_name (char *name)
 111 {
 112         char dcss_name[9];
 113         struct list_head *l;
 114         struct dcss_segment *tmp, *retval = NULL;
 115 
 116         BUG_ON(!mutex_is_locked(&dcss_lock));
 117         dcss_mkname (name, dcss_name);
 118         list_for_each (l, &dcss_list) {
 119                 tmp = list_entry (l, struct dcss_segment, list);
 120                 if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) {
 121                         retval = tmp;
 122                         break;
 123                 }
 124         }
 125         return retval;
 126 }
 127 
 128 
 129 /*
 130  * Perform a function on a dcss segment.
 131  */
 132 static inline int
 133 dcss_diag(int *func, void *parameter,
 134            unsigned long *ret1, unsigned long *ret2)
 135 {
 136         unsigned long rx, ry;
 137         int rc;
 138 
 139         rx = (unsigned long) parameter;
 140         ry = (unsigned long) *func;
 141 
 142         diag_stat_inc(DIAG_STAT_X064);
 143         asm volatile(
 144                 "       diag    %0,%1,0x64\n"
 145                 "       ipm     %2\n"
 146                 "       srl     %2,28\n"
 147                 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
 148         *ret1 = rx;
 149         *ret2 = ry;
 150         return rc;
 151 }
 152 
 153 static inline int
 154 dcss_diag_translate_rc (int vm_rc) {
 155         if (vm_rc == 44)
 156                 return -ENOENT;
 157         return -EIO;
 158 }
 159 
 160 
 161 /* do a diag to get info about a segment.
 162  * fills start_address, end and vm_segtype fields
 163  */
 164 static int
 165 query_segment_type (struct dcss_segment *seg)
 166 {
 167         unsigned long dummy, vmrc;
 168         int diag_cc, rc, i;
 169         struct qout64 *qout;
 170         struct qin64 *qin;
 171 
 172         qin = kmalloc(sizeof(*qin), GFP_KERNEL | GFP_DMA);
 173         qout = kmalloc(sizeof(*qout), GFP_KERNEL | GFP_DMA);
 174         if ((qin == NULL) || (qout == NULL)) {
 175                 rc = -ENOMEM;
 176                 goto out_free;
 177         }
 178 
 179         /* initialize diag input parameters */
 180         qin->qopcode = DCSS_FINDSEGA;
 181         qin->qoutptr = (unsigned long) qout;
 182         qin->qoutlen = sizeof(struct qout64);
 183         memcpy (qin->qname, seg->dcss_name, 8);
 184 
 185         diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc);
 186 
 187         if (diag_cc < 0) {
 188                 rc = diag_cc;
 189                 goto out_free;
 190         }
 191         if (diag_cc > 1) {
 192                 pr_warn("Querying a DCSS type failed with rc=%ld\n", vmrc);
 193                 rc = dcss_diag_translate_rc (vmrc);
 194                 goto out_free;
 195         }
 196 
 197         if (qout->segcnt > 6) {
 198                 rc = -EOPNOTSUPP;
 199                 goto out_free;
 200         }
 201 
 202         if (qout->segcnt == 1) {
 203                 seg->vm_segtype = qout->range[0].start & 0xff;
 204         } else {
 205                 /* multi-part segment. only one type supported here:
 206                     - all parts are contiguous
 207                     - all parts are either EW or EN type
 208                     - maximum 6 parts allowed */
 209                 unsigned long start = qout->segstart >> PAGE_SHIFT;
 210                 for (i=0; i<qout->segcnt; i++) {
 211                         if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) &&
 212                             ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) {
 213                                 rc = -EOPNOTSUPP;
 214                                 goto out_free;
 215                         }
 216                         if (start != qout->range[i].start >> PAGE_SHIFT) {
 217                                 rc = -EOPNOTSUPP;
 218                                 goto out_free;
 219                         }
 220                         start = (qout->range[i].end >> PAGE_SHIFT) + 1;
 221                 }
 222                 seg->vm_segtype = SEG_TYPE_EWEN;
 223         }
 224 
 225         /* analyze diag output and update seg */
 226         seg->start_addr = qout->segstart;
 227         seg->end = qout->segend;
 228 
 229         memcpy (seg->range, qout->range, 6*sizeof(struct qrange));
 230         seg->segcnt = qout->segcnt;
 231 
 232         rc = 0;
 233 
 234  out_free:
 235         kfree(qin);
 236         kfree(qout);
 237         return rc;
 238 }
 239 
 240 /*
 241  * get info about a segment
 242  * possible return values:
 243  * -ENOSYS  : we are not running on VM
 244  * -EIO     : could not perform query diagnose
 245  * -ENOENT  : no such segment
 246  * -EOPNOTSUPP: multi-part segment cannot be used with linux
 247  * -ENOMEM  : out of memory
 248  * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
 249  */
 250 int
 251 segment_type (char* name)
 252 {
 253         int rc;
 254         struct dcss_segment seg;
 255 
 256         if (!MACHINE_IS_VM)
 257                 return -ENOSYS;
 258 
 259         dcss_mkname(name, seg.dcss_name);
 260         rc = query_segment_type (&seg);
 261         if (rc < 0)
 262                 return rc;
 263         return seg.vm_segtype;
 264 }
 265 
 266 /*
 267  * check if segment collides with other segments that are currently loaded
 268  * returns 1 if this is the case, 0 if no collision was found
 269  */
 270 static int
 271 segment_overlaps_others (struct dcss_segment *seg)
 272 {
 273         struct list_head *l;
 274         struct dcss_segment *tmp;
 275 
 276         BUG_ON(!mutex_is_locked(&dcss_lock));
 277         list_for_each(l, &dcss_list) {
 278                 tmp = list_entry(l, struct dcss_segment, list);
 279                 if ((tmp->start_addr >> 20) > (seg->end >> 20))
 280                         continue;
 281                 if ((tmp->end >> 20) < (seg->start_addr >> 20))
 282                         continue;
 283                 if (seg == tmp)
 284                         continue;
 285                 return 1;
 286         }
 287         return 0;
 288 }
 289 
 290 /*
 291  * real segment loading function, called from segment_load
 292  */
 293 static int
 294 __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end)
 295 {
 296         unsigned long start_addr, end_addr, dummy;
 297         struct dcss_segment *seg;
 298         int rc, diag_cc;
 299 
 300         start_addr = end_addr = 0;
 301         seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA);
 302         if (seg == NULL) {
 303                 rc = -ENOMEM;
 304                 goto out;
 305         }
 306         dcss_mkname (name, seg->dcss_name);
 307         rc = query_segment_type (seg);
 308         if (rc < 0)
 309                 goto out_free;
 310 
 311         if (segment_overlaps_others(seg)) {
 312                 rc = -EBUSY;
 313                 goto out_free;
 314         }
 315 
 316         rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
 317 
 318         if (rc)
 319                 goto out_free;
 320 
 321         seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL);
 322         if (seg->res == NULL) {
 323                 rc = -ENOMEM;
 324                 goto out_shared;
 325         }
 326         seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM;
 327         seg->res->start = seg->start_addr;
 328         seg->res->end = seg->end;
 329         memcpy(&seg->res_name, seg->dcss_name, 8);
 330         EBCASC(seg->res_name, 8);
 331         seg->res_name[8] = '\0';
 332         strlcat(seg->res_name, " (DCSS)", sizeof(seg->res_name));
 333         seg->res->name = seg->res_name;
 334         rc = seg->vm_segtype;
 335         if (rc == SEG_TYPE_SC ||
 336             ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared))
 337                 seg->res->flags |= IORESOURCE_READONLY;
 338         if (request_resource(&iomem_resource, seg->res)) {
 339                 rc = -EBUSY;
 340                 kfree(seg->res);
 341                 goto out_shared;
 342         }
 343 
 344         if (do_nonshared)
 345                 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
 346                                 &start_addr, &end_addr);
 347         else
 348                 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
 349                                 &start_addr, &end_addr);
 350         if (diag_cc < 0) {
 351                 dcss_diag(&purgeseg_scode, seg->dcss_name,
 352                                 &dummy, &dummy);
 353                 rc = diag_cc;
 354                 goto out_resource;
 355         }
 356         if (diag_cc > 1) {
 357                 pr_warn("Loading DCSS %s failed with rc=%ld\n", name, end_addr);
 358                 rc = dcss_diag_translate_rc(end_addr);
 359                 dcss_diag(&purgeseg_scode, seg->dcss_name,
 360                                 &dummy, &dummy);
 361                 goto out_resource;
 362         }
 363         seg->start_addr = start_addr;
 364         seg->end = end_addr;
 365         seg->do_nonshared = do_nonshared;
 366         refcount_set(&seg->ref_count, 1);
 367         list_add(&seg->list, &dcss_list);
 368         *addr = seg->start_addr;
 369         *end  = seg->end;
 370         if (do_nonshared)
 371                 pr_info("DCSS %s of range %px to %px and type %s loaded as "
 372                         "exclusive-writable\n", name, (void*) seg->start_addr,
 373                         (void*) seg->end, segtype_string[seg->vm_segtype]);
 374         else {
 375                 pr_info("DCSS %s of range %px to %px and type %s loaded in "
 376                         "shared access mode\n", name, (void*) seg->start_addr,
 377                         (void*) seg->end, segtype_string[seg->vm_segtype]);
 378         }
 379         goto out;
 380  out_resource:
 381         release_resource(seg->res);
 382         kfree(seg->res);
 383  out_shared:
 384         vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
 385  out_free:
 386         kfree(seg);
 387  out:
 388         return rc;
 389 }
 390 
 391 /*
 392  * this function loads a DCSS segment
 393  * name         : name of the DCSS
 394  * do_nonshared : 0 indicates that the dcss should be shared with other linux images
 395  *                1 indicates that the dcss should be exclusive for this linux image
 396  * addr         : will be filled with start address of the segment
 397  * end          : will be filled with end address of the segment
 398  * return values:
 399  * -ENOSYS  : we are not running on VM
 400  * -EIO     : could not perform query or load diagnose
 401  * -ENOENT  : no such segment
 402  * -EOPNOTSUPP: multi-part segment cannot be used with linux
 403  * -ENOSPC  : segment cannot be used (overlaps with storage)
 404  * -EBUSY   : segment can temporarily not be used (overlaps with dcss)
 405  * -ERANGE  : segment cannot be used (exceeds kernel mapping range)
 406  * -EPERM   : segment is currently loaded with incompatible permissions
 407  * -ENOMEM  : out of memory
 408  * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
 409  */
 410 int
 411 segment_load (char *name, int do_nonshared, unsigned long *addr,
 412                 unsigned long *end)
 413 {
 414         struct dcss_segment *seg;
 415         int rc;
 416 
 417         if (!MACHINE_IS_VM)
 418                 return -ENOSYS;
 419 
 420         mutex_lock(&dcss_lock);
 421         seg = segment_by_name (name);
 422         if (seg == NULL)
 423                 rc = __segment_load (name, do_nonshared, addr, end);
 424         else {
 425                 if (do_nonshared == seg->do_nonshared) {
 426                         refcount_inc(&seg->ref_count);
 427                         *addr = seg->start_addr;
 428                         *end  = seg->end;
 429                         rc    = seg->vm_segtype;
 430                 } else {
 431                         *addr = *end = 0;
 432                         rc    = -EPERM;
 433                 }
 434         }
 435         mutex_unlock(&dcss_lock);
 436         return rc;
 437 }
 438 
 439 /*
 440  * this function modifies the shared state of a DCSS segment. note that
 441  * name         : name of the DCSS
 442  * do_nonshared : 0 indicates that the dcss should be shared with other linux images
 443  *                1 indicates that the dcss should be exclusive for this linux image
 444  * return values:
 445  * -EIO     : could not perform load diagnose (segment gone!)
 446  * -ENOENT  : no such segment (segment gone!)
 447  * -EAGAIN  : segment is in use by other exploiters, try later
 448  * -EINVAL  : no segment with the given name is currently loaded - name invalid
 449  * -EBUSY   : segment can temporarily not be used (overlaps with dcss)
 450  * 0        : operation succeeded
 451  */
 452 int
 453 segment_modify_shared (char *name, int do_nonshared)
 454 {
 455         struct dcss_segment *seg;
 456         unsigned long start_addr, end_addr, dummy;
 457         int rc, diag_cc;
 458 
 459         start_addr = end_addr = 0;
 460         mutex_lock(&dcss_lock);
 461         seg = segment_by_name (name);
 462         if (seg == NULL) {
 463                 rc = -EINVAL;
 464                 goto out_unlock;
 465         }
 466         if (do_nonshared == seg->do_nonshared) {
 467                 pr_info("DCSS %s is already in the requested access "
 468                         "mode\n", name);
 469                 rc = 0;
 470                 goto out_unlock;
 471         }
 472         if (refcount_read(&seg->ref_count) != 1) {
 473                 pr_warn("DCSS %s is in use and cannot be reloaded\n", name);
 474                 rc = -EAGAIN;
 475                 goto out_unlock;
 476         }
 477         release_resource(seg->res);
 478         if (do_nonshared)
 479                 seg->res->flags &= ~IORESOURCE_READONLY;
 480         else
 481                 if (seg->vm_segtype == SEG_TYPE_SR ||
 482                     seg->vm_segtype == SEG_TYPE_ER)
 483                         seg->res->flags |= IORESOURCE_READONLY;
 484 
 485         if (request_resource(&iomem_resource, seg->res)) {
 486                 pr_warn("DCSS %s overlaps with used memory resources and cannot be reloaded\n",
 487                         name);
 488                 rc = -EBUSY;
 489                 kfree(seg->res);
 490                 goto out_del_mem;
 491         }
 492 
 493         dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
 494         if (do_nonshared)
 495                 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
 496                                 &start_addr, &end_addr);
 497         else
 498                 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
 499                                 &start_addr, &end_addr);
 500         if (diag_cc < 0) {
 501                 rc = diag_cc;
 502                 goto out_del_res;
 503         }
 504         if (diag_cc > 1) {
 505                 pr_warn("Reloading DCSS %s failed with rc=%ld\n",
 506                         name, end_addr);
 507                 rc = dcss_diag_translate_rc(end_addr);
 508                 goto out_del_res;
 509         }
 510         seg->start_addr = start_addr;
 511         seg->end = end_addr;
 512         seg->do_nonshared = do_nonshared;
 513         rc = 0;
 514         goto out_unlock;
 515  out_del_res:
 516         release_resource(seg->res);
 517         kfree(seg->res);
 518  out_del_mem:
 519         vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
 520         list_del(&seg->list);
 521         dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
 522         kfree(seg);
 523  out_unlock:
 524         mutex_unlock(&dcss_lock);
 525         return rc;
 526 }
 527 
 528 /*
 529  * Decrease the use count of a DCSS segment and remove
 530  * it from the address space if nobody is using it
 531  * any longer.
 532  */
 533 void
 534 segment_unload(char *name)
 535 {
 536         unsigned long dummy;
 537         struct dcss_segment *seg;
 538 
 539         if (!MACHINE_IS_VM)
 540                 return;
 541 
 542         mutex_lock(&dcss_lock);
 543         seg = segment_by_name (name);
 544         if (seg == NULL) {
 545                 pr_err("Unloading unknown DCSS %s failed\n", name);
 546                 goto out_unlock;
 547         }
 548         if (!refcount_dec_and_test(&seg->ref_count))
 549                 goto out_unlock;
 550         release_resource(seg->res);
 551         kfree(seg->res);
 552         vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
 553         list_del(&seg->list);
 554         dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
 555         kfree(seg);
 556 out_unlock:
 557         mutex_unlock(&dcss_lock);
 558 }
 559 
 560 /*
 561  * save segment content permanently
 562  */
 563 void
 564 segment_save(char *name)
 565 {
 566         struct dcss_segment *seg;
 567         char cmd1[160];
 568         char cmd2[80];
 569         int i, response;
 570 
 571         if (!MACHINE_IS_VM)
 572                 return;
 573 
 574         mutex_lock(&dcss_lock);
 575         seg = segment_by_name (name);
 576 
 577         if (seg == NULL) {
 578                 pr_err("Saving unknown DCSS %s failed\n", name);
 579                 goto out;
 580         }
 581 
 582         sprintf(cmd1, "DEFSEG %s", name);
 583         for (i=0; i<seg->segcnt; i++) {
 584                 sprintf(cmd1+strlen(cmd1), " %lX-%lX %s",
 585                         seg->range[i].start >> PAGE_SHIFT,
 586                         seg->range[i].end >> PAGE_SHIFT,
 587                         segtype_string[seg->range[i].start & 0xff]);
 588         }
 589         sprintf(cmd2, "SAVESEG %s", name);
 590         response = 0;
 591         cpcmd(cmd1, NULL, 0, &response);
 592         if (response) {
 593                 pr_err("Saving a DCSS failed with DEFSEG response code "
 594                        "%i\n", response);
 595                 goto out;
 596         }
 597         cpcmd(cmd2, NULL, 0, &response);
 598         if (response) {
 599                 pr_err("Saving a DCSS failed with SAVESEG response code "
 600                        "%i\n", response);
 601                 goto out;
 602         }
 603 out:
 604         mutex_unlock(&dcss_lock);
 605 }
 606 
 607 /*
 608  * print appropriate error message for segment_load()/segment_type()
 609  * return code
 610  */
 611 void segment_warning(int rc, char *seg_name)
 612 {
 613         switch (rc) {
 614         case -ENOENT:
 615                 pr_err("DCSS %s cannot be loaded or queried\n", seg_name);
 616                 break;
 617         case -ENOSYS:
 618                 pr_err("DCSS %s cannot be loaded or queried without "
 619                        "z/VM\n", seg_name);
 620                 break;
 621         case -EIO:
 622                 pr_err("Loading or querying DCSS %s resulted in a "
 623                        "hardware error\n", seg_name);
 624                 break;
 625         case -EOPNOTSUPP:
 626                 pr_err("DCSS %s has multiple page ranges and cannot be "
 627                        "loaded or queried\n", seg_name);
 628                 break;
 629         case -ENOSPC:
 630                 pr_err("DCSS %s overlaps with used storage and cannot "
 631                        "be loaded\n", seg_name);
 632                 break;
 633         case -EBUSY:
 634                 pr_err("%s needs used memory resources and cannot be "
 635                        "loaded or queried\n", seg_name);
 636                 break;
 637         case -EPERM:
 638                 pr_err("DCSS %s is already loaded in a different access "
 639                        "mode\n", seg_name);
 640                 break;
 641         case -ENOMEM:
 642                 pr_err("There is not enough memory to load or query "
 643                        "DCSS %s\n", seg_name);
 644                 break;
 645         case -ERANGE:
 646                 pr_err("DCSS %s exceeds the kernel mapping range (%lu) "
 647                        "and cannot be loaded\n", seg_name, VMEM_MAX_PHYS);
 648                 break;
 649         default:
 650                 break;
 651         }
 652 }
 653 
 654 EXPORT_SYMBOL(segment_load);
 655 EXPORT_SYMBOL(segment_unload);
 656 EXPORT_SYMBOL(segment_save);
 657 EXPORT_SYMBOL(segment_type);
 658 EXPORT_SYMBOL(segment_modify_shared);
 659 EXPORT_SYMBOL(segment_warning);

/* [<][>][^][v][top][bottom][index][help] */