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); 660