1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * Author(s)......: Carsten Otte <cotte@de.ibm.com> 41da177e4SLinus Torvalds * Rob M van der Heij <rvdheij@nl.ibm.com> 51da177e4SLinus Torvalds * Steven Shultz <shultzss@us.ibm.com> 61da177e4SLinus Torvalds * Bugreports.to..: <Linux390@de.ibm.com> 7a53c8fabSHeiko Carstens * Copyright IBM Corp. 2002, 2004 81da177e4SLinus Torvalds */ 91da177e4SLinus Torvalds 1093098bf0SHongjie Yang #define KMSG_COMPONENT "extmem" 1193098bf0SHongjie Yang #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1293098bf0SHongjie Yang 131da177e4SLinus Torvalds #include <linux/kernel.h> 141da177e4SLinus Torvalds #include <linux/string.h> 151da177e4SLinus Torvalds #include <linux/spinlock.h> 161da177e4SLinus Torvalds #include <linux/list.h> 171da177e4SLinus Torvalds #include <linux/slab.h> 18ff24b07aSPaul Gortmaker #include <linux/export.h> 1957c8a661SMike Rapoport #include <linux/memblock.h> 2036a2bd42SHeiko Carstens #include <linux/ctype.h> 21444f0e54SGerald Schaefer #include <linux/ioport.h> 221ec2772eSMartin Schwidefsky #include <asm/diag.h> 231da177e4SLinus Torvalds #include <asm/page.h> 24f4eb07c1SHeiko Carstens #include <asm/pgtable.h> 251da177e4SLinus Torvalds #include <asm/ebcdic.h> 261da177e4SLinus Torvalds #include <asm/errno.h> 271da177e4SLinus Torvalds #include <asm/extmem.h> 281da177e4SLinus Torvalds #include <asm/cpcmd.h> 2936a2bd42SHeiko Carstens #include <asm/setup.h> 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #define DCSS_PURGESEG 0x08 32b2300b9eSHongjie Yang #define DCSS_LOADSHRX 0x20 33b2300b9eSHongjie Yang #define DCSS_LOADNSRX 0x24 34b2300b9eSHongjie Yang #define DCSS_FINDSEGX 0x2c 35b2300b9eSHongjie Yang #define DCSS_SEGEXTX 0x38 361da177e4SLinus Torvalds #define DCSS_FINDSEGA 0x0c 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds struct qrange { 39b2300b9eSHongjie Yang unsigned long start; /* last byte type */ 40b2300b9eSHongjie Yang unsigned long end; /* last byte reserved */ 411da177e4SLinus Torvalds }; 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds struct qout64 { 44b2300b9eSHongjie Yang unsigned long segstart; 45b2300b9eSHongjie Yang unsigned long segend; 461da177e4SLinus Torvalds int segcnt; 471da177e4SLinus Torvalds int segrcnt; 481da177e4SLinus Torvalds struct qrange range[6]; 491da177e4SLinus Torvalds }; 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds struct qin64 { 521da177e4SLinus Torvalds char qopcode; 531da177e4SLinus Torvalds char rsrv1[3]; 541da177e4SLinus Torvalds char qrcode; 551da177e4SLinus Torvalds char rsrv2[3]; 561da177e4SLinus Torvalds char qname[8]; 571da177e4SLinus Torvalds unsigned int qoutptr; 581da177e4SLinus Torvalds short int qoutlen; 591da177e4SLinus Torvalds }; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds struct dcss_segment { 621da177e4SLinus Torvalds struct list_head list; 631da177e4SLinus Torvalds char dcss_name[8]; 646b2ddf33SVasily Gorbik char res_name[16]; 651da177e4SLinus Torvalds unsigned long start_addr; 661da177e4SLinus Torvalds unsigned long end; 671da177e4SLinus Torvalds atomic_t ref_count; 681da177e4SLinus Torvalds int do_nonshared; 691da177e4SLinus Torvalds unsigned int vm_segtype; 701da177e4SLinus Torvalds struct qrange range[6]; 711da177e4SLinus Torvalds int segcnt; 72444f0e54SGerald Schaefer struct resource *res; 731da177e4SLinus Torvalds }; 741da177e4SLinus Torvalds 7509252e77SHeiko Carstens static DEFINE_MUTEX(dcss_lock); 76c11ca97eSDenis Cheng static LIST_HEAD(dcss_list); 771da177e4SLinus Torvalds static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", 781da177e4SLinus Torvalds "EW/EN-MIXED" }; 79ca571146SGerald Schaefer static int loadshr_scode = DCSS_LOADSHRX; 80ca571146SGerald Schaefer static int loadnsr_scode = DCSS_LOADNSRX; 81ca571146SGerald Schaefer static int purgeseg_scode = DCSS_PURGESEG; 82ca571146SGerald Schaefer static int segext_scode = DCSS_SEGEXTX; 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds /* 851da177e4SLinus Torvalds * Create the 8 bytes, ebcdic VM segment name from 861da177e4SLinus Torvalds * an ascii name. 871da177e4SLinus Torvalds */ 884d284cacSHeiko Carstens static void 891da177e4SLinus Torvalds dcss_mkname(char *name, char *dcss_name) 901da177e4SLinus Torvalds { 911da177e4SLinus Torvalds int i; 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds for (i = 0; i < 8; i++) { 941da177e4SLinus Torvalds if (name[i] == '\0') 951da177e4SLinus Torvalds break; 961da177e4SLinus Torvalds dcss_name[i] = toupper(name[i]); 970b925159SHeiko Carstens } 981da177e4SLinus Torvalds for (; i < 8; i++) 991da177e4SLinus Torvalds dcss_name[i] = ' '; 1001da177e4SLinus Torvalds ASCEBC(dcss_name, 8); 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds /* 1051da177e4SLinus Torvalds * search all segments in dcss_list, and return the one 1061da177e4SLinus Torvalds * namend *name. If not found, return NULL. 1071da177e4SLinus Torvalds */ 1081da177e4SLinus Torvalds static struct dcss_segment * 1091da177e4SLinus Torvalds segment_by_name (char *name) 1101da177e4SLinus Torvalds { 1111da177e4SLinus Torvalds char dcss_name[9]; 1121da177e4SLinus Torvalds struct list_head *l; 1131da177e4SLinus Torvalds struct dcss_segment *tmp, *retval = NULL; 1141da177e4SLinus Torvalds 11509252e77SHeiko Carstens BUG_ON(!mutex_is_locked(&dcss_lock)); 1161da177e4SLinus Torvalds dcss_mkname (name, dcss_name); 1171da177e4SLinus Torvalds list_for_each (l, &dcss_list) { 1181da177e4SLinus Torvalds tmp = list_entry (l, struct dcss_segment, list); 1191da177e4SLinus Torvalds if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) { 1201da177e4SLinus Torvalds retval = tmp; 1211da177e4SLinus Torvalds break; 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds return retval; 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds /* 1291da177e4SLinus Torvalds * Perform a function on a dcss segment. 1301da177e4SLinus Torvalds */ 1311da177e4SLinus Torvalds static inline int 132b2300b9eSHongjie Yang dcss_diag(int *func, void *parameter, 1331da177e4SLinus Torvalds unsigned long *ret1, unsigned long *ret2) 1341da177e4SLinus Torvalds { 1351da177e4SLinus Torvalds unsigned long rx, ry; 1361da177e4SLinus Torvalds int rc; 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds rx = (unsigned long) parameter; 139b2300b9eSHongjie Yang ry = (unsigned long) *func; 140b2300b9eSHongjie Yang 1411ec2772eSMartin Schwidefsky diag_stat_inc(DIAG_STAT_X064); 142b2300b9eSHongjie Yang asm volatile( 1431da177e4SLinus Torvalds " diag %0,%1,0x64\n" 1441da177e4SLinus Torvalds " ipm %2\n" 1451da177e4SLinus Torvalds " srl %2,28\n" 1461da177e4SLinus Torvalds : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); 1471da177e4SLinus Torvalds *ret1 = rx; 1481da177e4SLinus Torvalds *ret2 = ry; 1491da177e4SLinus Torvalds return rc; 1501da177e4SLinus Torvalds } 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds static inline int 1531da177e4SLinus Torvalds dcss_diag_translate_rc (int vm_rc) { 1541da177e4SLinus Torvalds if (vm_rc == 44) 1551da177e4SLinus Torvalds return -ENOENT; 1561da177e4SLinus Torvalds return -EIO; 1571da177e4SLinus Torvalds } 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds /* do a diag to get info about a segment. 1611da177e4SLinus Torvalds * fills start_address, end and vm_segtype fields 1621da177e4SLinus Torvalds */ 1631da177e4SLinus Torvalds static int 1641da177e4SLinus Torvalds query_segment_type (struct dcss_segment *seg) 1651da177e4SLinus Torvalds { 1661da177e4SLinus Torvalds unsigned long dummy, vmrc; 167c2f0e8c8SHeiko Carstens int diag_cc, rc, i; 168c2f0e8c8SHeiko Carstens struct qout64 *qout; 169c2f0e8c8SHeiko Carstens struct qin64 *qin; 1701da177e4SLinus Torvalds 171c2f0e8c8SHeiko Carstens qin = kmalloc(sizeof(*qin), GFP_KERNEL | GFP_DMA); 172c2f0e8c8SHeiko Carstens qout = kmalloc(sizeof(*qout), GFP_KERNEL | GFP_DMA); 1731da177e4SLinus Torvalds if ((qin == NULL) || (qout == NULL)) { 1741da177e4SLinus Torvalds rc = -ENOMEM; 1751da177e4SLinus Torvalds goto out_free; 1761da177e4SLinus Torvalds } 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds /* initialize diag input parameters */ 1791da177e4SLinus Torvalds qin->qopcode = DCSS_FINDSEGA; 1801da177e4SLinus Torvalds qin->qoutptr = (unsigned long) qout; 1811da177e4SLinus Torvalds qin->qoutlen = sizeof(struct qout64); 1821da177e4SLinus Torvalds memcpy (qin->qname, seg->dcss_name, 8); 1831da177e4SLinus Torvalds 184b2300b9eSHongjie Yang diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc); 1851da177e4SLinus Torvalds 186b2300b9eSHongjie Yang if (diag_cc < 0) { 187b2300b9eSHongjie Yang rc = diag_cc; 188b2300b9eSHongjie Yang goto out_free; 189b2300b9eSHongjie Yang } 1901da177e4SLinus Torvalds if (diag_cc > 1) { 191baebc70aSJoe Perches pr_warn("Querying a DCSS type failed with rc=%ld\n", vmrc); 1921da177e4SLinus Torvalds rc = dcss_diag_translate_rc (vmrc); 1931da177e4SLinus Torvalds goto out_free; 1941da177e4SLinus Torvalds } 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds if (qout->segcnt > 6) { 197b8e660b8SHeiko Carstens rc = -EOPNOTSUPP; 1981da177e4SLinus Torvalds goto out_free; 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds if (qout->segcnt == 1) { 2021da177e4SLinus Torvalds seg->vm_segtype = qout->range[0].start & 0xff; 2031da177e4SLinus Torvalds } else { 2041da177e4SLinus Torvalds /* multi-part segment. only one type supported here: 2051da177e4SLinus Torvalds - all parts are contiguous 2061da177e4SLinus Torvalds - all parts are either EW or EN type 2071da177e4SLinus Torvalds - maximum 6 parts allowed */ 2081da177e4SLinus Torvalds unsigned long start = qout->segstart >> PAGE_SHIFT; 2091da177e4SLinus Torvalds for (i=0; i<qout->segcnt; i++) { 2101da177e4SLinus Torvalds if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) && 2111da177e4SLinus Torvalds ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) { 212b8e660b8SHeiko Carstens rc = -EOPNOTSUPP; 2131da177e4SLinus Torvalds goto out_free; 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds if (start != qout->range[i].start >> PAGE_SHIFT) { 216b8e660b8SHeiko Carstens rc = -EOPNOTSUPP; 2171da177e4SLinus Torvalds goto out_free; 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds start = (qout->range[i].end >> PAGE_SHIFT) + 1; 2201da177e4SLinus Torvalds } 2211da177e4SLinus Torvalds seg->vm_segtype = SEG_TYPE_EWEN; 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds /* analyze diag output and update seg */ 2251da177e4SLinus Torvalds seg->start_addr = qout->segstart; 2261da177e4SLinus Torvalds seg->end = qout->segend; 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds memcpy (seg->range, qout->range, 6*sizeof(struct qrange)); 2291da177e4SLinus Torvalds seg->segcnt = qout->segcnt; 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds rc = 0; 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds out_free: 234b2325fe1SJesper Juhl kfree(qin); 235b2325fe1SJesper Juhl kfree(qout); 2361da177e4SLinus Torvalds return rc; 2371da177e4SLinus Torvalds } 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds /* 2401da177e4SLinus Torvalds * get info about a segment 2411da177e4SLinus Torvalds * possible return values: 2421da177e4SLinus Torvalds * -ENOSYS : we are not running on VM 2431da177e4SLinus Torvalds * -EIO : could not perform query diagnose 2441da177e4SLinus Torvalds * -ENOENT : no such segment 245b8e660b8SHeiko Carstens * -EOPNOTSUPP: multi-part segment cannot be used with linux 2461da177e4SLinus Torvalds * -ENOMEM : out of memory 2471da177e4SLinus Torvalds * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 2481da177e4SLinus Torvalds */ 2491da177e4SLinus Torvalds int 2501da177e4SLinus Torvalds segment_type (char* name) 2511da177e4SLinus Torvalds { 2521da177e4SLinus Torvalds int rc; 2531da177e4SLinus Torvalds struct dcss_segment seg; 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds if (!MACHINE_IS_VM) 2561da177e4SLinus Torvalds return -ENOSYS; 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds dcss_mkname(name, seg.dcss_name); 2591da177e4SLinus Torvalds rc = query_segment_type (&seg); 2601da177e4SLinus Torvalds if (rc < 0) 2611da177e4SLinus Torvalds return rc; 2621da177e4SLinus Torvalds return seg.vm_segtype; 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds /* 266b2300b9eSHongjie Yang * check if segment collides with other segments that are currently loaded 267b2300b9eSHongjie Yang * returns 1 if this is the case, 0 if no collision was found 268b2300b9eSHongjie Yang */ 269b2300b9eSHongjie Yang static int 270b2300b9eSHongjie Yang segment_overlaps_others (struct dcss_segment *seg) 271b2300b9eSHongjie Yang { 272b2300b9eSHongjie Yang struct list_head *l; 273b2300b9eSHongjie Yang struct dcss_segment *tmp; 274b2300b9eSHongjie Yang 275b2300b9eSHongjie Yang BUG_ON(!mutex_is_locked(&dcss_lock)); 276b2300b9eSHongjie Yang list_for_each(l, &dcss_list) { 277b2300b9eSHongjie Yang tmp = list_entry(l, struct dcss_segment, list); 278b2300b9eSHongjie Yang if ((tmp->start_addr >> 20) > (seg->end >> 20)) 279b2300b9eSHongjie Yang continue; 280b2300b9eSHongjie Yang if ((tmp->end >> 20) < (seg->start_addr >> 20)) 281b2300b9eSHongjie Yang continue; 282b2300b9eSHongjie Yang if (seg == tmp) 283b2300b9eSHongjie Yang continue; 284b2300b9eSHongjie Yang return 1; 285b2300b9eSHongjie Yang } 286b2300b9eSHongjie Yang return 0; 287b2300b9eSHongjie Yang } 288b2300b9eSHongjie Yang 289b2300b9eSHongjie Yang /* 2901da177e4SLinus Torvalds * real segment loading function, called from segment_load 2911da177e4SLinus Torvalds */ 2921da177e4SLinus Torvalds static int 2931da177e4SLinus Torvalds __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end) 2941da177e4SLinus Torvalds { 295b2300b9eSHongjie Yang unsigned long start_addr, end_addr, dummy; 296c2f0e8c8SHeiko Carstens struct dcss_segment *seg; 297c2f0e8c8SHeiko Carstens int rc, diag_cc; 2981da177e4SLinus Torvalds 29989db4df1SHeiko Carstens start_addr = end_addr = 0; 300c2f0e8c8SHeiko Carstens seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA); 3011da177e4SLinus Torvalds if (seg == NULL) { 3021da177e4SLinus Torvalds rc = -ENOMEM; 3031da177e4SLinus Torvalds goto out; 3041da177e4SLinus Torvalds } 3051da177e4SLinus Torvalds dcss_mkname (name, seg->dcss_name); 3061da177e4SLinus Torvalds rc = query_segment_type (seg); 3071da177e4SLinus Torvalds if (rc < 0) 3081da177e4SLinus Torvalds goto out_free; 309f4eb07c1SHeiko Carstens 310b2300b9eSHongjie Yang if (segment_overlaps_others(seg)) { 311b2300b9eSHongjie Yang rc = -EBUSY; 312b2300b9eSHongjie Yang goto out_free; 313b2300b9eSHongjie Yang } 314b2300b9eSHongjie Yang 31517f34580SHeiko Carstens rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 316f4eb07c1SHeiko Carstens 317ca68305bSMartin Schwidefsky if (rc) 318f4eb07c1SHeiko Carstens goto out_free; 319f4eb07c1SHeiko Carstens 320444f0e54SGerald Schaefer seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL); 321444f0e54SGerald Schaefer if (seg->res == NULL) { 322444f0e54SGerald Schaefer rc = -ENOMEM; 323444f0e54SGerald Schaefer goto out_shared; 324444f0e54SGerald Schaefer } 325444f0e54SGerald Schaefer seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; 326444f0e54SGerald Schaefer seg->res->start = seg->start_addr; 327444f0e54SGerald Schaefer seg->res->end = seg->end; 328444f0e54SGerald Schaefer memcpy(&seg->res_name, seg->dcss_name, 8); 329444f0e54SGerald Schaefer EBCASC(seg->res_name, 8); 330444f0e54SGerald Schaefer seg->res_name[8] = '\0'; 3316b2ddf33SVasily Gorbik strlcat(seg->res_name, " (DCSS)", sizeof(seg->res_name)); 332444f0e54SGerald Schaefer seg->res->name = seg->res_name; 333444f0e54SGerald Schaefer rc = seg->vm_segtype; 334444f0e54SGerald Schaefer if (rc == SEG_TYPE_SC || 335444f0e54SGerald Schaefer ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared)) 336444f0e54SGerald Schaefer seg->res->flags |= IORESOURCE_READONLY; 337444f0e54SGerald Schaefer if (request_resource(&iomem_resource, seg->res)) { 338444f0e54SGerald Schaefer rc = -EBUSY; 339444f0e54SGerald Schaefer kfree(seg->res); 340444f0e54SGerald Schaefer goto out_shared; 341444f0e54SGerald Schaefer } 342444f0e54SGerald Schaefer 3431da177e4SLinus Torvalds if (do_nonshared) 344b2300b9eSHongjie Yang diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 345b2300b9eSHongjie Yang &start_addr, &end_addr); 3461da177e4SLinus Torvalds else 347b2300b9eSHongjie Yang diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 348b2300b9eSHongjie Yang &start_addr, &end_addr); 349b2300b9eSHongjie Yang if (diag_cc < 0) { 350b2300b9eSHongjie Yang dcss_diag(&purgeseg_scode, seg->dcss_name, 351b2300b9eSHongjie Yang &dummy, &dummy); 352b2300b9eSHongjie Yang rc = diag_cc; 353444f0e54SGerald Schaefer goto out_resource; 3541da177e4SLinus Torvalds } 355b2300b9eSHongjie Yang if (diag_cc > 1) { 356baebc70aSJoe Perches pr_warn("Loading DCSS %s failed with rc=%ld\n", name, end_addr); 357b2300b9eSHongjie Yang rc = dcss_diag_translate_rc(end_addr); 358b2300b9eSHongjie Yang dcss_diag(&purgeseg_scode, seg->dcss_name, 359b2300b9eSHongjie Yang &dummy, &dummy); 360b2300b9eSHongjie Yang goto out_resource; 361b2300b9eSHongjie Yang } 362b2300b9eSHongjie Yang seg->start_addr = start_addr; 363b2300b9eSHongjie Yang seg->end = end_addr; 3641da177e4SLinus Torvalds seg->do_nonshared = do_nonshared; 3651da177e4SLinus Torvalds atomic_set(&seg->ref_count, 1); 3661da177e4SLinus Torvalds list_add(&seg->list, &dcss_list); 3671da177e4SLinus Torvalds *addr = seg->start_addr; 3681da177e4SLinus Torvalds *end = seg->end; 3691da177e4SLinus Torvalds if (do_nonshared) 37093098bf0SHongjie Yang pr_info("DCSS %s of range %p to %p and type %s loaded as " 37193098bf0SHongjie Yang "exclusive-writable\n", name, (void*) seg->start_addr, 37293098bf0SHongjie Yang (void*) seg->end, segtype_string[seg->vm_segtype]); 373444f0e54SGerald Schaefer else { 37493098bf0SHongjie Yang pr_info("DCSS %s of range %p to %p and type %s loaded in " 37593098bf0SHongjie Yang "shared access mode\n", name, (void*) seg->start_addr, 37693098bf0SHongjie Yang (void*) seg->end, segtype_string[seg->vm_segtype]); 377444f0e54SGerald Schaefer } 3781da177e4SLinus Torvalds goto out; 379444f0e54SGerald Schaefer out_resource: 380444f0e54SGerald Schaefer release_resource(seg->res); 381444f0e54SGerald Schaefer kfree(seg->res); 382f4eb07c1SHeiko Carstens out_shared: 38317f34580SHeiko Carstens vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 3841da177e4SLinus Torvalds out_free: 3851da177e4SLinus Torvalds kfree(seg); 3861da177e4SLinus Torvalds out: 3871da177e4SLinus Torvalds return rc; 3881da177e4SLinus Torvalds } 3891da177e4SLinus Torvalds 3901da177e4SLinus Torvalds /* 3911da177e4SLinus Torvalds * this function loads a DCSS segment 3921da177e4SLinus Torvalds * name : name of the DCSS 3931da177e4SLinus Torvalds * do_nonshared : 0 indicates that the dcss should be shared with other linux images 3941da177e4SLinus Torvalds * 1 indicates that the dcss should be exclusive for this linux image 3951da177e4SLinus Torvalds * addr : will be filled with start address of the segment 3961da177e4SLinus Torvalds * end : will be filled with end address of the segment 3971da177e4SLinus Torvalds * return values: 3981da177e4SLinus Torvalds * -ENOSYS : we are not running on VM 3991da177e4SLinus Torvalds * -EIO : could not perform query or load diagnose 4001da177e4SLinus Torvalds * -ENOENT : no such segment 401b8e660b8SHeiko Carstens * -EOPNOTSUPP: multi-part segment cannot be used with linux 4021da177e4SLinus Torvalds * -ENOSPC : segment cannot be used (overlaps with storage) 4031da177e4SLinus Torvalds * -EBUSY : segment can temporarily not be used (overlaps with dcss) 4041da177e4SLinus Torvalds * -ERANGE : segment cannot be used (exceeds kernel mapping range) 4051da177e4SLinus Torvalds * -EPERM : segment is currently loaded with incompatible permissions 4061da177e4SLinus Torvalds * -ENOMEM : out of memory 4071da177e4SLinus Torvalds * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 4081da177e4SLinus Torvalds */ 4091da177e4SLinus Torvalds int 4101da177e4SLinus Torvalds segment_load (char *name, int do_nonshared, unsigned long *addr, 4111da177e4SLinus Torvalds unsigned long *end) 4121da177e4SLinus Torvalds { 4131da177e4SLinus Torvalds struct dcss_segment *seg; 4141da177e4SLinus Torvalds int rc; 4151da177e4SLinus Torvalds 4161da177e4SLinus Torvalds if (!MACHINE_IS_VM) 4171da177e4SLinus Torvalds return -ENOSYS; 4181da177e4SLinus Torvalds 41909252e77SHeiko Carstens mutex_lock(&dcss_lock); 4201da177e4SLinus Torvalds seg = segment_by_name (name); 4211da177e4SLinus Torvalds if (seg == NULL) 4221da177e4SLinus Torvalds rc = __segment_load (name, do_nonshared, addr, end); 4231da177e4SLinus Torvalds else { 4241da177e4SLinus Torvalds if (do_nonshared == seg->do_nonshared) { 4251da177e4SLinus Torvalds atomic_inc(&seg->ref_count); 4261da177e4SLinus Torvalds *addr = seg->start_addr; 4271da177e4SLinus Torvalds *end = seg->end; 4281da177e4SLinus Torvalds rc = seg->vm_segtype; 4291da177e4SLinus Torvalds } else { 4301da177e4SLinus Torvalds *addr = *end = 0; 4311da177e4SLinus Torvalds rc = -EPERM; 4321da177e4SLinus Torvalds } 4331da177e4SLinus Torvalds } 43409252e77SHeiko Carstens mutex_unlock(&dcss_lock); 4351da177e4SLinus Torvalds return rc; 4361da177e4SLinus Torvalds } 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds /* 4391da177e4SLinus Torvalds * this function modifies the shared state of a DCSS segment. note that 4401da177e4SLinus Torvalds * name : name of the DCSS 4411da177e4SLinus Torvalds * do_nonshared : 0 indicates that the dcss should be shared with other linux images 4421da177e4SLinus Torvalds * 1 indicates that the dcss should be exclusive for this linux image 4431da177e4SLinus Torvalds * return values: 4441da177e4SLinus Torvalds * -EIO : could not perform load diagnose (segment gone!) 4451da177e4SLinus Torvalds * -ENOENT : no such segment (segment gone!) 4461da177e4SLinus Torvalds * -EAGAIN : segment is in use by other exploiters, try later 4471da177e4SLinus Torvalds * -EINVAL : no segment with the given name is currently loaded - name invalid 448444f0e54SGerald Schaefer * -EBUSY : segment can temporarily not be used (overlaps with dcss) 4491da177e4SLinus Torvalds * 0 : operation succeeded 4501da177e4SLinus Torvalds */ 4511da177e4SLinus Torvalds int 4521da177e4SLinus Torvalds segment_modify_shared (char *name, int do_nonshared) 4531da177e4SLinus Torvalds { 4541da177e4SLinus Torvalds struct dcss_segment *seg; 455b2300b9eSHongjie Yang unsigned long start_addr, end_addr, dummy; 456b2300b9eSHongjie Yang int rc, diag_cc; 4571da177e4SLinus Torvalds 45889db4df1SHeiko Carstens start_addr = end_addr = 0; 45909252e77SHeiko Carstens mutex_lock(&dcss_lock); 4601da177e4SLinus Torvalds seg = segment_by_name (name); 4611da177e4SLinus Torvalds if (seg == NULL) { 4621da177e4SLinus Torvalds rc = -EINVAL; 4631da177e4SLinus Torvalds goto out_unlock; 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds if (do_nonshared == seg->do_nonshared) { 46693098bf0SHongjie Yang pr_info("DCSS %s is already in the requested access " 46793098bf0SHongjie Yang "mode\n", name); 4681da177e4SLinus Torvalds rc = 0; 4691da177e4SLinus Torvalds goto out_unlock; 4701da177e4SLinus Torvalds } 4711da177e4SLinus Torvalds if (atomic_read (&seg->ref_count) != 1) { 472baebc70aSJoe Perches pr_warn("DCSS %s is in use and cannot be reloaded\n", name); 4731da177e4SLinus Torvalds rc = -EAGAIN; 4741da177e4SLinus Torvalds goto out_unlock; 4751da177e4SLinus Torvalds } 476444f0e54SGerald Schaefer release_resource(seg->res); 477b2300b9eSHongjie Yang if (do_nonshared) 478444f0e54SGerald Schaefer seg->res->flags &= ~IORESOURCE_READONLY; 479b2300b9eSHongjie Yang else 480444f0e54SGerald Schaefer if (seg->vm_segtype == SEG_TYPE_SR || 481444f0e54SGerald Schaefer seg->vm_segtype == SEG_TYPE_ER) 482444f0e54SGerald Schaefer seg->res->flags |= IORESOURCE_READONLY; 483b2300b9eSHongjie Yang 484444f0e54SGerald Schaefer if (request_resource(&iomem_resource, seg->res)) { 485baebc70aSJoe Perches pr_warn("DCSS %s overlaps with used memory resources and cannot be reloaded\n", 486baebc70aSJoe Perches name); 487444f0e54SGerald Schaefer rc = -EBUSY; 488444f0e54SGerald Schaefer kfree(seg->res); 489b2300b9eSHongjie Yang goto out_del_mem; 490444f0e54SGerald Schaefer } 491b2300b9eSHongjie Yang 492b2300b9eSHongjie Yang dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 493b2300b9eSHongjie Yang if (do_nonshared) 494b2300b9eSHongjie Yang diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 495b2300b9eSHongjie Yang &start_addr, &end_addr); 496b2300b9eSHongjie Yang else 497b2300b9eSHongjie Yang diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 498b2300b9eSHongjie Yang &start_addr, &end_addr); 499b2300b9eSHongjie Yang if (diag_cc < 0) { 500b2300b9eSHongjie Yang rc = diag_cc; 501b2300b9eSHongjie Yang goto out_del_res; 502b2300b9eSHongjie Yang } 5031da177e4SLinus Torvalds if (diag_cc > 1) { 504baebc70aSJoe Perches pr_warn("Reloading DCSS %s failed with rc=%ld\n", 505baebc70aSJoe Perches name, end_addr); 506b2300b9eSHongjie Yang rc = dcss_diag_translate_rc(end_addr); 507b2300b9eSHongjie Yang goto out_del_res; 5081da177e4SLinus Torvalds } 509b2300b9eSHongjie Yang seg->start_addr = start_addr; 510b2300b9eSHongjie Yang seg->end = end_addr; 5111da177e4SLinus Torvalds seg->do_nonshared = do_nonshared; 5121da177e4SLinus Torvalds rc = 0; 5131da177e4SLinus Torvalds goto out_unlock; 514b2300b9eSHongjie Yang out_del_res: 515b2300b9eSHongjie Yang release_resource(seg->res); 516b2300b9eSHongjie Yang kfree(seg->res); 517b2300b9eSHongjie Yang out_del_mem: 51817f34580SHeiko Carstens vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 5191da177e4SLinus Torvalds list_del(&seg->list); 520b2300b9eSHongjie Yang dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 5211da177e4SLinus Torvalds kfree(seg); 5221da177e4SLinus Torvalds out_unlock: 52309252e77SHeiko Carstens mutex_unlock(&dcss_lock); 5241da177e4SLinus Torvalds return rc; 5251da177e4SLinus Torvalds } 5261da177e4SLinus Torvalds 5271da177e4SLinus Torvalds /* 5281da177e4SLinus Torvalds * Decrease the use count of a DCSS segment and remove 5291da177e4SLinus Torvalds * it from the address space if nobody is using it 5301da177e4SLinus Torvalds * any longer. 5311da177e4SLinus Torvalds */ 5321da177e4SLinus Torvalds void 5331da177e4SLinus Torvalds segment_unload(char *name) 5341da177e4SLinus Torvalds { 5351da177e4SLinus Torvalds unsigned long dummy; 5361da177e4SLinus Torvalds struct dcss_segment *seg; 5371da177e4SLinus Torvalds 5381da177e4SLinus Torvalds if (!MACHINE_IS_VM) 5391da177e4SLinus Torvalds return; 5401da177e4SLinus Torvalds 54109252e77SHeiko Carstens mutex_lock(&dcss_lock); 5421da177e4SLinus Torvalds seg = segment_by_name (name); 5431da177e4SLinus Torvalds if (seg == NULL) { 54493098bf0SHongjie Yang pr_err("Unloading unknown DCSS %s failed\n", name); 5451da177e4SLinus Torvalds goto out_unlock; 5461da177e4SLinus Torvalds } 547f4eb07c1SHeiko Carstens if (atomic_dec_return(&seg->ref_count) != 0) 548f4eb07c1SHeiko Carstens goto out_unlock; 549444f0e54SGerald Schaefer release_resource(seg->res); 550444f0e54SGerald Schaefer kfree(seg->res); 55117f34580SHeiko Carstens vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 5521da177e4SLinus Torvalds list_del(&seg->list); 553b2300b9eSHongjie Yang dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 5541da177e4SLinus Torvalds kfree(seg); 5551da177e4SLinus Torvalds out_unlock: 55609252e77SHeiko Carstens mutex_unlock(&dcss_lock); 5571da177e4SLinus Torvalds } 5581da177e4SLinus Torvalds 5591da177e4SLinus Torvalds /* 5601da177e4SLinus Torvalds * save segment content permanently 5611da177e4SLinus Torvalds */ 5621da177e4SLinus Torvalds void 5631da177e4SLinus Torvalds segment_save(char *name) 5641da177e4SLinus Torvalds { 5651da177e4SLinus Torvalds struct dcss_segment *seg; 5661da177e4SLinus Torvalds char cmd1[160]; 5671da177e4SLinus Torvalds char cmd2[80]; 5689b5dec1aSGerald Schaefer int i, response; 5691da177e4SLinus Torvalds 5701da177e4SLinus Torvalds if (!MACHINE_IS_VM) 5711da177e4SLinus Torvalds return; 5721da177e4SLinus Torvalds 57309252e77SHeiko Carstens mutex_lock(&dcss_lock); 5741da177e4SLinus Torvalds seg = segment_by_name (name); 5751da177e4SLinus Torvalds 5761da177e4SLinus Torvalds if (seg == NULL) { 57793098bf0SHongjie Yang pr_err("Saving unknown DCSS %s failed\n", name); 5786b4044bdSHeiko Carstens goto out; 5791da177e4SLinus Torvalds } 5801da177e4SLinus Torvalds 5811da177e4SLinus Torvalds sprintf(cmd1, "DEFSEG %s", name); 5821da177e4SLinus Torvalds for (i=0; i<seg->segcnt; i++) { 583b2300b9eSHongjie Yang sprintf(cmd1+strlen(cmd1), " %lX-%lX %s", 5841da177e4SLinus Torvalds seg->range[i].start >> PAGE_SHIFT, 5851da177e4SLinus Torvalds seg->range[i].end >> PAGE_SHIFT, 5861da177e4SLinus Torvalds segtype_string[seg->range[i].start & 0xff]); 5871da177e4SLinus Torvalds } 5881da177e4SLinus Torvalds sprintf(cmd2, "SAVESEG %s", name); 5899b5dec1aSGerald Schaefer response = 0; 5909b5dec1aSGerald Schaefer cpcmd(cmd1, NULL, 0, &response); 5919b5dec1aSGerald Schaefer if (response) { 59293098bf0SHongjie Yang pr_err("Saving a DCSS failed with DEFSEG response code " 59393098bf0SHongjie Yang "%i\n", response); 5949b5dec1aSGerald Schaefer goto out; 5959b5dec1aSGerald Schaefer } 5969b5dec1aSGerald Schaefer cpcmd(cmd2, NULL, 0, &response); 5979b5dec1aSGerald Schaefer if (response) { 59893098bf0SHongjie Yang pr_err("Saving a DCSS failed with SAVESEG response code " 59993098bf0SHongjie Yang "%i\n", response); 6009b5dec1aSGerald Schaefer goto out; 6019b5dec1aSGerald Schaefer } 6029b5dec1aSGerald Schaefer out: 60309252e77SHeiko Carstens mutex_unlock(&dcss_lock); 6041da177e4SLinus Torvalds } 6051da177e4SLinus Torvalds 606ca68305bSMartin Schwidefsky /* 607ca68305bSMartin Schwidefsky * print appropriate error message for segment_load()/segment_type() 608ca68305bSMartin Schwidefsky * return code 609ca68305bSMartin Schwidefsky */ 610ca68305bSMartin Schwidefsky void segment_warning(int rc, char *seg_name) 611ca68305bSMartin Schwidefsky { 612ca68305bSMartin Schwidefsky switch (rc) { 613ca68305bSMartin Schwidefsky case -ENOENT: 61493098bf0SHongjie Yang pr_err("DCSS %s cannot be loaded or queried\n", seg_name); 615ca68305bSMartin Schwidefsky break; 616ca68305bSMartin Schwidefsky case -ENOSYS: 61793098bf0SHongjie Yang pr_err("DCSS %s cannot be loaded or queried without " 61893098bf0SHongjie Yang "z/VM\n", seg_name); 619ca68305bSMartin Schwidefsky break; 620ca68305bSMartin Schwidefsky case -EIO: 62193098bf0SHongjie Yang pr_err("Loading or querying DCSS %s resulted in a " 622ca68305bSMartin Schwidefsky "hardware error\n", seg_name); 623ca68305bSMartin Schwidefsky break; 624b8e660b8SHeiko Carstens case -EOPNOTSUPP: 62593098bf0SHongjie Yang pr_err("DCSS %s has multiple page ranges and cannot be " 62693098bf0SHongjie Yang "loaded or queried\n", seg_name); 627ca68305bSMartin Schwidefsky break; 628ca68305bSMartin Schwidefsky case -ENOSPC: 62993098bf0SHongjie Yang pr_err("DCSS %s overlaps with used storage and cannot " 63093098bf0SHongjie Yang "be loaded\n", seg_name); 631ca68305bSMartin Schwidefsky break; 632ca68305bSMartin Schwidefsky case -EBUSY: 63393098bf0SHongjie Yang pr_err("%s needs used memory resources and cannot be " 63493098bf0SHongjie Yang "loaded or queried\n", seg_name); 635ca68305bSMartin Schwidefsky break; 636ca68305bSMartin Schwidefsky case -EPERM: 63793098bf0SHongjie Yang pr_err("DCSS %s is already loaded in a different access " 63893098bf0SHongjie Yang "mode\n", seg_name); 639ca68305bSMartin Schwidefsky break; 640ca68305bSMartin Schwidefsky case -ENOMEM: 64193098bf0SHongjie Yang pr_err("There is not enough memory to load or query " 64293098bf0SHongjie Yang "DCSS %s\n", seg_name); 643ca68305bSMartin Schwidefsky break; 644ca68305bSMartin Schwidefsky case -ERANGE: 64593098bf0SHongjie Yang pr_err("DCSS %s exceeds the kernel mapping range (%lu) " 64693098bf0SHongjie Yang "and cannot be loaded\n", seg_name, VMEM_MAX_PHYS); 647ca68305bSMartin Schwidefsky break; 648ca68305bSMartin Schwidefsky default: 649ca68305bSMartin Schwidefsky break; 650ca68305bSMartin Schwidefsky } 651ca68305bSMartin Schwidefsky } 652ca68305bSMartin Schwidefsky 6531da177e4SLinus Torvalds EXPORT_SYMBOL(segment_load); 6541da177e4SLinus Torvalds EXPORT_SYMBOL(segment_unload); 6551da177e4SLinus Torvalds EXPORT_SYMBOL(segment_save); 6561da177e4SLinus Torvalds EXPORT_SYMBOL(segment_type); 6571da177e4SLinus Torvalds EXPORT_SYMBOL(segment_modify_shared); 658ca68305bSMartin Schwidefsky EXPORT_SYMBOL(segment_warning); 659