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