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