11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * File...........: arch/s390/mm/extmem.c 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> 71da177e4SLinus Torvalds * (C) IBM Corporation 2002-2004 81da177e4SLinus Torvalds */ 91da177e4SLinus Torvalds 101da177e4SLinus Torvalds #include <linux/kernel.h> 111da177e4SLinus Torvalds #include <linux/string.h> 121da177e4SLinus Torvalds #include <linux/spinlock.h> 131da177e4SLinus Torvalds #include <linux/list.h> 141da177e4SLinus Torvalds #include <linux/slab.h> 151da177e4SLinus Torvalds #include <linux/module.h> 161da177e4SLinus Torvalds #include <linux/bootmem.h> 1736a2bd42SHeiko Carstens #include <linux/ctype.h> 18444f0e54SGerald Schaefer #include <linux/ioport.h> 191da177e4SLinus Torvalds #include <asm/page.h> 20f4eb07c1SHeiko Carstens #include <asm/pgtable.h> 211da177e4SLinus Torvalds #include <asm/ebcdic.h> 221da177e4SLinus Torvalds #include <asm/errno.h> 231da177e4SLinus Torvalds #include <asm/extmem.h> 241da177e4SLinus Torvalds #include <asm/cpcmd.h> 2536a2bd42SHeiko Carstens #include <asm/setup.h> 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds #define DCSS_DEBUG /* Debug messages on/off */ 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds #define DCSS_NAME "extmem" 301da177e4SLinus Torvalds #ifdef DCSS_DEBUG 311da177e4SLinus Torvalds #define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSS_NAME " debug:" x) 321da177e4SLinus Torvalds #else 331da177e4SLinus Torvalds #define PRINT_DEBUG(x...) do {} while (0) 341da177e4SLinus Torvalds #endif 351da177e4SLinus Torvalds #define PRINT_INFO(x...) printk(KERN_INFO DCSS_NAME " info:" x) 361da177e4SLinus Torvalds #define PRINT_WARN(x...) printk(KERN_WARNING DCSS_NAME " warning:" x) 371da177e4SLinus Torvalds #define PRINT_ERR(x...) printk(KERN_ERR DCSS_NAME " error:" x) 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds #define DCSS_LOADSHR 0x00 411da177e4SLinus Torvalds #define DCSS_LOADNSR 0x04 421da177e4SLinus Torvalds #define DCSS_PURGESEG 0x08 431da177e4SLinus Torvalds #define DCSS_FINDSEG 0x0c 441da177e4SLinus Torvalds #define DCSS_LOADNOLY 0x10 451da177e4SLinus Torvalds #define DCSS_SEGEXT 0x18 461da177e4SLinus Torvalds #define DCSS_FINDSEGA 0x0c 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds struct qrange { 491da177e4SLinus Torvalds unsigned int start; // 3byte start address, 1 byte type 501da177e4SLinus Torvalds unsigned int end; // 3byte end address, 1 byte reserved 511da177e4SLinus Torvalds }; 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds struct qout64 { 541da177e4SLinus Torvalds int segstart; 551da177e4SLinus Torvalds int segend; 561da177e4SLinus Torvalds int segcnt; 571da177e4SLinus Torvalds int segrcnt; 581da177e4SLinus Torvalds struct qrange range[6]; 591da177e4SLinus Torvalds }; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds struct qin64 { 621da177e4SLinus Torvalds char qopcode; 631da177e4SLinus Torvalds char rsrv1[3]; 641da177e4SLinus Torvalds char qrcode; 651da177e4SLinus Torvalds char rsrv2[3]; 661da177e4SLinus Torvalds char qname[8]; 671da177e4SLinus Torvalds unsigned int qoutptr; 681da177e4SLinus Torvalds short int qoutlen; 691da177e4SLinus Torvalds }; 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds struct dcss_segment { 721da177e4SLinus Torvalds struct list_head list; 731da177e4SLinus Torvalds char dcss_name[8]; 74444f0e54SGerald Schaefer char res_name[15]; 751da177e4SLinus Torvalds unsigned long start_addr; 761da177e4SLinus Torvalds unsigned long end; 771da177e4SLinus Torvalds atomic_t ref_count; 781da177e4SLinus Torvalds int do_nonshared; 791da177e4SLinus Torvalds unsigned int vm_segtype; 801da177e4SLinus Torvalds struct qrange range[6]; 811da177e4SLinus Torvalds int segcnt; 82444f0e54SGerald Schaefer struct resource *res; 831da177e4SLinus Torvalds }; 841da177e4SLinus Torvalds 8509252e77SHeiko Carstens static DEFINE_MUTEX(dcss_lock); 861da177e4SLinus Torvalds static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list); 871da177e4SLinus Torvalds static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", 881da177e4SLinus Torvalds "EW/EN-MIXED" }; 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds /* 911da177e4SLinus Torvalds * Create the 8 bytes, ebcdic VM segment name from 921da177e4SLinus Torvalds * an ascii name. 931da177e4SLinus Torvalds */ 944d284cacSHeiko Carstens static void 951da177e4SLinus Torvalds dcss_mkname(char *name, char *dcss_name) 961da177e4SLinus Torvalds { 971da177e4SLinus Torvalds int i; 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds for (i = 0; i < 8; i++) { 1001da177e4SLinus Torvalds if (name[i] == '\0') 1011da177e4SLinus Torvalds break; 1021da177e4SLinus Torvalds dcss_name[i] = toupper(name[i]); 1031da177e4SLinus Torvalds }; 1041da177e4SLinus Torvalds for (; i < 8; i++) 1051da177e4SLinus Torvalds dcss_name[i] = ' '; 1061da177e4SLinus Torvalds ASCEBC(dcss_name, 8); 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds /* 1111da177e4SLinus Torvalds * search all segments in dcss_list, and return the one 1121da177e4SLinus Torvalds * namend *name. If not found, return NULL. 1131da177e4SLinus Torvalds */ 1141da177e4SLinus Torvalds static struct dcss_segment * 1151da177e4SLinus Torvalds segment_by_name (char *name) 1161da177e4SLinus Torvalds { 1171da177e4SLinus Torvalds char dcss_name[9]; 1181da177e4SLinus Torvalds struct list_head *l; 1191da177e4SLinus Torvalds struct dcss_segment *tmp, *retval = NULL; 1201da177e4SLinus Torvalds 12109252e77SHeiko Carstens BUG_ON(!mutex_is_locked(&dcss_lock)); 1221da177e4SLinus Torvalds dcss_mkname (name, dcss_name); 1231da177e4SLinus Torvalds list_for_each (l, &dcss_list) { 1241da177e4SLinus Torvalds tmp = list_entry (l, struct dcss_segment, list); 1251da177e4SLinus Torvalds if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) { 1261da177e4SLinus Torvalds retval = tmp; 1271da177e4SLinus Torvalds break; 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds } 1301da177e4SLinus Torvalds return retval; 1311da177e4SLinus Torvalds } 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds /* 1351da177e4SLinus Torvalds * Perform a function on a dcss segment. 1361da177e4SLinus Torvalds */ 1371da177e4SLinus Torvalds static inline int 1381da177e4SLinus Torvalds dcss_diag (__u8 func, void *parameter, 1391da177e4SLinus Torvalds unsigned long *ret1, unsigned long *ret2) 1401da177e4SLinus Torvalds { 1411da177e4SLinus Torvalds unsigned long rx, ry; 1421da177e4SLinus Torvalds int rc; 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds rx = (unsigned long) parameter; 1451da177e4SLinus Torvalds ry = (unsigned long) func; 14694c12cc7SMartin Schwidefsky asm volatile( 147347a8dc3SMartin Schwidefsky #ifdef CONFIG_64BIT 14894c12cc7SMartin Schwidefsky " sam31\n" 1491da177e4SLinus Torvalds " diag %0,%1,0x64\n" 15094c12cc7SMartin Schwidefsky " sam64\n" 1511da177e4SLinus Torvalds #else 1521da177e4SLinus Torvalds " diag %0,%1,0x64\n" 1531da177e4SLinus Torvalds #endif 1541da177e4SLinus Torvalds " ipm %2\n" 1551da177e4SLinus Torvalds " srl %2,28\n" 1561da177e4SLinus Torvalds : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); 1571da177e4SLinus Torvalds *ret1 = rx; 1581da177e4SLinus Torvalds *ret2 = ry; 1591da177e4SLinus Torvalds return rc; 1601da177e4SLinus Torvalds } 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds static inline int 1631da177e4SLinus Torvalds dcss_diag_translate_rc (int vm_rc) { 1641da177e4SLinus Torvalds if (vm_rc == 44) 1651da177e4SLinus Torvalds return -ENOENT; 1661da177e4SLinus Torvalds return -EIO; 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds /* do a diag to get info about a segment. 1711da177e4SLinus Torvalds * fills start_address, end and vm_segtype fields 1721da177e4SLinus Torvalds */ 1731da177e4SLinus Torvalds static int 1741da177e4SLinus Torvalds query_segment_type (struct dcss_segment *seg) 1751da177e4SLinus Torvalds { 1761da177e4SLinus Torvalds struct qin64 *qin = kmalloc (sizeof(struct qin64), GFP_DMA); 1771da177e4SLinus Torvalds struct qout64 *qout = kmalloc (sizeof(struct qout64), GFP_DMA); 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds int diag_cc, rc, i; 1801da177e4SLinus Torvalds unsigned long dummy, vmrc; 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds if ((qin == NULL) || (qout == NULL)) { 1831da177e4SLinus Torvalds rc = -ENOMEM; 1841da177e4SLinus Torvalds goto out_free; 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds /* initialize diag input parameters */ 1881da177e4SLinus Torvalds qin->qopcode = DCSS_FINDSEGA; 1891da177e4SLinus Torvalds qin->qoutptr = (unsigned long) qout; 1901da177e4SLinus Torvalds qin->qoutlen = sizeof(struct qout64); 1911da177e4SLinus Torvalds memcpy (qin->qname, seg->dcss_name, 8); 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds diag_cc = dcss_diag (DCSS_SEGEXT, qin, &dummy, &vmrc); 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds if (diag_cc > 1) { 1969b5dec1aSGerald Schaefer PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc); 1971da177e4SLinus Torvalds rc = dcss_diag_translate_rc (vmrc); 1981da177e4SLinus Torvalds goto out_free; 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds if (qout->segcnt > 6) { 2021da177e4SLinus Torvalds rc = -ENOTSUPP; 2031da177e4SLinus Torvalds goto out_free; 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds if (qout->segcnt == 1) { 2071da177e4SLinus Torvalds seg->vm_segtype = qout->range[0].start & 0xff; 2081da177e4SLinus Torvalds } else { 2091da177e4SLinus Torvalds /* multi-part segment. only one type supported here: 2101da177e4SLinus Torvalds - all parts are contiguous 2111da177e4SLinus Torvalds - all parts are either EW or EN type 2121da177e4SLinus Torvalds - maximum 6 parts allowed */ 2131da177e4SLinus Torvalds unsigned long start = qout->segstart >> PAGE_SHIFT; 2141da177e4SLinus Torvalds for (i=0; i<qout->segcnt; i++) { 2151da177e4SLinus Torvalds if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) && 2161da177e4SLinus Torvalds ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) { 2171da177e4SLinus Torvalds rc = -ENOTSUPP; 2181da177e4SLinus Torvalds goto out_free; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds if (start != qout->range[i].start >> PAGE_SHIFT) { 2211da177e4SLinus Torvalds rc = -ENOTSUPP; 2221da177e4SLinus Torvalds goto out_free; 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds start = (qout->range[i].end >> PAGE_SHIFT) + 1; 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds seg->vm_segtype = SEG_TYPE_EWEN; 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds /* analyze diag output and update seg */ 2301da177e4SLinus Torvalds seg->start_addr = qout->segstart; 2311da177e4SLinus Torvalds seg->end = qout->segend; 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds memcpy (seg->range, qout->range, 6*sizeof(struct qrange)); 2341da177e4SLinus Torvalds seg->segcnt = qout->segcnt; 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds rc = 0; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds out_free: 239b2325fe1SJesper Juhl kfree(qin); 240b2325fe1SJesper Juhl kfree(qout); 2411da177e4SLinus Torvalds return rc; 2421da177e4SLinus Torvalds } 2431da177e4SLinus Torvalds 2441da177e4SLinus Torvalds /* 2451da177e4SLinus Torvalds * get info about a segment 2461da177e4SLinus Torvalds * possible return values: 2471da177e4SLinus Torvalds * -ENOSYS : we are not running on VM 2481da177e4SLinus Torvalds * -EIO : could not perform query diagnose 2491da177e4SLinus Torvalds * -ENOENT : no such segment 2501da177e4SLinus Torvalds * -ENOTSUPP: multi-part segment cannot be used with linux 2511da177e4SLinus Torvalds * -ENOSPC : segment cannot be used (overlaps with storage) 2521da177e4SLinus Torvalds * -ENOMEM : out of memory 2531da177e4SLinus Torvalds * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 2541da177e4SLinus Torvalds */ 2551da177e4SLinus Torvalds int 2561da177e4SLinus Torvalds segment_type (char* name) 2571da177e4SLinus Torvalds { 2581da177e4SLinus Torvalds int rc; 2591da177e4SLinus Torvalds struct dcss_segment seg; 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds if (!MACHINE_IS_VM) 2621da177e4SLinus Torvalds return -ENOSYS; 2631da177e4SLinus Torvalds 2641da177e4SLinus Torvalds dcss_mkname(name, seg.dcss_name); 2651da177e4SLinus Torvalds rc = query_segment_type (&seg); 2661da177e4SLinus Torvalds if (rc < 0) 2671da177e4SLinus Torvalds return rc; 2681da177e4SLinus Torvalds return seg.vm_segtype; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds /* 2721da177e4SLinus Torvalds * real segment loading function, called from segment_load 2731da177e4SLinus Torvalds */ 2741da177e4SLinus Torvalds static int 2751da177e4SLinus Torvalds __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end) 2761da177e4SLinus Torvalds { 2771da177e4SLinus Torvalds struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment), 2781da177e4SLinus Torvalds GFP_DMA); 2791da177e4SLinus Torvalds int dcss_command, rc, diag_cc; 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds if (seg == NULL) { 2821da177e4SLinus Torvalds rc = -ENOMEM; 2831da177e4SLinus Torvalds goto out; 2841da177e4SLinus Torvalds } 2851da177e4SLinus Torvalds dcss_mkname (name, seg->dcss_name); 2861da177e4SLinus Torvalds rc = query_segment_type (seg); 2871da177e4SLinus Torvalds if (rc < 0) 2881da177e4SLinus Torvalds goto out_free; 289f4eb07c1SHeiko Carstens 290f4eb07c1SHeiko Carstens rc = add_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); 291f4eb07c1SHeiko Carstens 292f4eb07c1SHeiko Carstens switch (rc) { 293f4eb07c1SHeiko Carstens case 0: 294f4eb07c1SHeiko Carstens break; 295f4eb07c1SHeiko Carstens case -ENOSPC: 296f4eb07c1SHeiko Carstens PRINT_WARN("segment_load: not loading segment %s - overlaps " 297f4eb07c1SHeiko Carstens "storage/segment\n", name); 298f4eb07c1SHeiko Carstens goto out_free; 299f4eb07c1SHeiko Carstens case -ERANGE: 3001da177e4SLinus Torvalds PRINT_WARN("segment_load: not loading segment %s - exceeds " 3011da177e4SLinus Torvalds "kernel mapping range\n", name); 302f4eb07c1SHeiko Carstens goto out_free; 303f4eb07c1SHeiko Carstens default: 304f4eb07c1SHeiko Carstens PRINT_WARN("segment_load: not loading segment %s (rc: %d)\n", 305f4eb07c1SHeiko Carstens name, rc); 3061da177e4SLinus Torvalds goto out_free; 3071da177e4SLinus Torvalds } 308f4eb07c1SHeiko Carstens 309444f0e54SGerald Schaefer seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL); 310444f0e54SGerald Schaefer if (seg->res == NULL) { 311444f0e54SGerald Schaefer rc = -ENOMEM; 312444f0e54SGerald Schaefer goto out_shared; 313444f0e54SGerald Schaefer } 314444f0e54SGerald Schaefer seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; 315444f0e54SGerald Schaefer seg->res->start = seg->start_addr; 316444f0e54SGerald Schaefer seg->res->end = seg->end; 317444f0e54SGerald Schaefer memcpy(&seg->res_name, seg->dcss_name, 8); 318444f0e54SGerald Schaefer EBCASC(seg->res_name, 8); 319444f0e54SGerald Schaefer seg->res_name[8] = '\0'; 320444f0e54SGerald Schaefer strncat(seg->res_name, " (DCSS)", 7); 321444f0e54SGerald Schaefer seg->res->name = seg->res_name; 322444f0e54SGerald Schaefer rc = seg->vm_segtype; 323444f0e54SGerald Schaefer if (rc == SEG_TYPE_SC || 324444f0e54SGerald Schaefer ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared)) 325444f0e54SGerald Schaefer seg->res->flags |= IORESOURCE_READONLY; 326444f0e54SGerald Schaefer if (request_resource(&iomem_resource, seg->res)) { 327444f0e54SGerald Schaefer rc = -EBUSY; 328444f0e54SGerald Schaefer kfree(seg->res); 329444f0e54SGerald Schaefer goto out_shared; 330444f0e54SGerald Schaefer } 331444f0e54SGerald Schaefer 3321da177e4SLinus Torvalds if (do_nonshared) 3331da177e4SLinus Torvalds dcss_command = DCSS_LOADNSR; 3341da177e4SLinus Torvalds else 3351da177e4SLinus Torvalds dcss_command = DCSS_LOADNOLY; 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds diag_cc = dcss_diag(dcss_command, seg->dcss_name, 3381da177e4SLinus Torvalds &seg->start_addr, &seg->end); 3391da177e4SLinus Torvalds if (diag_cc > 1) { 3401da177e4SLinus Torvalds PRINT_WARN ("segment_load: could not load segment %s - " 3411da177e4SLinus Torvalds "diag returned error (%ld)\n",name,seg->end); 3421da177e4SLinus Torvalds rc = dcss_diag_translate_rc (seg->end); 3431da177e4SLinus Torvalds dcss_diag(DCSS_PURGESEG, seg->dcss_name, 3441da177e4SLinus Torvalds &seg->start_addr, &seg->end); 345444f0e54SGerald Schaefer goto out_resource; 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds seg->do_nonshared = do_nonshared; 3481da177e4SLinus Torvalds atomic_set(&seg->ref_count, 1); 3491da177e4SLinus Torvalds list_add(&seg->list, &dcss_list); 3501da177e4SLinus Torvalds *addr = seg->start_addr; 3511da177e4SLinus Torvalds *end = seg->end; 3521da177e4SLinus Torvalds if (do_nonshared) 3531da177e4SLinus Torvalds PRINT_INFO ("segment_load: loaded segment %s range %p .. %p " 3541da177e4SLinus Torvalds "type %s in non-shared mode\n", name, 3551da177e4SLinus Torvalds (void*)seg->start_addr, (void*)seg->end, 3561da177e4SLinus Torvalds segtype_string[seg->vm_segtype]); 357444f0e54SGerald Schaefer else { 3581da177e4SLinus Torvalds PRINT_INFO ("segment_load: loaded segment %s range %p .. %p " 3591da177e4SLinus Torvalds "type %s in shared mode\n", name, 3601da177e4SLinus Torvalds (void*)seg->start_addr, (void*)seg->end, 3611da177e4SLinus Torvalds segtype_string[seg->vm_segtype]); 362444f0e54SGerald Schaefer } 3631da177e4SLinus Torvalds goto out; 364444f0e54SGerald Schaefer out_resource: 365444f0e54SGerald Schaefer release_resource(seg->res); 366444f0e54SGerald Schaefer kfree(seg->res); 367f4eb07c1SHeiko Carstens out_shared: 368f4eb07c1SHeiko Carstens remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); 3691da177e4SLinus Torvalds out_free: 3701da177e4SLinus Torvalds kfree(seg); 3711da177e4SLinus Torvalds out: 3721da177e4SLinus Torvalds return rc; 3731da177e4SLinus Torvalds } 3741da177e4SLinus Torvalds 3751da177e4SLinus Torvalds /* 3761da177e4SLinus Torvalds * this function loads a DCSS segment 3771da177e4SLinus Torvalds * name : name of the DCSS 3781da177e4SLinus Torvalds * do_nonshared : 0 indicates that the dcss should be shared with other linux images 3791da177e4SLinus Torvalds * 1 indicates that the dcss should be exclusive for this linux image 3801da177e4SLinus Torvalds * addr : will be filled with start address of the segment 3811da177e4SLinus Torvalds * end : will be filled with end address of the segment 3821da177e4SLinus Torvalds * return values: 3831da177e4SLinus Torvalds * -ENOSYS : we are not running on VM 3841da177e4SLinus Torvalds * -EIO : could not perform query or load diagnose 3851da177e4SLinus Torvalds * -ENOENT : no such segment 3861da177e4SLinus Torvalds * -ENOTSUPP: multi-part segment cannot be used with linux 3871da177e4SLinus Torvalds * -ENOSPC : segment cannot be used (overlaps with storage) 3881da177e4SLinus Torvalds * -EBUSY : segment can temporarily not be used (overlaps with dcss) 3891da177e4SLinus Torvalds * -ERANGE : segment cannot be used (exceeds kernel mapping range) 3901da177e4SLinus Torvalds * -EPERM : segment is currently loaded with incompatible permissions 3911da177e4SLinus Torvalds * -ENOMEM : out of memory 3921da177e4SLinus Torvalds * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 3931da177e4SLinus Torvalds */ 3941da177e4SLinus Torvalds int 3951da177e4SLinus Torvalds segment_load (char *name, int do_nonshared, unsigned long *addr, 3961da177e4SLinus Torvalds unsigned long *end) 3971da177e4SLinus Torvalds { 3981da177e4SLinus Torvalds struct dcss_segment *seg; 3991da177e4SLinus Torvalds int rc; 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds if (!MACHINE_IS_VM) 4021da177e4SLinus Torvalds return -ENOSYS; 4031da177e4SLinus Torvalds 40409252e77SHeiko Carstens mutex_lock(&dcss_lock); 4051da177e4SLinus Torvalds seg = segment_by_name (name); 4061da177e4SLinus Torvalds if (seg == NULL) 4071da177e4SLinus Torvalds rc = __segment_load (name, do_nonshared, addr, end); 4081da177e4SLinus Torvalds else { 4091da177e4SLinus Torvalds if (do_nonshared == seg->do_nonshared) { 4101da177e4SLinus Torvalds atomic_inc(&seg->ref_count); 4111da177e4SLinus Torvalds *addr = seg->start_addr; 4121da177e4SLinus Torvalds *end = seg->end; 4131da177e4SLinus Torvalds rc = seg->vm_segtype; 4141da177e4SLinus Torvalds } else { 4151da177e4SLinus Torvalds *addr = *end = 0; 4161da177e4SLinus Torvalds rc = -EPERM; 4171da177e4SLinus Torvalds } 4181da177e4SLinus Torvalds } 41909252e77SHeiko Carstens mutex_unlock(&dcss_lock); 4201da177e4SLinus Torvalds return rc; 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds /* 4241da177e4SLinus Torvalds * this function modifies the shared state of a DCSS segment. note that 4251da177e4SLinus Torvalds * name : name of the DCSS 4261da177e4SLinus Torvalds * do_nonshared : 0 indicates that the dcss should be shared with other linux images 4271da177e4SLinus Torvalds * 1 indicates that the dcss should be exclusive for this linux image 4281da177e4SLinus Torvalds * return values: 4291da177e4SLinus Torvalds * -EIO : could not perform load diagnose (segment gone!) 4301da177e4SLinus Torvalds * -ENOENT : no such segment (segment gone!) 4311da177e4SLinus Torvalds * -EAGAIN : segment is in use by other exploiters, try later 4321da177e4SLinus Torvalds * -EINVAL : no segment with the given name is currently loaded - name invalid 433444f0e54SGerald Schaefer * -EBUSY : segment can temporarily not be used (overlaps with dcss) 4341da177e4SLinus Torvalds * 0 : operation succeeded 4351da177e4SLinus Torvalds */ 4361da177e4SLinus Torvalds int 4371da177e4SLinus Torvalds segment_modify_shared (char *name, int do_nonshared) 4381da177e4SLinus Torvalds { 4391da177e4SLinus Torvalds struct dcss_segment *seg; 4401da177e4SLinus Torvalds unsigned long dummy; 4411da177e4SLinus Torvalds int dcss_command, rc, diag_cc; 4421da177e4SLinus Torvalds 44309252e77SHeiko Carstens mutex_lock(&dcss_lock); 4441da177e4SLinus Torvalds seg = segment_by_name (name); 4451da177e4SLinus Torvalds if (seg == NULL) { 4461da177e4SLinus Torvalds rc = -EINVAL; 4471da177e4SLinus Torvalds goto out_unlock; 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds if (do_nonshared == seg->do_nonshared) { 4501da177e4SLinus Torvalds PRINT_INFO ("segment_modify_shared: not reloading segment %s" 4511da177e4SLinus Torvalds " - already in requested mode\n",name); 4521da177e4SLinus Torvalds rc = 0; 4531da177e4SLinus Torvalds goto out_unlock; 4541da177e4SLinus Torvalds } 4551da177e4SLinus Torvalds if (atomic_read (&seg->ref_count) != 1) { 4561da177e4SLinus Torvalds PRINT_WARN ("segment_modify_shared: not reloading segment %s - " 4571da177e4SLinus Torvalds "segment is in use by other driver(s)\n",name); 4581da177e4SLinus Torvalds rc = -EAGAIN; 4591da177e4SLinus Torvalds goto out_unlock; 4601da177e4SLinus Torvalds } 461444f0e54SGerald Schaefer release_resource(seg->res); 462444f0e54SGerald Schaefer if (do_nonshared) { 4631da177e4SLinus Torvalds dcss_command = DCSS_LOADNSR; 464444f0e54SGerald Schaefer seg->res->flags &= ~IORESOURCE_READONLY; 465444f0e54SGerald Schaefer } else { 4661da177e4SLinus Torvalds dcss_command = DCSS_LOADNOLY; 467444f0e54SGerald Schaefer if (seg->vm_segtype == SEG_TYPE_SR || 468444f0e54SGerald Schaefer seg->vm_segtype == SEG_TYPE_ER) 469444f0e54SGerald Schaefer seg->res->flags |= IORESOURCE_READONLY; 470444f0e54SGerald Schaefer } 471444f0e54SGerald Schaefer if (request_resource(&iomem_resource, seg->res)) { 472444f0e54SGerald Schaefer PRINT_WARN("segment_modify_shared: could not reload segment %s" 473444f0e54SGerald Schaefer " - overlapping resources\n", name); 474444f0e54SGerald Schaefer rc = -EBUSY; 475444f0e54SGerald Schaefer kfree(seg->res); 476444f0e54SGerald Schaefer goto out_del; 477444f0e54SGerald Schaefer } 478444f0e54SGerald Schaefer dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); 4791da177e4SLinus Torvalds diag_cc = dcss_diag(dcss_command, seg->dcss_name, 4801da177e4SLinus Torvalds &seg->start_addr, &seg->end); 4811da177e4SLinus Torvalds if (diag_cc > 1) { 4821da177e4SLinus Torvalds PRINT_WARN ("segment_modify_shared: could not reload segment %s" 4831da177e4SLinus Torvalds " - diag returned error (%ld)\n",name,seg->end); 4841da177e4SLinus Torvalds rc = dcss_diag_translate_rc (seg->end); 4851da177e4SLinus Torvalds goto out_del; 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds seg->do_nonshared = do_nonshared; 4881da177e4SLinus Torvalds rc = 0; 4891da177e4SLinus Torvalds goto out_unlock; 4901da177e4SLinus Torvalds out_del: 491444f0e54SGerald Schaefer remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); 4921da177e4SLinus Torvalds list_del(&seg->list); 493444f0e54SGerald Schaefer dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); 4941da177e4SLinus Torvalds kfree(seg); 4951da177e4SLinus Torvalds out_unlock: 49609252e77SHeiko Carstens mutex_unlock(&dcss_lock); 4971da177e4SLinus Torvalds return rc; 4981da177e4SLinus Torvalds } 4991da177e4SLinus Torvalds 5001da177e4SLinus Torvalds /* 5011da177e4SLinus Torvalds * Decrease the use count of a DCSS segment and remove 5021da177e4SLinus Torvalds * it from the address space if nobody is using it 5031da177e4SLinus Torvalds * any longer. 5041da177e4SLinus Torvalds */ 5051da177e4SLinus Torvalds void 5061da177e4SLinus Torvalds segment_unload(char *name) 5071da177e4SLinus Torvalds { 5081da177e4SLinus Torvalds unsigned long dummy; 5091da177e4SLinus Torvalds struct dcss_segment *seg; 5101da177e4SLinus Torvalds 5111da177e4SLinus Torvalds if (!MACHINE_IS_VM) 5121da177e4SLinus Torvalds return; 5131da177e4SLinus Torvalds 51409252e77SHeiko Carstens mutex_lock(&dcss_lock); 5151da177e4SLinus Torvalds seg = segment_by_name (name); 5161da177e4SLinus Torvalds if (seg == NULL) { 5171da177e4SLinus Torvalds PRINT_ERR ("could not find segment %s in segment_unload, " 5181da177e4SLinus Torvalds "please report to linux390@de.ibm.com\n",name); 5191da177e4SLinus Torvalds goto out_unlock; 5201da177e4SLinus Torvalds } 521f4eb07c1SHeiko Carstens if (atomic_dec_return(&seg->ref_count) != 0) 522f4eb07c1SHeiko Carstens goto out_unlock; 523444f0e54SGerald Schaefer release_resource(seg->res); 524444f0e54SGerald Schaefer kfree(seg->res); 525f4eb07c1SHeiko Carstens remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); 5261da177e4SLinus Torvalds list_del(&seg->list); 527f4eb07c1SHeiko Carstens dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); 5281da177e4SLinus Torvalds kfree(seg); 5291da177e4SLinus Torvalds out_unlock: 53009252e77SHeiko Carstens mutex_unlock(&dcss_lock); 5311da177e4SLinus Torvalds } 5321da177e4SLinus Torvalds 5331da177e4SLinus Torvalds /* 5341da177e4SLinus Torvalds * save segment content permanently 5351da177e4SLinus Torvalds */ 5361da177e4SLinus Torvalds void 5371da177e4SLinus Torvalds segment_save(char *name) 5381da177e4SLinus Torvalds { 5391da177e4SLinus Torvalds struct dcss_segment *seg; 5401da177e4SLinus Torvalds int startpfn = 0; 5411da177e4SLinus Torvalds int endpfn = 0; 5421da177e4SLinus Torvalds char cmd1[160]; 5431da177e4SLinus Torvalds char cmd2[80]; 5449b5dec1aSGerald Schaefer int i, response; 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds if (!MACHINE_IS_VM) 5471da177e4SLinus Torvalds return; 5481da177e4SLinus Torvalds 54909252e77SHeiko Carstens mutex_lock(&dcss_lock); 5501da177e4SLinus Torvalds seg = segment_by_name (name); 5511da177e4SLinus Torvalds 5521da177e4SLinus Torvalds if (seg == NULL) { 5536b4044bdSHeiko Carstens PRINT_ERR("could not find segment %s in segment_save, please " 5546b4044bdSHeiko Carstens "report to linux390@de.ibm.com\n", name); 5556b4044bdSHeiko Carstens goto out; 5561da177e4SLinus Torvalds } 5571da177e4SLinus Torvalds 5581da177e4SLinus Torvalds startpfn = seg->start_addr >> PAGE_SHIFT; 5591da177e4SLinus Torvalds endpfn = (seg->end) >> PAGE_SHIFT; 5601da177e4SLinus Torvalds sprintf(cmd1, "DEFSEG %s", name); 5611da177e4SLinus Torvalds for (i=0; i<seg->segcnt; i++) { 5621da177e4SLinus Torvalds sprintf(cmd1+strlen(cmd1), " %X-%X %s", 5631da177e4SLinus Torvalds seg->range[i].start >> PAGE_SHIFT, 5641da177e4SLinus Torvalds seg->range[i].end >> PAGE_SHIFT, 5651da177e4SLinus Torvalds segtype_string[seg->range[i].start & 0xff]); 5661da177e4SLinus Torvalds } 5671da177e4SLinus Torvalds sprintf(cmd2, "SAVESEG %s", name); 5689b5dec1aSGerald Schaefer response = 0; 5699b5dec1aSGerald Schaefer cpcmd(cmd1, NULL, 0, &response); 5709b5dec1aSGerald Schaefer if (response) { 5719b5dec1aSGerald Schaefer PRINT_ERR("segment_save: DEFSEG failed with response code %i\n", 5729b5dec1aSGerald Schaefer response); 5739b5dec1aSGerald Schaefer goto out; 5749b5dec1aSGerald Schaefer } 5759b5dec1aSGerald Schaefer cpcmd(cmd2, NULL, 0, &response); 5769b5dec1aSGerald Schaefer if (response) { 5779b5dec1aSGerald Schaefer PRINT_ERR("segment_save: SAVESEG failed with response code %i\n", 5789b5dec1aSGerald Schaefer response); 5799b5dec1aSGerald Schaefer goto out; 5809b5dec1aSGerald Schaefer } 5819b5dec1aSGerald Schaefer out: 58209252e77SHeiko Carstens mutex_unlock(&dcss_lock); 5831da177e4SLinus Torvalds } 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds EXPORT_SYMBOL(segment_load); 5861da177e4SLinus Torvalds EXPORT_SYMBOL(segment_unload); 5871da177e4SLinus Torvalds EXPORT_SYMBOL(segment_save); 5881da177e4SLinus Torvalds EXPORT_SYMBOL(segment_type); 5891da177e4SLinus Torvalds EXPORT_SYMBOL(segment_modify_shared); 590