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/diag.h> 25 #include <asm/page.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 * Must return either an error code < 0, or the segment type code >= 0 293 */ 294 static int 295 __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end) 296 { 297 unsigned long start_addr, end_addr, dummy; 298 struct dcss_segment *seg; 299 int rc, diag_cc, segtype; 300 301 start_addr = end_addr = 0; 302 segtype = -1; 303 seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA); 304 if (seg == NULL) { 305 rc = -ENOMEM; 306 goto out; 307 } 308 dcss_mkname (name, seg->dcss_name); 309 rc = query_segment_type (seg); 310 if (rc < 0) 311 goto out_free; 312 313 if (segment_overlaps_others(seg)) { 314 rc = -EBUSY; 315 goto out_free; 316 } 317 318 seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL); 319 if (seg->res == NULL) { 320 rc = -ENOMEM; 321 goto out_free; 322 } 323 seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; 324 seg->res->start = seg->start_addr; 325 seg->res->end = seg->end; 326 memcpy(&seg->res_name, seg->dcss_name, 8); 327 EBCASC(seg->res_name, 8); 328 seg->res_name[8] = '\0'; 329 strlcat(seg->res_name, " (DCSS)", sizeof(seg->res_name)); 330 seg->res->name = seg->res_name; 331 segtype = seg->vm_segtype; 332 if (segtype == SEG_TYPE_SC || 333 ((segtype == SEG_TYPE_SR || segtype == SEG_TYPE_ER) && !do_nonshared)) 334 seg->res->flags |= IORESOURCE_READONLY; 335 336 /* Check for overlapping resources before adding the mapping. */ 337 if (request_resource(&iomem_resource, seg->res)) { 338 rc = -EBUSY; 339 goto out_free_resource; 340 } 341 342 rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 343 if (rc) 344 goto out_resource; 345 346 if (do_nonshared) 347 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 348 &start_addr, &end_addr); 349 else 350 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 351 &start_addr, &end_addr); 352 if (diag_cc < 0) { 353 dcss_diag(&purgeseg_scode, seg->dcss_name, 354 &dummy, &dummy); 355 rc = diag_cc; 356 goto out_mapping; 357 } 358 if (diag_cc > 1) { 359 pr_warn("Loading DCSS %s failed with rc=%ld\n", name, end_addr); 360 rc = dcss_diag_translate_rc(end_addr); 361 dcss_diag(&purgeseg_scode, seg->dcss_name, 362 &dummy, &dummy); 363 goto out_mapping; 364 } 365 seg->start_addr = start_addr; 366 seg->end = end_addr; 367 seg->do_nonshared = do_nonshared; 368 refcount_set(&seg->ref_count, 1); 369 list_add(&seg->list, &dcss_list); 370 *addr = seg->start_addr; 371 *end = seg->end; 372 if (do_nonshared) 373 pr_info("DCSS %s of range %px to %px and type %s loaded as " 374 "exclusive-writable\n", name, (void*) seg->start_addr, 375 (void*) seg->end, segtype_string[seg->vm_segtype]); 376 else { 377 pr_info("DCSS %s of range %px to %px and type %s loaded in " 378 "shared access mode\n", name, (void*) seg->start_addr, 379 (void*) seg->end, segtype_string[seg->vm_segtype]); 380 } 381 goto out; 382 out_mapping: 383 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 384 out_resource: 385 release_resource(seg->res); 386 out_free_resource: 387 kfree(seg->res); 388 out_free: 389 kfree(seg); 390 out: 391 return rc < 0 ? rc : segtype; 392 } 393 394 /* 395 * this function loads a DCSS segment 396 * name : name of the DCSS 397 * do_nonshared : 0 indicates that the dcss should be shared with other linux images 398 * 1 indicates that the dcss should be exclusive for this linux image 399 * addr : will be filled with start address of the segment 400 * end : will be filled with end address of the segment 401 * return values: 402 * -ENOSYS : we are not running on VM 403 * -EIO : could not perform query or load diagnose 404 * -ENOENT : no such segment 405 * -EOPNOTSUPP: multi-part segment cannot be used with linux 406 * -EBUSY : segment cannot be used (overlaps with dcss or storage) 407 * -ERANGE : segment cannot be used (exceeds kernel mapping range) 408 * -EPERM : segment is currently loaded with incompatible permissions 409 * -ENOMEM : out of memory 410 * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 411 */ 412 int 413 segment_load (char *name, int do_nonshared, unsigned long *addr, 414 unsigned long *end) 415 { 416 struct dcss_segment *seg; 417 int rc; 418 419 if (!MACHINE_IS_VM) 420 return -ENOSYS; 421 422 mutex_lock(&dcss_lock); 423 seg = segment_by_name (name); 424 if (seg == NULL) 425 rc = __segment_load (name, do_nonshared, addr, end); 426 else { 427 if (do_nonshared == seg->do_nonshared) { 428 refcount_inc(&seg->ref_count); 429 *addr = seg->start_addr; 430 *end = seg->end; 431 rc = seg->vm_segtype; 432 } else { 433 *addr = *end = 0; 434 rc = -EPERM; 435 } 436 } 437 mutex_unlock(&dcss_lock); 438 return rc; 439 } 440 441 /* 442 * this function modifies the shared state of a DCSS segment. note that 443 * name : name of the DCSS 444 * do_nonshared : 0 indicates that the dcss should be shared with other linux images 445 * 1 indicates that the dcss should be exclusive for this linux image 446 * return values: 447 * -EIO : could not perform load diagnose (segment gone!) 448 * -ENOENT : no such segment (segment gone!) 449 * -EAGAIN : segment is in use by other exploiters, try later 450 * -EINVAL : no segment with the given name is currently loaded - name invalid 451 * -EBUSY : segment can temporarily not be used (overlaps with dcss) 452 * 0 : operation succeeded 453 */ 454 int 455 segment_modify_shared (char *name, int do_nonshared) 456 { 457 struct dcss_segment *seg; 458 unsigned long start_addr, end_addr, dummy; 459 int rc, diag_cc; 460 461 start_addr = end_addr = 0; 462 mutex_lock(&dcss_lock); 463 seg = segment_by_name (name); 464 if (seg == NULL) { 465 rc = -EINVAL; 466 goto out_unlock; 467 } 468 if (do_nonshared == seg->do_nonshared) { 469 pr_info("DCSS %s is already in the requested access " 470 "mode\n", name); 471 rc = 0; 472 goto out_unlock; 473 } 474 if (refcount_read(&seg->ref_count) != 1) { 475 pr_warn("DCSS %s is in use and cannot be reloaded\n", name); 476 rc = -EAGAIN; 477 goto out_unlock; 478 } 479 release_resource(seg->res); 480 if (do_nonshared) 481 seg->res->flags &= ~IORESOURCE_READONLY; 482 else 483 if (seg->vm_segtype == SEG_TYPE_SR || 484 seg->vm_segtype == SEG_TYPE_ER) 485 seg->res->flags |= IORESOURCE_READONLY; 486 487 if (request_resource(&iomem_resource, seg->res)) { 488 pr_warn("DCSS %s overlaps with used memory resources and cannot be reloaded\n", 489 name); 490 rc = -EBUSY; 491 kfree(seg->res); 492 goto out_del_mem; 493 } 494 495 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 496 if (do_nonshared) 497 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 498 &start_addr, &end_addr); 499 else 500 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 501 &start_addr, &end_addr); 502 if (diag_cc < 0) { 503 rc = diag_cc; 504 goto out_del_res; 505 } 506 if (diag_cc > 1) { 507 pr_warn("Reloading DCSS %s failed with rc=%ld\n", 508 name, end_addr); 509 rc = dcss_diag_translate_rc(end_addr); 510 goto out_del_res; 511 } 512 seg->start_addr = start_addr; 513 seg->end = end_addr; 514 seg->do_nonshared = do_nonshared; 515 rc = 0; 516 goto out_unlock; 517 out_del_res: 518 release_resource(seg->res); 519 kfree(seg->res); 520 out_del_mem: 521 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 522 list_del(&seg->list); 523 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 524 kfree(seg); 525 out_unlock: 526 mutex_unlock(&dcss_lock); 527 return rc; 528 } 529 530 /* 531 * Decrease the use count of a DCSS segment and remove 532 * it from the address space if nobody is using it 533 * any longer. 534 */ 535 void 536 segment_unload(char *name) 537 { 538 unsigned long dummy; 539 struct dcss_segment *seg; 540 541 if (!MACHINE_IS_VM) 542 return; 543 544 mutex_lock(&dcss_lock); 545 seg = segment_by_name (name); 546 if (seg == NULL) { 547 pr_err("Unloading unknown DCSS %s failed\n", name); 548 goto out_unlock; 549 } 550 if (!refcount_dec_and_test(&seg->ref_count)) 551 goto out_unlock; 552 release_resource(seg->res); 553 kfree(seg->res); 554 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 555 list_del(&seg->list); 556 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 557 kfree(seg); 558 out_unlock: 559 mutex_unlock(&dcss_lock); 560 } 561 562 /* 563 * save segment content permanently 564 */ 565 void 566 segment_save(char *name) 567 { 568 struct dcss_segment *seg; 569 char cmd1[160]; 570 char cmd2[80]; 571 int i, response; 572 573 if (!MACHINE_IS_VM) 574 return; 575 576 mutex_lock(&dcss_lock); 577 seg = segment_by_name (name); 578 579 if (seg == NULL) { 580 pr_err("Saving unknown DCSS %s failed\n", name); 581 goto out; 582 } 583 584 sprintf(cmd1, "DEFSEG %s", name); 585 for (i=0; i<seg->segcnt; i++) { 586 sprintf(cmd1+strlen(cmd1), " %lX-%lX %s", 587 seg->range[i].start >> PAGE_SHIFT, 588 seg->range[i].end >> PAGE_SHIFT, 589 segtype_string[seg->range[i].start & 0xff]); 590 } 591 sprintf(cmd2, "SAVESEG %s", name); 592 response = 0; 593 cpcmd(cmd1, NULL, 0, &response); 594 if (response) { 595 pr_err("Saving a DCSS failed with DEFSEG response code " 596 "%i\n", response); 597 goto out; 598 } 599 cpcmd(cmd2, NULL, 0, &response); 600 if (response) { 601 pr_err("Saving a DCSS failed with SAVESEG response code " 602 "%i\n", response); 603 goto out; 604 } 605 out: 606 mutex_unlock(&dcss_lock); 607 } 608 609 /* 610 * print appropriate error message for segment_load()/segment_type() 611 * return code 612 */ 613 void segment_warning(int rc, char *seg_name) 614 { 615 switch (rc) { 616 case -ENOENT: 617 pr_err("DCSS %s cannot be loaded or queried\n", seg_name); 618 break; 619 case -ENOSYS: 620 pr_err("DCSS %s cannot be loaded or queried without " 621 "z/VM\n", seg_name); 622 break; 623 case -EIO: 624 pr_err("Loading or querying DCSS %s resulted in a " 625 "hardware error\n", seg_name); 626 break; 627 case -EOPNOTSUPP: 628 pr_err("DCSS %s has multiple page ranges and cannot be " 629 "loaded or queried\n", seg_name); 630 break; 631 case -EBUSY: 632 pr_err("%s needs used memory resources and cannot be " 633 "loaded or queried\n", seg_name); 634 break; 635 case -EPERM: 636 pr_err("DCSS %s is already loaded in a different access " 637 "mode\n", seg_name); 638 break; 639 case -ENOMEM: 640 pr_err("There is not enough memory to load or query " 641 "DCSS %s\n", seg_name); 642 break; 643 case -ERANGE: 644 pr_err("DCSS %s exceeds the kernel mapping range (%lu) " 645 "and cannot be loaded\n", seg_name, VMEM_MAX_PHYS); 646 break; 647 default: 648 break; 649 } 650 } 651 652 EXPORT_SYMBOL(segment_load); 653 EXPORT_SYMBOL(segment_unload); 654 EXPORT_SYMBOL(segment_save); 655 EXPORT_SYMBOL(segment_type); 656 EXPORT_SYMBOL(segment_modify_shared); 657 EXPORT_SYMBOL(segment_warning); 658