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> 171da177e4SLinus Torvalds #include <asm/page.h> 181da177e4SLinus Torvalds #include <asm/ebcdic.h> 191da177e4SLinus Torvalds #include <asm/errno.h> 201da177e4SLinus Torvalds #include <asm/extmem.h> 211da177e4SLinus Torvalds #include <asm/cpcmd.h> 221da177e4SLinus Torvalds #include <linux/ctype.h> 231da177e4SLinus Torvalds 241da177e4SLinus Torvalds #define DCSS_DEBUG /* Debug messages on/off */ 251da177e4SLinus Torvalds 261da177e4SLinus Torvalds #define DCSS_NAME "extmem" 271da177e4SLinus Torvalds #ifdef DCSS_DEBUG 281da177e4SLinus Torvalds #define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSS_NAME " debug:" x) 291da177e4SLinus Torvalds #else 301da177e4SLinus Torvalds #define PRINT_DEBUG(x...) do {} while (0) 311da177e4SLinus Torvalds #endif 321da177e4SLinus Torvalds #define PRINT_INFO(x...) printk(KERN_INFO DCSS_NAME " info:" x) 331da177e4SLinus Torvalds #define PRINT_WARN(x...) printk(KERN_WARNING DCSS_NAME " warning:" x) 341da177e4SLinus Torvalds #define PRINT_ERR(x...) printk(KERN_ERR DCSS_NAME " error:" x) 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds #define DCSS_LOADSHR 0x00 381da177e4SLinus Torvalds #define DCSS_LOADNSR 0x04 391da177e4SLinus Torvalds #define DCSS_PURGESEG 0x08 401da177e4SLinus Torvalds #define DCSS_FINDSEG 0x0c 411da177e4SLinus Torvalds #define DCSS_LOADNOLY 0x10 421da177e4SLinus Torvalds #define DCSS_SEGEXT 0x18 431da177e4SLinus Torvalds #define DCSS_FINDSEGA 0x0c 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds struct qrange { 461da177e4SLinus Torvalds unsigned int start; // 3byte start address, 1 byte type 471da177e4SLinus Torvalds unsigned int end; // 3byte end address, 1 byte reserved 481da177e4SLinus Torvalds }; 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds struct qout64 { 511da177e4SLinus Torvalds int segstart; 521da177e4SLinus Torvalds int segend; 531da177e4SLinus Torvalds int segcnt; 541da177e4SLinus Torvalds int segrcnt; 551da177e4SLinus Torvalds struct qrange range[6]; 561da177e4SLinus Torvalds }; 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds struct qin64 { 591da177e4SLinus Torvalds char qopcode; 601da177e4SLinus Torvalds char rsrv1[3]; 611da177e4SLinus Torvalds char qrcode; 621da177e4SLinus Torvalds char rsrv2[3]; 631da177e4SLinus Torvalds char qname[8]; 641da177e4SLinus Torvalds unsigned int qoutptr; 651da177e4SLinus Torvalds short int qoutlen; 661da177e4SLinus Torvalds }; 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds struct dcss_segment { 691da177e4SLinus Torvalds struct list_head list; 701da177e4SLinus Torvalds char dcss_name[8]; 711da177e4SLinus Torvalds unsigned long start_addr; 721da177e4SLinus Torvalds unsigned long end; 731da177e4SLinus Torvalds atomic_t ref_count; 741da177e4SLinus Torvalds int do_nonshared; 751da177e4SLinus Torvalds unsigned int vm_segtype; 761da177e4SLinus Torvalds struct qrange range[6]; 771da177e4SLinus Torvalds int segcnt; 781da177e4SLinus Torvalds }; 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds static DEFINE_SPINLOCK(dcss_lock); 811da177e4SLinus Torvalds static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list); 821da177e4SLinus Torvalds static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", 831da177e4SLinus Torvalds "EW/EN-MIXED" }; 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds extern struct { 861da177e4SLinus Torvalds unsigned long addr, size, type; 871da177e4SLinus Torvalds } memory_chunk[MEMORY_CHUNKS]; 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds /* 901da177e4SLinus Torvalds * Create the 8 bytes, ebcdic VM segment name from 911da177e4SLinus Torvalds * an ascii name. 921da177e4SLinus Torvalds */ 931da177e4SLinus Torvalds static void inline 941da177e4SLinus Torvalds dcss_mkname(char *name, char *dcss_name) 951da177e4SLinus Torvalds { 961da177e4SLinus Torvalds int i; 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds for (i = 0; i < 8; i++) { 991da177e4SLinus Torvalds if (name[i] == '\0') 1001da177e4SLinus Torvalds break; 1011da177e4SLinus Torvalds dcss_name[i] = toupper(name[i]); 1021da177e4SLinus Torvalds }; 1031da177e4SLinus Torvalds for (; i < 8; i++) 1041da177e4SLinus Torvalds dcss_name[i] = ' '; 1051da177e4SLinus Torvalds ASCEBC(dcss_name, 8); 1061da177e4SLinus Torvalds } 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds /* 1101da177e4SLinus Torvalds * search all segments in dcss_list, and return the one 1111da177e4SLinus Torvalds * namend *name. If not found, return NULL. 1121da177e4SLinus Torvalds */ 1131da177e4SLinus Torvalds static struct dcss_segment * 1141da177e4SLinus Torvalds segment_by_name (char *name) 1151da177e4SLinus Torvalds { 1161da177e4SLinus Torvalds char dcss_name[9]; 1171da177e4SLinus Torvalds struct list_head *l; 1181da177e4SLinus Torvalds struct dcss_segment *tmp, *retval = NULL; 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds assert_spin_locked(&dcss_lock); 1211da177e4SLinus Torvalds dcss_mkname (name, dcss_name); 1221da177e4SLinus Torvalds list_for_each (l, &dcss_list) { 1231da177e4SLinus Torvalds tmp = list_entry (l, struct dcss_segment, list); 1241da177e4SLinus Torvalds if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) { 1251da177e4SLinus Torvalds retval = tmp; 1261da177e4SLinus Torvalds break; 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds return retval; 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds /* 1341da177e4SLinus Torvalds * Perform a function on a dcss segment. 1351da177e4SLinus Torvalds */ 1361da177e4SLinus Torvalds static inline int 1371da177e4SLinus Torvalds dcss_diag (__u8 func, void *parameter, 1381da177e4SLinus Torvalds unsigned long *ret1, unsigned long *ret2) 1391da177e4SLinus Torvalds { 1401da177e4SLinus Torvalds unsigned long rx, ry; 1411da177e4SLinus Torvalds int rc; 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds rx = (unsigned long) parameter; 1441da177e4SLinus Torvalds ry = (unsigned long) func; 1451da177e4SLinus Torvalds __asm__ __volatile__( 146347a8dc3SMartin Schwidefsky #ifdef CONFIG_64BIT 1471da177e4SLinus Torvalds " sam31\n" // switch to 31 bit 1481da177e4SLinus Torvalds " diag %0,%1,0x64\n" 1491da177e4SLinus Torvalds " sam64\n" // switch back to 64 bit 1501da177e4SLinus Torvalds #else 1511da177e4SLinus Torvalds " diag %0,%1,0x64\n" 1521da177e4SLinus Torvalds #endif 1531da177e4SLinus Torvalds " ipm %2\n" 1541da177e4SLinus Torvalds " srl %2,28\n" 1551da177e4SLinus Torvalds : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" ); 1561da177e4SLinus Torvalds *ret1 = rx; 1571da177e4SLinus Torvalds *ret2 = ry; 1581da177e4SLinus Torvalds return rc; 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds static inline int 1621da177e4SLinus Torvalds dcss_diag_translate_rc (int vm_rc) { 1631da177e4SLinus Torvalds if (vm_rc == 44) 1641da177e4SLinus Torvalds return -ENOENT; 1651da177e4SLinus Torvalds return -EIO; 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds /* do a diag to get info about a segment. 1701da177e4SLinus Torvalds * fills start_address, end and vm_segtype fields 1711da177e4SLinus Torvalds */ 1721da177e4SLinus Torvalds static int 1731da177e4SLinus Torvalds query_segment_type (struct dcss_segment *seg) 1741da177e4SLinus Torvalds { 1751da177e4SLinus Torvalds struct qin64 *qin = kmalloc (sizeof(struct qin64), GFP_DMA); 1761da177e4SLinus Torvalds struct qout64 *qout = kmalloc (sizeof(struct qout64), GFP_DMA); 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds int diag_cc, rc, i; 1791da177e4SLinus Torvalds unsigned long dummy, vmrc; 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds if ((qin == NULL) || (qout == NULL)) { 1821da177e4SLinus Torvalds rc = -ENOMEM; 1831da177e4SLinus Torvalds goto out_free; 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds /* initialize diag input parameters */ 1871da177e4SLinus Torvalds qin->qopcode = DCSS_FINDSEGA; 1881da177e4SLinus Torvalds qin->qoutptr = (unsigned long) qout; 1891da177e4SLinus Torvalds qin->qoutlen = sizeof(struct qout64); 1901da177e4SLinus Torvalds memcpy (qin->qname, seg->dcss_name, 8); 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds diag_cc = dcss_diag (DCSS_SEGEXT, qin, &dummy, &vmrc); 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds if (diag_cc > 1) { 1959b5dec1aSGerald Schaefer PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc); 1961da177e4SLinus Torvalds rc = dcss_diag_translate_rc (vmrc); 1971da177e4SLinus Torvalds goto out_free; 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds if (qout->segcnt > 6) { 2011da177e4SLinus Torvalds rc = -ENOTSUPP; 2021da177e4SLinus Torvalds goto out_free; 2031da177e4SLinus Torvalds } 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds if (qout->segcnt == 1) { 2061da177e4SLinus Torvalds seg->vm_segtype = qout->range[0].start & 0xff; 2071da177e4SLinus Torvalds } else { 2081da177e4SLinus Torvalds /* multi-part segment. only one type supported here: 2091da177e4SLinus Torvalds - all parts are contiguous 2101da177e4SLinus Torvalds - all parts are either EW or EN type 2111da177e4SLinus Torvalds - maximum 6 parts allowed */ 2121da177e4SLinus Torvalds unsigned long start = qout->segstart >> PAGE_SHIFT; 2131da177e4SLinus Torvalds for (i=0; i<qout->segcnt; i++) { 2141da177e4SLinus Torvalds if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) && 2151da177e4SLinus Torvalds ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) { 2161da177e4SLinus Torvalds rc = -ENOTSUPP; 2171da177e4SLinus Torvalds goto out_free; 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds if (start != qout->range[i].start >> PAGE_SHIFT) { 2201da177e4SLinus Torvalds rc = -ENOTSUPP; 2211da177e4SLinus Torvalds goto out_free; 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds start = (qout->range[i].end >> PAGE_SHIFT) + 1; 2241da177e4SLinus Torvalds } 2251da177e4SLinus Torvalds seg->vm_segtype = SEG_TYPE_EWEN; 2261da177e4SLinus Torvalds } 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds /* analyze diag output and update seg */ 2291da177e4SLinus Torvalds seg->start_addr = qout->segstart; 2301da177e4SLinus Torvalds seg->end = qout->segend; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds memcpy (seg->range, qout->range, 6*sizeof(struct qrange)); 2331da177e4SLinus Torvalds seg->segcnt = qout->segcnt; 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds rc = 0; 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds out_free: 238b2325fe1SJesper Juhl kfree(qin); 239b2325fe1SJesper Juhl kfree(qout); 2401da177e4SLinus Torvalds return rc; 2411da177e4SLinus Torvalds } 2421da177e4SLinus Torvalds 2431da177e4SLinus Torvalds /* 2441da177e4SLinus Torvalds * check if the given segment collides with guest storage. 2451da177e4SLinus Torvalds * returns 1 if this is the case, 0 if no collision was found 2461da177e4SLinus Torvalds */ 2471da177e4SLinus Torvalds static int 2481da177e4SLinus Torvalds segment_overlaps_storage(struct dcss_segment *seg) 2491da177e4SLinus Torvalds { 2501da177e4SLinus Torvalds int i; 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds for (i=0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { 2531da177e4SLinus Torvalds if (memory_chunk[i].type != 0) 2541da177e4SLinus Torvalds continue; 2551da177e4SLinus Torvalds if ((memory_chunk[i].addr >> 20) > (seg->end >> 20)) 2561da177e4SLinus Torvalds continue; 2571da177e4SLinus Torvalds if (((memory_chunk[i].addr + memory_chunk[i].size - 1) >> 20) 2581da177e4SLinus Torvalds < (seg->start_addr >> 20)) 2591da177e4SLinus Torvalds continue; 2601da177e4SLinus Torvalds return 1; 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds return 0; 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds /* 2661da177e4SLinus Torvalds * check if segment collides with other segments that are currently loaded 2671da177e4SLinus Torvalds * returns 1 if this is the case, 0 if no collision was found 2681da177e4SLinus Torvalds */ 2691da177e4SLinus Torvalds static int 2701da177e4SLinus Torvalds segment_overlaps_others (struct dcss_segment *seg) 2711da177e4SLinus Torvalds { 2721da177e4SLinus Torvalds struct list_head *l; 2731da177e4SLinus Torvalds struct dcss_segment *tmp; 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds assert_spin_locked(&dcss_lock); 2761da177e4SLinus Torvalds list_for_each(l, &dcss_list) { 2771da177e4SLinus Torvalds tmp = list_entry(l, struct dcss_segment, list); 2781da177e4SLinus Torvalds if ((tmp->start_addr >> 20) > (seg->end >> 20)) 2791da177e4SLinus Torvalds continue; 2801da177e4SLinus Torvalds if ((tmp->end >> 20) < (seg->start_addr >> 20)) 2811da177e4SLinus Torvalds continue; 2821da177e4SLinus Torvalds if (seg == tmp) 2831da177e4SLinus Torvalds continue; 2841da177e4SLinus Torvalds return 1; 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds return 0; 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds /* 2901da177e4SLinus Torvalds * check if segment exceeds the kernel mapping range (detected or set via mem=) 2911da177e4SLinus Torvalds * returns 1 if this is the case, 0 if segment fits into the range 2921da177e4SLinus Torvalds */ 2931da177e4SLinus Torvalds static inline int 2941da177e4SLinus Torvalds segment_exceeds_range (struct dcss_segment *seg) 2951da177e4SLinus Torvalds { 2961da177e4SLinus Torvalds int seg_last_pfn = (seg->end) >> PAGE_SHIFT; 2971da177e4SLinus Torvalds if (seg_last_pfn > max_pfn) 2981da177e4SLinus Torvalds return 1; 2991da177e4SLinus Torvalds return 0; 3001da177e4SLinus Torvalds } 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds /* 3031da177e4SLinus Torvalds * get info about a segment 3041da177e4SLinus Torvalds * possible return values: 3051da177e4SLinus Torvalds * -ENOSYS : we are not running on VM 3061da177e4SLinus Torvalds * -EIO : could not perform query diagnose 3071da177e4SLinus Torvalds * -ENOENT : no such segment 3081da177e4SLinus Torvalds * -ENOTSUPP: multi-part segment cannot be used with linux 3091da177e4SLinus Torvalds * -ENOSPC : segment cannot be used (overlaps with storage) 3101da177e4SLinus Torvalds * -ENOMEM : out of memory 3111da177e4SLinus Torvalds * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 3121da177e4SLinus Torvalds */ 3131da177e4SLinus Torvalds int 3141da177e4SLinus Torvalds segment_type (char* name) 3151da177e4SLinus Torvalds { 3161da177e4SLinus Torvalds int rc; 3171da177e4SLinus Torvalds struct dcss_segment seg; 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvalds if (!MACHINE_IS_VM) 3201da177e4SLinus Torvalds return -ENOSYS; 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds dcss_mkname(name, seg.dcss_name); 3231da177e4SLinus Torvalds rc = query_segment_type (&seg); 3241da177e4SLinus Torvalds if (rc < 0) 3251da177e4SLinus Torvalds return rc; 3261da177e4SLinus Torvalds return seg.vm_segtype; 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds /* 3301da177e4SLinus Torvalds * real segment loading function, called from segment_load 3311da177e4SLinus Torvalds */ 3321da177e4SLinus Torvalds static int 3331da177e4SLinus Torvalds __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end) 3341da177e4SLinus Torvalds { 3351da177e4SLinus Torvalds struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment), 3361da177e4SLinus Torvalds GFP_DMA); 3371da177e4SLinus Torvalds int dcss_command, rc, diag_cc; 3381da177e4SLinus Torvalds 3391da177e4SLinus Torvalds if (seg == NULL) { 3401da177e4SLinus Torvalds rc = -ENOMEM; 3411da177e4SLinus Torvalds goto out; 3421da177e4SLinus Torvalds } 3431da177e4SLinus Torvalds dcss_mkname (name, seg->dcss_name); 3441da177e4SLinus Torvalds rc = query_segment_type (seg); 3451da177e4SLinus Torvalds if (rc < 0) 3461da177e4SLinus Torvalds goto out_free; 3471da177e4SLinus Torvalds if (segment_exceeds_range(seg)) { 3481da177e4SLinus Torvalds PRINT_WARN ("segment_load: not loading segment %s - exceeds" 3491da177e4SLinus Torvalds " kernel mapping range\n",name); 3501da177e4SLinus Torvalds rc = -ERANGE; 3511da177e4SLinus Torvalds goto out_free; 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds if (segment_overlaps_storage(seg)) { 3541da177e4SLinus Torvalds PRINT_WARN ("segment_load: not loading segment %s - overlaps" 3551da177e4SLinus Torvalds " storage\n",name); 3561da177e4SLinus Torvalds rc = -ENOSPC; 3571da177e4SLinus Torvalds goto out_free; 3581da177e4SLinus Torvalds } 3591da177e4SLinus Torvalds if (segment_overlaps_others(seg)) { 3601da177e4SLinus Torvalds PRINT_WARN ("segment_load: not loading segment %s - overlaps" 3611da177e4SLinus Torvalds " other segments\n",name); 3621da177e4SLinus Torvalds rc = -EBUSY; 3631da177e4SLinus Torvalds goto out_free; 3641da177e4SLinus Torvalds } 3651da177e4SLinus Torvalds if (do_nonshared) 3661da177e4SLinus Torvalds dcss_command = DCSS_LOADNSR; 3671da177e4SLinus Torvalds else 3681da177e4SLinus Torvalds dcss_command = DCSS_LOADNOLY; 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds diag_cc = dcss_diag(dcss_command, seg->dcss_name, 3711da177e4SLinus Torvalds &seg->start_addr, &seg->end); 3721da177e4SLinus Torvalds if (diag_cc > 1) { 3731da177e4SLinus Torvalds PRINT_WARN ("segment_load: could not load segment %s - " 3741da177e4SLinus Torvalds "diag returned error (%ld)\n",name,seg->end); 3751da177e4SLinus Torvalds rc = dcss_diag_translate_rc (seg->end); 3761da177e4SLinus Torvalds dcss_diag(DCSS_PURGESEG, seg->dcss_name, 3771da177e4SLinus Torvalds &seg->start_addr, &seg->end); 3781da177e4SLinus Torvalds goto out_free; 3791da177e4SLinus Torvalds } 3801da177e4SLinus Torvalds seg->do_nonshared = do_nonshared; 3811da177e4SLinus Torvalds atomic_set(&seg->ref_count, 1); 3821da177e4SLinus Torvalds list_add(&seg->list, &dcss_list); 3831da177e4SLinus Torvalds rc = seg->vm_segtype; 3841da177e4SLinus Torvalds *addr = seg->start_addr; 3851da177e4SLinus Torvalds *end = seg->end; 3861da177e4SLinus Torvalds if (do_nonshared) 3871da177e4SLinus Torvalds PRINT_INFO ("segment_load: loaded segment %s range %p .. %p " 3881da177e4SLinus Torvalds "type %s in non-shared mode\n", name, 3891da177e4SLinus Torvalds (void*)seg->start_addr, (void*)seg->end, 3901da177e4SLinus Torvalds segtype_string[seg->vm_segtype]); 3911da177e4SLinus Torvalds else 3921da177e4SLinus Torvalds PRINT_INFO ("segment_load: loaded segment %s range %p .. %p " 3931da177e4SLinus Torvalds "type %s in shared mode\n", name, 3941da177e4SLinus Torvalds (void*)seg->start_addr, (void*)seg->end, 3951da177e4SLinus Torvalds segtype_string[seg->vm_segtype]); 3961da177e4SLinus Torvalds goto out; 3971da177e4SLinus Torvalds out_free: 3981da177e4SLinus Torvalds kfree(seg); 3991da177e4SLinus Torvalds out: 4001da177e4SLinus Torvalds return rc; 4011da177e4SLinus Torvalds } 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds /* 4041da177e4SLinus Torvalds * this function loads a DCSS segment 4051da177e4SLinus Torvalds * name : name of the DCSS 4061da177e4SLinus Torvalds * do_nonshared : 0 indicates that the dcss should be shared with other linux images 4071da177e4SLinus Torvalds * 1 indicates that the dcss should be exclusive for this linux image 4081da177e4SLinus Torvalds * addr : will be filled with start address of the segment 4091da177e4SLinus Torvalds * end : will be filled with end address of the segment 4101da177e4SLinus Torvalds * return values: 4111da177e4SLinus Torvalds * -ENOSYS : we are not running on VM 4121da177e4SLinus Torvalds * -EIO : could not perform query or load diagnose 4131da177e4SLinus Torvalds * -ENOENT : no such segment 4141da177e4SLinus Torvalds * -ENOTSUPP: multi-part segment cannot be used with linux 4151da177e4SLinus Torvalds * -ENOSPC : segment cannot be used (overlaps with storage) 4161da177e4SLinus Torvalds * -EBUSY : segment can temporarily not be used (overlaps with dcss) 4171da177e4SLinus Torvalds * -ERANGE : segment cannot be used (exceeds kernel mapping range) 4181da177e4SLinus Torvalds * -EPERM : segment is currently loaded with incompatible permissions 4191da177e4SLinus Torvalds * -ENOMEM : out of memory 4201da177e4SLinus Torvalds * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 4211da177e4SLinus Torvalds */ 4221da177e4SLinus Torvalds int 4231da177e4SLinus Torvalds segment_load (char *name, int do_nonshared, unsigned long *addr, 4241da177e4SLinus Torvalds unsigned long *end) 4251da177e4SLinus Torvalds { 4261da177e4SLinus Torvalds struct dcss_segment *seg; 4271da177e4SLinus Torvalds int rc; 4281da177e4SLinus Torvalds 4291da177e4SLinus Torvalds if (!MACHINE_IS_VM) 4301da177e4SLinus Torvalds return -ENOSYS; 4311da177e4SLinus Torvalds 4321da177e4SLinus Torvalds spin_lock (&dcss_lock); 4331da177e4SLinus Torvalds seg = segment_by_name (name); 4341da177e4SLinus Torvalds if (seg == NULL) 4351da177e4SLinus Torvalds rc = __segment_load (name, do_nonshared, addr, end); 4361da177e4SLinus Torvalds else { 4371da177e4SLinus Torvalds if (do_nonshared == seg->do_nonshared) { 4381da177e4SLinus Torvalds atomic_inc(&seg->ref_count); 4391da177e4SLinus Torvalds *addr = seg->start_addr; 4401da177e4SLinus Torvalds *end = seg->end; 4411da177e4SLinus Torvalds rc = seg->vm_segtype; 4421da177e4SLinus Torvalds } else { 4431da177e4SLinus Torvalds *addr = *end = 0; 4441da177e4SLinus Torvalds rc = -EPERM; 4451da177e4SLinus Torvalds } 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds spin_unlock (&dcss_lock); 4481da177e4SLinus Torvalds return rc; 4491da177e4SLinus Torvalds } 4501da177e4SLinus Torvalds 4511da177e4SLinus Torvalds /* 4521da177e4SLinus Torvalds * this function modifies the shared state of a DCSS segment. note that 4531da177e4SLinus Torvalds * name : name of the DCSS 4541da177e4SLinus Torvalds * do_nonshared : 0 indicates that the dcss should be shared with other linux images 4551da177e4SLinus Torvalds * 1 indicates that the dcss should be exclusive for this linux image 4561da177e4SLinus Torvalds * return values: 4571da177e4SLinus Torvalds * -EIO : could not perform load diagnose (segment gone!) 4581da177e4SLinus Torvalds * -ENOENT : no such segment (segment gone!) 4591da177e4SLinus Torvalds * -EAGAIN : segment is in use by other exploiters, try later 4601da177e4SLinus Torvalds * -EINVAL : no segment with the given name is currently loaded - name invalid 4611da177e4SLinus Torvalds * 0 : operation succeeded 4621da177e4SLinus Torvalds */ 4631da177e4SLinus Torvalds int 4641da177e4SLinus Torvalds segment_modify_shared (char *name, int do_nonshared) 4651da177e4SLinus Torvalds { 4661da177e4SLinus Torvalds struct dcss_segment *seg; 4671da177e4SLinus Torvalds unsigned long dummy; 4681da177e4SLinus Torvalds int dcss_command, rc, diag_cc; 4691da177e4SLinus Torvalds 4701da177e4SLinus Torvalds spin_lock (&dcss_lock); 4711da177e4SLinus Torvalds seg = segment_by_name (name); 4721da177e4SLinus Torvalds if (seg == NULL) { 4731da177e4SLinus Torvalds rc = -EINVAL; 4741da177e4SLinus Torvalds goto out_unlock; 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds if (do_nonshared == seg->do_nonshared) { 4771da177e4SLinus Torvalds PRINT_INFO ("segment_modify_shared: not reloading segment %s" 4781da177e4SLinus Torvalds " - already in requested mode\n",name); 4791da177e4SLinus Torvalds rc = 0; 4801da177e4SLinus Torvalds goto out_unlock; 4811da177e4SLinus Torvalds } 4821da177e4SLinus Torvalds if (atomic_read (&seg->ref_count) != 1) { 4831da177e4SLinus Torvalds PRINT_WARN ("segment_modify_shared: not reloading segment %s - " 4841da177e4SLinus Torvalds "segment is in use by other driver(s)\n",name); 4851da177e4SLinus Torvalds rc = -EAGAIN; 4861da177e4SLinus Torvalds goto out_unlock; 4871da177e4SLinus Torvalds } 4881da177e4SLinus Torvalds dcss_diag(DCSS_PURGESEG, seg->dcss_name, 4891da177e4SLinus Torvalds &dummy, &dummy); 4901da177e4SLinus Torvalds if (do_nonshared) 4911da177e4SLinus Torvalds dcss_command = DCSS_LOADNSR; 4921da177e4SLinus Torvalds else 4931da177e4SLinus Torvalds dcss_command = DCSS_LOADNOLY; 4941da177e4SLinus Torvalds diag_cc = dcss_diag(dcss_command, seg->dcss_name, 4951da177e4SLinus Torvalds &seg->start_addr, &seg->end); 4961da177e4SLinus Torvalds if (diag_cc > 1) { 4971da177e4SLinus Torvalds PRINT_WARN ("segment_modify_shared: could not reload segment %s" 4981da177e4SLinus Torvalds " - diag returned error (%ld)\n",name,seg->end); 4991da177e4SLinus Torvalds rc = dcss_diag_translate_rc (seg->end); 5001da177e4SLinus Torvalds goto out_del; 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds seg->do_nonshared = do_nonshared; 5031da177e4SLinus Torvalds rc = 0; 5041da177e4SLinus Torvalds goto out_unlock; 5051da177e4SLinus Torvalds out_del: 5061da177e4SLinus Torvalds list_del(&seg->list); 5071da177e4SLinus Torvalds dcss_diag(DCSS_PURGESEG, seg->dcss_name, 5081da177e4SLinus Torvalds &dummy, &dummy); 5091da177e4SLinus Torvalds kfree(seg); 5101da177e4SLinus Torvalds out_unlock: 5111da177e4SLinus Torvalds spin_unlock(&dcss_lock); 5121da177e4SLinus Torvalds return rc; 5131da177e4SLinus Torvalds } 5141da177e4SLinus Torvalds 5151da177e4SLinus Torvalds /* 5161da177e4SLinus Torvalds * Decrease the use count of a DCSS segment and remove 5171da177e4SLinus Torvalds * it from the address space if nobody is using it 5181da177e4SLinus Torvalds * any longer. 5191da177e4SLinus Torvalds */ 5201da177e4SLinus Torvalds void 5211da177e4SLinus Torvalds segment_unload(char *name) 5221da177e4SLinus Torvalds { 5231da177e4SLinus Torvalds unsigned long dummy; 5241da177e4SLinus Torvalds struct dcss_segment *seg; 5251da177e4SLinus Torvalds 5261da177e4SLinus Torvalds if (!MACHINE_IS_VM) 5271da177e4SLinus Torvalds return; 5281da177e4SLinus Torvalds 5291da177e4SLinus Torvalds spin_lock(&dcss_lock); 5301da177e4SLinus Torvalds seg = segment_by_name (name); 5311da177e4SLinus Torvalds if (seg == NULL) { 5321da177e4SLinus Torvalds PRINT_ERR ("could not find segment %s in segment_unload, " 5331da177e4SLinus Torvalds "please report to linux390@de.ibm.com\n",name); 5341da177e4SLinus Torvalds goto out_unlock; 5351da177e4SLinus Torvalds } 5361da177e4SLinus Torvalds if (atomic_dec_return(&seg->ref_count) == 0) { 5371da177e4SLinus Torvalds list_del(&seg->list); 5381da177e4SLinus Torvalds dcss_diag(DCSS_PURGESEG, seg->dcss_name, 5391da177e4SLinus Torvalds &dummy, &dummy); 5401da177e4SLinus Torvalds kfree(seg); 5411da177e4SLinus Torvalds } 5421da177e4SLinus Torvalds out_unlock: 5431da177e4SLinus Torvalds spin_unlock(&dcss_lock); 5441da177e4SLinus Torvalds } 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds /* 5471da177e4SLinus Torvalds * save segment content permanently 5481da177e4SLinus Torvalds */ 5491da177e4SLinus Torvalds void 5501da177e4SLinus Torvalds segment_save(char *name) 5511da177e4SLinus Torvalds { 5521da177e4SLinus Torvalds struct dcss_segment *seg; 5531da177e4SLinus Torvalds int startpfn = 0; 5541da177e4SLinus Torvalds int endpfn = 0; 5551da177e4SLinus Torvalds char cmd1[160]; 5561da177e4SLinus Torvalds char cmd2[80]; 5579b5dec1aSGerald Schaefer int i, response; 5581da177e4SLinus Torvalds 5591da177e4SLinus Torvalds if (!MACHINE_IS_VM) 5601da177e4SLinus Torvalds return; 5611da177e4SLinus Torvalds 5621da177e4SLinus Torvalds spin_lock(&dcss_lock); 5631da177e4SLinus Torvalds seg = segment_by_name (name); 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds if (seg == NULL) { 5661da177e4SLinus Torvalds PRINT_ERR ("could not find segment %s in segment_save, please report to linux390@de.ibm.com\n",name); 5671da177e4SLinus Torvalds return; 5681da177e4SLinus Torvalds } 5691da177e4SLinus Torvalds 5701da177e4SLinus Torvalds startpfn = seg->start_addr >> PAGE_SHIFT; 5711da177e4SLinus Torvalds endpfn = (seg->end) >> PAGE_SHIFT; 5721da177e4SLinus Torvalds sprintf(cmd1, "DEFSEG %s", name); 5731da177e4SLinus Torvalds for (i=0; i<seg->segcnt; i++) { 5741da177e4SLinus Torvalds sprintf(cmd1+strlen(cmd1), " %X-%X %s", 5751da177e4SLinus Torvalds seg->range[i].start >> PAGE_SHIFT, 5761da177e4SLinus Torvalds seg->range[i].end >> PAGE_SHIFT, 5771da177e4SLinus Torvalds segtype_string[seg->range[i].start & 0xff]); 5781da177e4SLinus Torvalds } 5791da177e4SLinus Torvalds sprintf(cmd2, "SAVESEG %s", name); 5809b5dec1aSGerald Schaefer response = 0; 5819b5dec1aSGerald Schaefer cpcmd(cmd1, NULL, 0, &response); 5829b5dec1aSGerald Schaefer if (response) { 5839b5dec1aSGerald Schaefer PRINT_ERR("segment_save: DEFSEG failed with response code %i\n", 5849b5dec1aSGerald Schaefer response); 5859b5dec1aSGerald Schaefer goto out; 5869b5dec1aSGerald Schaefer } 5879b5dec1aSGerald Schaefer cpcmd(cmd2, NULL, 0, &response); 5889b5dec1aSGerald Schaefer if (response) { 5899b5dec1aSGerald Schaefer PRINT_ERR("segment_save: SAVESEG failed with response code %i\n", 5909b5dec1aSGerald Schaefer response); 5919b5dec1aSGerald Schaefer goto out; 5929b5dec1aSGerald Schaefer } 5939b5dec1aSGerald Schaefer out: 5941da177e4SLinus Torvalds spin_unlock(&dcss_lock); 5951da177e4SLinus Torvalds } 5961da177e4SLinus Torvalds 5971da177e4SLinus Torvalds EXPORT_SYMBOL(segment_load); 5981da177e4SLinus Torvalds EXPORT_SYMBOL(segment_unload); 5991da177e4SLinus Torvalds EXPORT_SYMBOL(segment_save); 6001da177e4SLinus Torvalds EXPORT_SYMBOL(segment_type); 6011da177e4SLinus Torvalds EXPORT_SYMBOL(segment_modify_shared); 602