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> 223434caecSChuhong Yuan #include <linux/refcount.h> 2365fddcfcSMike Rapoport #include <linux/pgtable.h> 241ec2772eSMartin Schwidefsky #include <asm/diag.h> 251da177e4SLinus Torvalds #include <asm/page.h> 261da177e4SLinus Torvalds #include <asm/ebcdic.h> 271da177e4SLinus Torvalds #include <asm/errno.h> 281da177e4SLinus Torvalds #include <asm/extmem.h> 291da177e4SLinus Torvalds #include <asm/cpcmd.h> 3036a2bd42SHeiko Carstens #include <asm/setup.h> 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds #define DCSS_PURGESEG 0x08 33b2300b9eSHongjie Yang #define DCSS_LOADSHRX 0x20 34b2300b9eSHongjie Yang #define DCSS_LOADNSRX 0x24 35b2300b9eSHongjie Yang #define DCSS_FINDSEGX 0x2c 36b2300b9eSHongjie Yang #define DCSS_SEGEXTX 0x38 371da177e4SLinus Torvalds #define DCSS_FINDSEGA 0x0c 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds struct qrange { 40b2300b9eSHongjie Yang unsigned long start; /* last byte type */ 41b2300b9eSHongjie Yang unsigned long end; /* last byte reserved */ 421da177e4SLinus Torvalds }; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds struct qout64 { 45b2300b9eSHongjie Yang unsigned long segstart; 46b2300b9eSHongjie Yang unsigned long segend; 471da177e4SLinus Torvalds int segcnt; 481da177e4SLinus Torvalds int segrcnt; 491da177e4SLinus Torvalds struct qrange range[6]; 501da177e4SLinus Torvalds }; 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds struct qin64 { 531da177e4SLinus Torvalds char qopcode; 541da177e4SLinus Torvalds char rsrv1[3]; 551da177e4SLinus Torvalds char qrcode; 561da177e4SLinus Torvalds char rsrv2[3]; 571da177e4SLinus Torvalds char qname[8]; 581da177e4SLinus Torvalds unsigned int qoutptr; 591da177e4SLinus Torvalds short int qoutlen; 601da177e4SLinus Torvalds }; 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds struct dcss_segment { 631da177e4SLinus Torvalds struct list_head list; 641da177e4SLinus Torvalds char dcss_name[8]; 656b2ddf33SVasily Gorbik char res_name[16]; 661da177e4SLinus Torvalds unsigned long start_addr; 671da177e4SLinus Torvalds unsigned long end; 683434caecSChuhong Yuan refcount_t ref_count; 691da177e4SLinus Torvalds int do_nonshared; 701da177e4SLinus Torvalds unsigned int vm_segtype; 711da177e4SLinus Torvalds struct qrange range[6]; 721da177e4SLinus Torvalds int segcnt; 73444f0e54SGerald Schaefer struct resource *res; 741da177e4SLinus Torvalds }; 751da177e4SLinus Torvalds 7609252e77SHeiko Carstens static DEFINE_MUTEX(dcss_lock); 77c11ca97eSDenis Cheng static LIST_HEAD(dcss_list); 781da177e4SLinus Torvalds static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", 791da177e4SLinus Torvalds "EW/EN-MIXED" }; 80ca571146SGerald Schaefer static int loadshr_scode = DCSS_LOADSHRX; 81ca571146SGerald Schaefer static int loadnsr_scode = DCSS_LOADNSRX; 82ca571146SGerald Schaefer static int purgeseg_scode = DCSS_PURGESEG; 83ca571146SGerald Schaefer static int segext_scode = DCSS_SEGEXTX; 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds /* 861da177e4SLinus Torvalds * Create the 8 bytes, ebcdic VM segment name from 871da177e4SLinus Torvalds * an ascii name. 881da177e4SLinus Torvalds */ 894d284cacSHeiko Carstens static void 901da177e4SLinus Torvalds dcss_mkname(char *name, char *dcss_name) 911da177e4SLinus Torvalds { 921da177e4SLinus Torvalds int i; 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds for (i = 0; i < 8; i++) { 951da177e4SLinus Torvalds if (name[i] == '\0') 961da177e4SLinus Torvalds break; 971da177e4SLinus Torvalds dcss_name[i] = toupper(name[i]); 980b925159SHeiko Carstens } 991da177e4SLinus Torvalds for (; i < 8; i++) 1001da177e4SLinus Torvalds dcss_name[i] = ' '; 1011da177e4SLinus Torvalds ASCEBC(dcss_name, 8); 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds /* 1061da177e4SLinus Torvalds * search all segments in dcss_list, and return the one 1071da177e4SLinus Torvalds * namend *name. If not found, return NULL. 1081da177e4SLinus Torvalds */ 1091da177e4SLinus Torvalds static struct dcss_segment * 1101da177e4SLinus Torvalds segment_by_name (char *name) 1111da177e4SLinus Torvalds { 1121da177e4SLinus Torvalds char dcss_name[9]; 1131da177e4SLinus Torvalds struct list_head *l; 1141da177e4SLinus Torvalds struct dcss_segment *tmp, *retval = NULL; 1151da177e4SLinus Torvalds 11609252e77SHeiko Carstens BUG_ON(!mutex_is_locked(&dcss_lock)); 1171da177e4SLinus Torvalds dcss_mkname (name, dcss_name); 1181da177e4SLinus Torvalds list_for_each (l, &dcss_list) { 1191da177e4SLinus Torvalds tmp = list_entry (l, struct dcss_segment, list); 1201da177e4SLinus Torvalds if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) { 1211da177e4SLinus Torvalds retval = tmp; 1221da177e4SLinus Torvalds break; 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds return retval; 1261da177e4SLinus Torvalds } 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds /* 1301da177e4SLinus Torvalds * Perform a function on a dcss segment. 1311da177e4SLinus Torvalds */ 1321da177e4SLinus Torvalds static inline int 133b2300b9eSHongjie Yang dcss_diag(int *func, void *parameter, 1341da177e4SLinus Torvalds unsigned long *ret1, unsigned long *ret2) 1351da177e4SLinus Torvalds { 1361da177e4SLinus Torvalds unsigned long rx, ry; 1371da177e4SLinus Torvalds int rc; 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds rx = (unsigned long) parameter; 140b2300b9eSHongjie Yang ry = (unsigned long) *func; 141b2300b9eSHongjie Yang 1421ec2772eSMartin Schwidefsky diag_stat_inc(DIAG_STAT_X064); 143b2300b9eSHongjie Yang asm volatile( 1441da177e4SLinus Torvalds " diag %0,%1,0x64\n" 1451da177e4SLinus Torvalds " ipm %2\n" 1461da177e4SLinus Torvalds " srl %2,28\n" 1471da177e4SLinus Torvalds : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); 1481da177e4SLinus Torvalds *ret1 = rx; 1491da177e4SLinus Torvalds *ret2 = ry; 1501da177e4SLinus Torvalds return rc; 1511da177e4SLinus Torvalds } 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds static inline int 1541da177e4SLinus Torvalds dcss_diag_translate_rc (int vm_rc) { 1551da177e4SLinus Torvalds if (vm_rc == 44) 1561da177e4SLinus Torvalds return -ENOENT; 1571da177e4SLinus Torvalds return -EIO; 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds /* do a diag to get info about a segment. 1621da177e4SLinus Torvalds * fills start_address, end and vm_segtype fields 1631da177e4SLinus Torvalds */ 1641da177e4SLinus Torvalds static int 1651da177e4SLinus Torvalds query_segment_type (struct dcss_segment *seg) 1661da177e4SLinus Torvalds { 1671da177e4SLinus Torvalds unsigned long dummy, vmrc; 168c2f0e8c8SHeiko Carstens int diag_cc, rc, i; 169c2f0e8c8SHeiko Carstens struct qout64 *qout; 170c2f0e8c8SHeiko Carstens struct qin64 *qin; 1711da177e4SLinus Torvalds 172c2f0e8c8SHeiko Carstens qin = kmalloc(sizeof(*qin), GFP_KERNEL | GFP_DMA); 173c2f0e8c8SHeiko Carstens qout = kmalloc(sizeof(*qout), GFP_KERNEL | GFP_DMA); 1741da177e4SLinus Torvalds if ((qin == NULL) || (qout == NULL)) { 1751da177e4SLinus Torvalds rc = -ENOMEM; 1761da177e4SLinus Torvalds goto out_free; 1771da177e4SLinus Torvalds } 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds /* initialize diag input parameters */ 1801da177e4SLinus Torvalds qin->qopcode = DCSS_FINDSEGA; 1811da177e4SLinus Torvalds qin->qoutptr = (unsigned long) qout; 1821da177e4SLinus Torvalds qin->qoutlen = sizeof(struct qout64); 1831da177e4SLinus Torvalds memcpy (qin->qname, seg->dcss_name, 8); 1841da177e4SLinus Torvalds 185b2300b9eSHongjie Yang diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc); 1861da177e4SLinus Torvalds 187b2300b9eSHongjie Yang if (diag_cc < 0) { 188b2300b9eSHongjie Yang rc = diag_cc; 189b2300b9eSHongjie Yang goto out_free; 190b2300b9eSHongjie Yang } 1911da177e4SLinus Torvalds if (diag_cc > 1) { 192baebc70aSJoe Perches pr_warn("Querying a DCSS type failed with rc=%ld\n", vmrc); 1931da177e4SLinus Torvalds rc = dcss_diag_translate_rc (vmrc); 1941da177e4SLinus Torvalds goto out_free; 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds if (qout->segcnt > 6) { 198b8e660b8SHeiko Carstens rc = -EOPNOTSUPP; 1991da177e4SLinus Torvalds goto out_free; 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds if (qout->segcnt == 1) { 2031da177e4SLinus Torvalds seg->vm_segtype = qout->range[0].start & 0xff; 2041da177e4SLinus Torvalds } else { 2051da177e4SLinus Torvalds /* multi-part segment. only one type supported here: 2061da177e4SLinus Torvalds - all parts are contiguous 2071da177e4SLinus Torvalds - all parts are either EW or EN type 2081da177e4SLinus Torvalds - maximum 6 parts allowed */ 2091da177e4SLinus Torvalds unsigned long start = qout->segstart >> PAGE_SHIFT; 2101da177e4SLinus Torvalds for (i=0; i<qout->segcnt; i++) { 2111da177e4SLinus Torvalds if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) && 2121da177e4SLinus Torvalds ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) { 213b8e660b8SHeiko Carstens rc = -EOPNOTSUPP; 2141da177e4SLinus Torvalds goto out_free; 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds if (start != qout->range[i].start >> PAGE_SHIFT) { 217b8e660b8SHeiko Carstens rc = -EOPNOTSUPP; 2181da177e4SLinus Torvalds goto out_free; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds start = (qout->range[i].end >> PAGE_SHIFT) + 1; 2211da177e4SLinus Torvalds } 2221da177e4SLinus Torvalds seg->vm_segtype = SEG_TYPE_EWEN; 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds /* analyze diag output and update seg */ 2261da177e4SLinus Torvalds seg->start_addr = qout->segstart; 2271da177e4SLinus Torvalds seg->end = qout->segend; 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds memcpy (seg->range, qout->range, 6*sizeof(struct qrange)); 2301da177e4SLinus Torvalds seg->segcnt = qout->segcnt; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds rc = 0; 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds out_free: 235b2325fe1SJesper Juhl kfree(qin); 236b2325fe1SJesper Juhl kfree(qout); 2371da177e4SLinus Torvalds return rc; 2381da177e4SLinus Torvalds } 2391da177e4SLinus Torvalds 2401da177e4SLinus Torvalds /* 2411da177e4SLinus Torvalds * get info about a segment 2421da177e4SLinus Torvalds * possible return values: 2431da177e4SLinus Torvalds * -ENOSYS : we are not running on VM 2441da177e4SLinus Torvalds * -EIO : could not perform query diagnose 2451da177e4SLinus Torvalds * -ENOENT : no such segment 246b8e660b8SHeiko Carstens * -EOPNOTSUPP: multi-part segment cannot be used with linux 2471da177e4SLinus Torvalds * -ENOMEM : out of memory 2481da177e4SLinus Torvalds * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 2491da177e4SLinus Torvalds */ 2501da177e4SLinus Torvalds int 2511da177e4SLinus Torvalds segment_type (char* name) 2521da177e4SLinus Torvalds { 2531da177e4SLinus Torvalds int rc; 2541da177e4SLinus Torvalds struct dcss_segment seg; 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds if (!MACHINE_IS_VM) 2571da177e4SLinus Torvalds return -ENOSYS; 2581da177e4SLinus Torvalds 2591da177e4SLinus Torvalds dcss_mkname(name, seg.dcss_name); 2601da177e4SLinus Torvalds rc = query_segment_type (&seg); 2611da177e4SLinus Torvalds if (rc < 0) 2621da177e4SLinus Torvalds return rc; 2631da177e4SLinus Torvalds return seg.vm_segtype; 2641da177e4SLinus Torvalds } 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds /* 267b2300b9eSHongjie Yang * check if segment collides with other segments that are currently loaded 268b2300b9eSHongjie Yang * returns 1 if this is the case, 0 if no collision was found 269b2300b9eSHongjie Yang */ 270b2300b9eSHongjie Yang static int 271b2300b9eSHongjie Yang segment_overlaps_others (struct dcss_segment *seg) 272b2300b9eSHongjie Yang { 273b2300b9eSHongjie Yang struct list_head *l; 274b2300b9eSHongjie Yang struct dcss_segment *tmp; 275b2300b9eSHongjie Yang 276b2300b9eSHongjie Yang BUG_ON(!mutex_is_locked(&dcss_lock)); 277b2300b9eSHongjie Yang list_for_each(l, &dcss_list) { 278b2300b9eSHongjie Yang tmp = list_entry(l, struct dcss_segment, list); 279b2300b9eSHongjie Yang if ((tmp->start_addr >> 20) > (seg->end >> 20)) 280b2300b9eSHongjie Yang continue; 281b2300b9eSHongjie Yang if ((tmp->end >> 20) < (seg->start_addr >> 20)) 282b2300b9eSHongjie Yang continue; 283b2300b9eSHongjie Yang if (seg == tmp) 284b2300b9eSHongjie Yang continue; 285b2300b9eSHongjie Yang return 1; 286b2300b9eSHongjie Yang } 287b2300b9eSHongjie Yang return 0; 288b2300b9eSHongjie Yang } 289b2300b9eSHongjie Yang 290b2300b9eSHongjie Yang /* 2911da177e4SLinus Torvalds * real segment loading function, called from segment_load 2921da177e4SLinus Torvalds */ 2931da177e4SLinus Torvalds static int 2941da177e4SLinus Torvalds __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end) 2951da177e4SLinus Torvalds { 296b2300b9eSHongjie Yang unsigned long start_addr, end_addr, dummy; 297c2f0e8c8SHeiko Carstens struct dcss_segment *seg; 298c2f0e8c8SHeiko Carstens int rc, diag_cc; 2991da177e4SLinus Torvalds 30089db4df1SHeiko Carstens start_addr = end_addr = 0; 301c2f0e8c8SHeiko Carstens seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA); 3021da177e4SLinus Torvalds if (seg == NULL) { 3031da177e4SLinus Torvalds rc = -ENOMEM; 3041da177e4SLinus Torvalds goto out; 3051da177e4SLinus Torvalds } 3061da177e4SLinus Torvalds dcss_mkname (name, seg->dcss_name); 3071da177e4SLinus Torvalds rc = query_segment_type (seg); 3081da177e4SLinus Torvalds if (rc < 0) 3091da177e4SLinus Torvalds goto out_free; 310f4eb07c1SHeiko Carstens 311b2300b9eSHongjie Yang if (segment_overlaps_others(seg)) { 312b2300b9eSHongjie Yang rc = -EBUSY; 313b2300b9eSHongjie Yang goto out_free; 314b2300b9eSHongjie Yang } 315b2300b9eSHongjie Yang 316444f0e54SGerald Schaefer seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL); 317444f0e54SGerald Schaefer if (seg->res == NULL) { 318444f0e54SGerald Schaefer rc = -ENOMEM; 319f05f62d0SDavid Hildenbrand goto out_free; 320444f0e54SGerald Schaefer } 321444f0e54SGerald Schaefer seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; 322444f0e54SGerald Schaefer seg->res->start = seg->start_addr; 323444f0e54SGerald Schaefer seg->res->end = seg->end; 324444f0e54SGerald Schaefer memcpy(&seg->res_name, seg->dcss_name, 8); 325444f0e54SGerald Schaefer EBCASC(seg->res_name, 8); 326444f0e54SGerald Schaefer seg->res_name[8] = '\0'; 3276b2ddf33SVasily Gorbik strlcat(seg->res_name, " (DCSS)", sizeof(seg->res_name)); 328444f0e54SGerald Schaefer seg->res->name = seg->res_name; 329444f0e54SGerald Schaefer rc = seg->vm_segtype; 330444f0e54SGerald Schaefer if (rc == SEG_TYPE_SC || 331444f0e54SGerald Schaefer ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared)) 332444f0e54SGerald Schaefer seg->res->flags |= IORESOURCE_READONLY; 333f05f62d0SDavid Hildenbrand 334f05f62d0SDavid Hildenbrand /* Check for overlapping resources before adding the mapping. */ 335444f0e54SGerald Schaefer if (request_resource(&iomem_resource, seg->res)) { 336444f0e54SGerald Schaefer rc = -EBUSY; 337f05f62d0SDavid Hildenbrand goto out_free_resource; 338444f0e54SGerald Schaefer } 339444f0e54SGerald Schaefer 340f05f62d0SDavid Hildenbrand rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 341f05f62d0SDavid Hildenbrand if (rc) 342f05f62d0SDavid Hildenbrand goto out_resource; 343f05f62d0SDavid Hildenbrand 3441da177e4SLinus Torvalds if (do_nonshared) 345b2300b9eSHongjie Yang diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 346b2300b9eSHongjie Yang &start_addr, &end_addr); 3471da177e4SLinus Torvalds else 348b2300b9eSHongjie Yang diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 349b2300b9eSHongjie Yang &start_addr, &end_addr); 350b2300b9eSHongjie Yang if (diag_cc < 0) { 351b2300b9eSHongjie Yang dcss_diag(&purgeseg_scode, seg->dcss_name, 352b2300b9eSHongjie Yang &dummy, &dummy); 353b2300b9eSHongjie Yang rc = diag_cc; 354f05f62d0SDavid Hildenbrand goto out_mapping; 3551da177e4SLinus Torvalds } 356b2300b9eSHongjie Yang if (diag_cc > 1) { 357baebc70aSJoe Perches pr_warn("Loading DCSS %s failed with rc=%ld\n", name, end_addr); 358b2300b9eSHongjie Yang rc = dcss_diag_translate_rc(end_addr); 359b2300b9eSHongjie Yang dcss_diag(&purgeseg_scode, seg->dcss_name, 360b2300b9eSHongjie Yang &dummy, &dummy); 361f05f62d0SDavid Hildenbrand goto out_mapping; 362b2300b9eSHongjie Yang } 363b2300b9eSHongjie Yang seg->start_addr = start_addr; 364b2300b9eSHongjie Yang seg->end = end_addr; 3651da177e4SLinus Torvalds seg->do_nonshared = do_nonshared; 3663434caecSChuhong Yuan refcount_set(&seg->ref_count, 1); 3671da177e4SLinus Torvalds list_add(&seg->list, &dcss_list); 3681da177e4SLinus Torvalds *addr = seg->start_addr; 3691da177e4SLinus Torvalds *end = seg->end; 3701da177e4SLinus Torvalds if (do_nonshared) 371f1777625SGerald Schaefer pr_info("DCSS %s of range %px to %px and type %s loaded as " 37293098bf0SHongjie Yang "exclusive-writable\n", name, (void*) seg->start_addr, 37393098bf0SHongjie Yang (void*) seg->end, segtype_string[seg->vm_segtype]); 374444f0e54SGerald Schaefer else { 375f1777625SGerald Schaefer pr_info("DCSS %s of range %px to %px and type %s loaded in " 37693098bf0SHongjie Yang "shared access mode\n", name, (void*) seg->start_addr, 37793098bf0SHongjie Yang (void*) seg->end, segtype_string[seg->vm_segtype]); 378444f0e54SGerald Schaefer } 3791da177e4SLinus Torvalds goto out; 380f05f62d0SDavid Hildenbrand out_mapping: 381f05f62d0SDavid Hildenbrand vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 382444f0e54SGerald Schaefer out_resource: 383444f0e54SGerald Schaefer release_resource(seg->res); 384f05f62d0SDavid Hildenbrand out_free_resource: 385444f0e54SGerald Schaefer kfree(seg->res); 3861da177e4SLinus Torvalds out_free: 3871da177e4SLinus Torvalds kfree(seg); 3881da177e4SLinus Torvalds out: 3891da177e4SLinus Torvalds return rc; 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds 3921da177e4SLinus Torvalds /* 3931da177e4SLinus Torvalds * this function loads a DCSS segment 3941da177e4SLinus Torvalds * name : name of the DCSS 3951da177e4SLinus Torvalds * do_nonshared : 0 indicates that the dcss should be shared with other linux images 3961da177e4SLinus Torvalds * 1 indicates that the dcss should be exclusive for this linux image 3971da177e4SLinus Torvalds * addr : will be filled with start address of the segment 3981da177e4SLinus Torvalds * end : will be filled with end address of the segment 3991da177e4SLinus Torvalds * return values: 4001da177e4SLinus Torvalds * -ENOSYS : we are not running on VM 4011da177e4SLinus Torvalds * -EIO : could not perform query or load diagnose 4021da177e4SLinus Torvalds * -ENOENT : no such segment 403b8e660b8SHeiko Carstens * -EOPNOTSUPP: multi-part segment cannot be used with linux 4040ef5d691SDavid Hildenbrand * -EBUSY : segment cannot be used (overlaps with dcss or storage) 4051da177e4SLinus Torvalds * -ERANGE : segment cannot be used (exceeds kernel mapping range) 4061da177e4SLinus Torvalds * -EPERM : segment is currently loaded with incompatible permissions 4071da177e4SLinus Torvalds * -ENOMEM : out of memory 4081da177e4SLinus Torvalds * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 4091da177e4SLinus Torvalds */ 4101da177e4SLinus Torvalds int 4111da177e4SLinus Torvalds segment_load (char *name, int do_nonshared, unsigned long *addr, 4121da177e4SLinus Torvalds unsigned long *end) 4131da177e4SLinus Torvalds { 4141da177e4SLinus Torvalds struct dcss_segment *seg; 4151da177e4SLinus Torvalds int rc; 4161da177e4SLinus Torvalds 4171da177e4SLinus Torvalds if (!MACHINE_IS_VM) 4181da177e4SLinus Torvalds return -ENOSYS; 4191da177e4SLinus Torvalds 42009252e77SHeiko Carstens mutex_lock(&dcss_lock); 4211da177e4SLinus Torvalds seg = segment_by_name (name); 4221da177e4SLinus Torvalds if (seg == NULL) 4231da177e4SLinus Torvalds rc = __segment_load (name, do_nonshared, addr, end); 4241da177e4SLinus Torvalds else { 4251da177e4SLinus Torvalds if (do_nonshared == seg->do_nonshared) { 4263434caecSChuhong Yuan refcount_inc(&seg->ref_count); 4271da177e4SLinus Torvalds *addr = seg->start_addr; 4281da177e4SLinus Torvalds *end = seg->end; 4291da177e4SLinus Torvalds rc = seg->vm_segtype; 4301da177e4SLinus Torvalds } else { 4311da177e4SLinus Torvalds *addr = *end = 0; 4321da177e4SLinus Torvalds rc = -EPERM; 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds } 43509252e77SHeiko Carstens mutex_unlock(&dcss_lock); 4361da177e4SLinus Torvalds return rc; 4371da177e4SLinus Torvalds } 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds /* 4401da177e4SLinus Torvalds * this function modifies the shared state of a DCSS segment. note that 4411da177e4SLinus Torvalds * name : name of the DCSS 4421da177e4SLinus Torvalds * do_nonshared : 0 indicates that the dcss should be shared with other linux images 4431da177e4SLinus Torvalds * 1 indicates that the dcss should be exclusive for this linux image 4441da177e4SLinus Torvalds * return values: 4451da177e4SLinus Torvalds * -EIO : could not perform load diagnose (segment gone!) 4461da177e4SLinus Torvalds * -ENOENT : no such segment (segment gone!) 4471da177e4SLinus Torvalds * -EAGAIN : segment is in use by other exploiters, try later 4481da177e4SLinus Torvalds * -EINVAL : no segment with the given name is currently loaded - name invalid 449444f0e54SGerald Schaefer * -EBUSY : segment can temporarily not be used (overlaps with dcss) 4501da177e4SLinus Torvalds * 0 : operation succeeded 4511da177e4SLinus Torvalds */ 4521da177e4SLinus Torvalds int 4531da177e4SLinus Torvalds segment_modify_shared (char *name, int do_nonshared) 4541da177e4SLinus Torvalds { 4551da177e4SLinus Torvalds struct dcss_segment *seg; 456b2300b9eSHongjie Yang unsigned long start_addr, end_addr, dummy; 457b2300b9eSHongjie Yang int rc, diag_cc; 4581da177e4SLinus Torvalds 45989db4df1SHeiko Carstens start_addr = end_addr = 0; 46009252e77SHeiko Carstens mutex_lock(&dcss_lock); 4611da177e4SLinus Torvalds seg = segment_by_name (name); 4621da177e4SLinus Torvalds if (seg == NULL) { 4631da177e4SLinus Torvalds rc = -EINVAL; 4641da177e4SLinus Torvalds goto out_unlock; 4651da177e4SLinus Torvalds } 4661da177e4SLinus Torvalds if (do_nonshared == seg->do_nonshared) { 46793098bf0SHongjie Yang pr_info("DCSS %s is already in the requested access " 46893098bf0SHongjie Yang "mode\n", name); 4691da177e4SLinus Torvalds rc = 0; 4701da177e4SLinus Torvalds goto out_unlock; 4711da177e4SLinus Torvalds } 4723434caecSChuhong Yuan if (refcount_read(&seg->ref_count) != 1) { 473baebc70aSJoe Perches pr_warn("DCSS %s is in use and cannot be reloaded\n", name); 4741da177e4SLinus Torvalds rc = -EAGAIN; 4751da177e4SLinus Torvalds goto out_unlock; 4761da177e4SLinus Torvalds } 477444f0e54SGerald Schaefer release_resource(seg->res); 478b2300b9eSHongjie Yang if (do_nonshared) 479444f0e54SGerald Schaefer seg->res->flags &= ~IORESOURCE_READONLY; 480b2300b9eSHongjie Yang else 481444f0e54SGerald Schaefer if (seg->vm_segtype == SEG_TYPE_SR || 482444f0e54SGerald Schaefer seg->vm_segtype == SEG_TYPE_ER) 483444f0e54SGerald Schaefer seg->res->flags |= IORESOURCE_READONLY; 484b2300b9eSHongjie Yang 485444f0e54SGerald Schaefer if (request_resource(&iomem_resource, seg->res)) { 486baebc70aSJoe Perches pr_warn("DCSS %s overlaps with used memory resources and cannot be reloaded\n", 487baebc70aSJoe Perches name); 488444f0e54SGerald Schaefer rc = -EBUSY; 489444f0e54SGerald Schaefer kfree(seg->res); 490b2300b9eSHongjie Yang goto out_del_mem; 491444f0e54SGerald Schaefer } 492b2300b9eSHongjie Yang 493b2300b9eSHongjie Yang dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 494b2300b9eSHongjie Yang if (do_nonshared) 495b2300b9eSHongjie Yang diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, 496b2300b9eSHongjie Yang &start_addr, &end_addr); 497b2300b9eSHongjie Yang else 498b2300b9eSHongjie Yang diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, 499b2300b9eSHongjie Yang &start_addr, &end_addr); 500b2300b9eSHongjie Yang if (diag_cc < 0) { 501b2300b9eSHongjie Yang rc = diag_cc; 502b2300b9eSHongjie Yang goto out_del_res; 503b2300b9eSHongjie Yang } 5041da177e4SLinus Torvalds if (diag_cc > 1) { 505baebc70aSJoe Perches pr_warn("Reloading DCSS %s failed with rc=%ld\n", 506baebc70aSJoe Perches name, end_addr); 507b2300b9eSHongjie Yang rc = dcss_diag_translate_rc(end_addr); 508b2300b9eSHongjie Yang goto out_del_res; 5091da177e4SLinus Torvalds } 510b2300b9eSHongjie Yang seg->start_addr = start_addr; 511b2300b9eSHongjie Yang seg->end = end_addr; 5121da177e4SLinus Torvalds seg->do_nonshared = do_nonshared; 5131da177e4SLinus Torvalds rc = 0; 5141da177e4SLinus Torvalds goto out_unlock; 515b2300b9eSHongjie Yang out_del_res: 516b2300b9eSHongjie Yang release_resource(seg->res); 517b2300b9eSHongjie Yang kfree(seg->res); 518b2300b9eSHongjie Yang out_del_mem: 51917f34580SHeiko Carstens vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 5201da177e4SLinus Torvalds list_del(&seg->list); 521b2300b9eSHongjie Yang dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 5221da177e4SLinus Torvalds kfree(seg); 5231da177e4SLinus Torvalds out_unlock: 52409252e77SHeiko Carstens mutex_unlock(&dcss_lock); 5251da177e4SLinus Torvalds return rc; 5261da177e4SLinus Torvalds } 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds /* 5291da177e4SLinus Torvalds * Decrease the use count of a DCSS segment and remove 5301da177e4SLinus Torvalds * it from the address space if nobody is using it 5311da177e4SLinus Torvalds * any longer. 5321da177e4SLinus Torvalds */ 5331da177e4SLinus Torvalds void 5341da177e4SLinus Torvalds segment_unload(char *name) 5351da177e4SLinus Torvalds { 5361da177e4SLinus Torvalds unsigned long dummy; 5371da177e4SLinus Torvalds struct dcss_segment *seg; 5381da177e4SLinus Torvalds 5391da177e4SLinus Torvalds if (!MACHINE_IS_VM) 5401da177e4SLinus Torvalds return; 5411da177e4SLinus Torvalds 54209252e77SHeiko Carstens mutex_lock(&dcss_lock); 5431da177e4SLinus Torvalds seg = segment_by_name (name); 5441da177e4SLinus Torvalds if (seg == NULL) { 54593098bf0SHongjie Yang pr_err("Unloading unknown DCSS %s failed\n", name); 5461da177e4SLinus Torvalds goto out_unlock; 5471da177e4SLinus Torvalds } 5483434caecSChuhong Yuan if (!refcount_dec_and_test(&seg->ref_count)) 549f4eb07c1SHeiko Carstens goto out_unlock; 550444f0e54SGerald Schaefer release_resource(seg->res); 551444f0e54SGerald Schaefer kfree(seg->res); 55217f34580SHeiko Carstens vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); 5531da177e4SLinus Torvalds list_del(&seg->list); 554b2300b9eSHongjie Yang dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); 5551da177e4SLinus Torvalds kfree(seg); 5561da177e4SLinus Torvalds out_unlock: 55709252e77SHeiko Carstens mutex_unlock(&dcss_lock); 5581da177e4SLinus Torvalds } 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds /* 5611da177e4SLinus Torvalds * save segment content permanently 5621da177e4SLinus Torvalds */ 5631da177e4SLinus Torvalds void 5641da177e4SLinus Torvalds segment_save(char *name) 5651da177e4SLinus Torvalds { 5661da177e4SLinus Torvalds struct dcss_segment *seg; 5671da177e4SLinus Torvalds char cmd1[160]; 5681da177e4SLinus Torvalds char cmd2[80]; 5699b5dec1aSGerald Schaefer int i, response; 5701da177e4SLinus Torvalds 5711da177e4SLinus Torvalds if (!MACHINE_IS_VM) 5721da177e4SLinus Torvalds return; 5731da177e4SLinus Torvalds 57409252e77SHeiko Carstens mutex_lock(&dcss_lock); 5751da177e4SLinus Torvalds seg = segment_by_name (name); 5761da177e4SLinus Torvalds 5771da177e4SLinus Torvalds if (seg == NULL) { 57893098bf0SHongjie Yang pr_err("Saving unknown DCSS %s failed\n", name); 5796b4044bdSHeiko Carstens goto out; 5801da177e4SLinus Torvalds } 5811da177e4SLinus Torvalds 5821da177e4SLinus Torvalds sprintf(cmd1, "DEFSEG %s", name); 5831da177e4SLinus Torvalds for (i=0; i<seg->segcnt; i++) { 584b2300b9eSHongjie Yang sprintf(cmd1+strlen(cmd1), " %lX-%lX %s", 5851da177e4SLinus Torvalds seg->range[i].start >> PAGE_SHIFT, 5861da177e4SLinus Torvalds seg->range[i].end >> PAGE_SHIFT, 5871da177e4SLinus Torvalds segtype_string[seg->range[i].start & 0xff]); 5881da177e4SLinus Torvalds } 5891da177e4SLinus Torvalds sprintf(cmd2, "SAVESEG %s", name); 5909b5dec1aSGerald Schaefer response = 0; 5919b5dec1aSGerald Schaefer cpcmd(cmd1, NULL, 0, &response); 5929b5dec1aSGerald Schaefer if (response) { 59393098bf0SHongjie Yang pr_err("Saving a DCSS failed with DEFSEG response code " 59493098bf0SHongjie Yang "%i\n", response); 5959b5dec1aSGerald Schaefer goto out; 5969b5dec1aSGerald Schaefer } 5979b5dec1aSGerald Schaefer cpcmd(cmd2, NULL, 0, &response); 5989b5dec1aSGerald Schaefer if (response) { 59993098bf0SHongjie Yang pr_err("Saving a DCSS failed with SAVESEG response code " 60093098bf0SHongjie Yang "%i\n", response); 6019b5dec1aSGerald Schaefer goto out; 6029b5dec1aSGerald Schaefer } 6039b5dec1aSGerald Schaefer out: 60409252e77SHeiko Carstens mutex_unlock(&dcss_lock); 6051da177e4SLinus Torvalds } 6061da177e4SLinus Torvalds 607ca68305bSMartin Schwidefsky /* 608ca68305bSMartin Schwidefsky * print appropriate error message for segment_load()/segment_type() 609ca68305bSMartin Schwidefsky * return code 610ca68305bSMartin Schwidefsky */ 611ca68305bSMartin Schwidefsky void segment_warning(int rc, char *seg_name) 612ca68305bSMartin Schwidefsky { 613ca68305bSMartin Schwidefsky switch (rc) { 614ca68305bSMartin Schwidefsky case -ENOENT: 61593098bf0SHongjie Yang pr_err("DCSS %s cannot be loaded or queried\n", seg_name); 616ca68305bSMartin Schwidefsky break; 617ca68305bSMartin Schwidefsky case -ENOSYS: 61893098bf0SHongjie Yang pr_err("DCSS %s cannot be loaded or queried without " 61993098bf0SHongjie Yang "z/VM\n", seg_name); 620ca68305bSMartin Schwidefsky break; 621ca68305bSMartin Schwidefsky case -EIO: 62293098bf0SHongjie Yang pr_err("Loading or querying DCSS %s resulted in a " 623ca68305bSMartin Schwidefsky "hardware error\n", seg_name); 624ca68305bSMartin Schwidefsky break; 625b8e660b8SHeiko Carstens case -EOPNOTSUPP: 62693098bf0SHongjie Yang pr_err("DCSS %s has multiple page ranges and cannot be " 62793098bf0SHongjie Yang "loaded or queried\n", seg_name); 628ca68305bSMartin Schwidefsky break; 629ca68305bSMartin Schwidefsky case -EBUSY: 63093098bf0SHongjie Yang pr_err("%s needs used memory resources and cannot be " 63193098bf0SHongjie Yang "loaded or queried\n", seg_name); 632ca68305bSMartin Schwidefsky break; 633ca68305bSMartin Schwidefsky case -EPERM: 63493098bf0SHongjie Yang pr_err("DCSS %s is already loaded in a different access " 63593098bf0SHongjie Yang "mode\n", seg_name); 636ca68305bSMartin Schwidefsky break; 637ca68305bSMartin Schwidefsky case -ENOMEM: 63893098bf0SHongjie Yang pr_err("There is not enough memory to load or query " 63993098bf0SHongjie Yang "DCSS %s\n", seg_name); 640ca68305bSMartin Schwidefsky break; 641ca68305bSMartin Schwidefsky case -ERANGE: 64293098bf0SHongjie Yang pr_err("DCSS %s exceeds the kernel mapping range (%lu) " 64393098bf0SHongjie Yang "and cannot be loaded\n", seg_name, VMEM_MAX_PHYS); 644ca68305bSMartin Schwidefsky break; 645ca68305bSMartin Schwidefsky default: 646ca68305bSMartin Schwidefsky break; 647ca68305bSMartin Schwidefsky } 648ca68305bSMartin Schwidefsky } 649ca68305bSMartin Schwidefsky 6501da177e4SLinus Torvalds EXPORT_SYMBOL(segment_load); 6511da177e4SLinus Torvalds EXPORT_SYMBOL(segment_unload); 6521da177e4SLinus Torvalds EXPORT_SYMBOL(segment_save); 6531da177e4SLinus Torvalds EXPORT_SYMBOL(segment_type); 6541da177e4SLinus Torvalds EXPORT_SYMBOL(segment_modify_shared); 655ca68305bSMartin Schwidefsky EXPORT_SYMBOL(segment_warning); 656