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__( 1461da177e4SLinus Torvalds #ifdef CONFIG_ARCH_S390X 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) { 1951da177e4SLinus Torvalds rc = dcss_diag_translate_rc (vmrc); 1961da177e4SLinus Torvalds goto out_free; 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds if (qout->segcnt > 6) { 2001da177e4SLinus Torvalds rc = -ENOTSUPP; 2011da177e4SLinus Torvalds goto out_free; 2021da177e4SLinus Torvalds } 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds if (qout->segcnt == 1) { 2051da177e4SLinus Torvalds seg->vm_segtype = qout->range[0].start & 0xff; 2061da177e4SLinus Torvalds } else { 2071da177e4SLinus Torvalds /* multi-part segment. only one type supported here: 2081da177e4SLinus Torvalds - all parts are contiguous 2091da177e4SLinus Torvalds - all parts are either EW or EN type 2101da177e4SLinus Torvalds - maximum 6 parts allowed */ 2111da177e4SLinus Torvalds unsigned long start = qout->segstart >> PAGE_SHIFT; 2121da177e4SLinus Torvalds for (i=0; i<qout->segcnt; i++) { 2131da177e4SLinus Torvalds if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) && 2141da177e4SLinus Torvalds ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) { 2151da177e4SLinus Torvalds rc = -ENOTSUPP; 2161da177e4SLinus Torvalds goto out_free; 2171da177e4SLinus Torvalds } 2181da177e4SLinus Torvalds if (start != qout->range[i].start >> PAGE_SHIFT) { 2191da177e4SLinus Torvalds rc = -ENOTSUPP; 2201da177e4SLinus Torvalds goto out_free; 2211da177e4SLinus Torvalds } 2221da177e4SLinus Torvalds start = (qout->range[i].end >> PAGE_SHIFT) + 1; 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds seg->vm_segtype = SEG_TYPE_EWEN; 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds /* analyze diag output and update seg */ 2281da177e4SLinus Torvalds seg->start_addr = qout->segstart; 2291da177e4SLinus Torvalds seg->end = qout->segend; 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds memcpy (seg->range, qout->range, 6*sizeof(struct qrange)); 2321da177e4SLinus Torvalds seg->segcnt = qout->segcnt; 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds rc = 0; 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds out_free: 2371da177e4SLinus Torvalds if (qin) kfree(qin); 2381da177e4SLinus Torvalds if (qout) kfree(qout); 2391da177e4SLinus Torvalds return rc; 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds /* 2431da177e4SLinus Torvalds * check if the given segment collides with guest storage. 2441da177e4SLinus Torvalds * returns 1 if this is the case, 0 if no collision was found 2451da177e4SLinus Torvalds */ 2461da177e4SLinus Torvalds static int 2471da177e4SLinus Torvalds segment_overlaps_storage(struct dcss_segment *seg) 2481da177e4SLinus Torvalds { 2491da177e4SLinus Torvalds int i; 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds for (i=0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { 2521da177e4SLinus Torvalds if (memory_chunk[i].type != 0) 2531da177e4SLinus Torvalds continue; 2541da177e4SLinus Torvalds if ((memory_chunk[i].addr >> 20) > (seg->end >> 20)) 2551da177e4SLinus Torvalds continue; 2561da177e4SLinus Torvalds if (((memory_chunk[i].addr + memory_chunk[i].size - 1) >> 20) 2571da177e4SLinus Torvalds < (seg->start_addr >> 20)) 2581da177e4SLinus Torvalds continue; 2591da177e4SLinus Torvalds return 1; 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds return 0; 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds 2641da177e4SLinus Torvalds /* 2651da177e4SLinus Torvalds * check if segment collides with other segments that are currently loaded 2661da177e4SLinus Torvalds * returns 1 if this is the case, 0 if no collision was found 2671da177e4SLinus Torvalds */ 2681da177e4SLinus Torvalds static int 2691da177e4SLinus Torvalds segment_overlaps_others (struct dcss_segment *seg) 2701da177e4SLinus Torvalds { 2711da177e4SLinus Torvalds struct list_head *l; 2721da177e4SLinus Torvalds struct dcss_segment *tmp; 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds assert_spin_locked(&dcss_lock); 2751da177e4SLinus Torvalds list_for_each(l, &dcss_list) { 2761da177e4SLinus Torvalds tmp = list_entry(l, struct dcss_segment, list); 2771da177e4SLinus Torvalds if ((tmp->start_addr >> 20) > (seg->end >> 20)) 2781da177e4SLinus Torvalds continue; 2791da177e4SLinus Torvalds if ((tmp->end >> 20) < (seg->start_addr >> 20)) 2801da177e4SLinus Torvalds continue; 2811da177e4SLinus Torvalds if (seg == tmp) 2821da177e4SLinus Torvalds continue; 2831da177e4SLinus Torvalds return 1; 2841da177e4SLinus Torvalds } 2851da177e4SLinus Torvalds return 0; 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds 2881da177e4SLinus Torvalds /* 2891da177e4SLinus Torvalds * check if segment exceeds the kernel mapping range (detected or set via mem=) 2901da177e4SLinus Torvalds * returns 1 if this is the case, 0 if segment fits into the range 2911da177e4SLinus Torvalds */ 2921da177e4SLinus Torvalds static inline int 2931da177e4SLinus Torvalds segment_exceeds_range (struct dcss_segment *seg) 2941da177e4SLinus Torvalds { 2951da177e4SLinus Torvalds int seg_last_pfn = (seg->end) >> PAGE_SHIFT; 2961da177e4SLinus Torvalds if (seg_last_pfn > max_pfn) 2971da177e4SLinus Torvalds return 1; 2981da177e4SLinus Torvalds return 0; 2991da177e4SLinus Torvalds } 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds /* 3021da177e4SLinus Torvalds * get info about a segment 3031da177e4SLinus Torvalds * possible return values: 3041da177e4SLinus Torvalds * -ENOSYS : we are not running on VM 3051da177e4SLinus Torvalds * -EIO : could not perform query diagnose 3061da177e4SLinus Torvalds * -ENOENT : no such segment 3071da177e4SLinus Torvalds * -ENOTSUPP: multi-part segment cannot be used with linux 3081da177e4SLinus Torvalds * -ENOSPC : segment cannot be used (overlaps with storage) 3091da177e4SLinus Torvalds * -ENOMEM : out of memory 3101da177e4SLinus Torvalds * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 3111da177e4SLinus Torvalds */ 3121da177e4SLinus Torvalds int 3131da177e4SLinus Torvalds segment_type (char* name) 3141da177e4SLinus Torvalds { 3151da177e4SLinus Torvalds int rc; 3161da177e4SLinus Torvalds struct dcss_segment seg; 3171da177e4SLinus Torvalds 3181da177e4SLinus Torvalds if (!MACHINE_IS_VM) 3191da177e4SLinus Torvalds return -ENOSYS; 3201da177e4SLinus Torvalds 3211da177e4SLinus Torvalds dcss_mkname(name, seg.dcss_name); 3221da177e4SLinus Torvalds rc = query_segment_type (&seg); 3231da177e4SLinus Torvalds if (rc < 0) 3241da177e4SLinus Torvalds return rc; 3251da177e4SLinus Torvalds return seg.vm_segtype; 3261da177e4SLinus Torvalds } 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds /* 3291da177e4SLinus Torvalds * real segment loading function, called from segment_load 3301da177e4SLinus Torvalds */ 3311da177e4SLinus Torvalds static int 3321da177e4SLinus Torvalds __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end) 3331da177e4SLinus Torvalds { 3341da177e4SLinus Torvalds struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment), 3351da177e4SLinus Torvalds GFP_DMA); 3361da177e4SLinus Torvalds int dcss_command, rc, diag_cc; 3371da177e4SLinus Torvalds 3381da177e4SLinus Torvalds if (seg == NULL) { 3391da177e4SLinus Torvalds rc = -ENOMEM; 3401da177e4SLinus Torvalds goto out; 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds dcss_mkname (name, seg->dcss_name); 3431da177e4SLinus Torvalds rc = query_segment_type (seg); 3441da177e4SLinus Torvalds if (rc < 0) 3451da177e4SLinus Torvalds goto out_free; 3461da177e4SLinus Torvalds if (segment_exceeds_range(seg)) { 3471da177e4SLinus Torvalds PRINT_WARN ("segment_load: not loading segment %s - exceeds" 3481da177e4SLinus Torvalds " kernel mapping range\n",name); 3491da177e4SLinus Torvalds rc = -ERANGE; 3501da177e4SLinus Torvalds goto out_free; 3511da177e4SLinus Torvalds } 3521da177e4SLinus Torvalds if (segment_overlaps_storage(seg)) { 3531da177e4SLinus Torvalds PRINT_WARN ("segment_load: not loading segment %s - overlaps" 3541da177e4SLinus Torvalds " storage\n",name); 3551da177e4SLinus Torvalds rc = -ENOSPC; 3561da177e4SLinus Torvalds goto out_free; 3571da177e4SLinus Torvalds } 3581da177e4SLinus Torvalds if (segment_overlaps_others(seg)) { 3591da177e4SLinus Torvalds PRINT_WARN ("segment_load: not loading segment %s - overlaps" 3601da177e4SLinus Torvalds " other segments\n",name); 3611da177e4SLinus Torvalds rc = -EBUSY; 3621da177e4SLinus Torvalds goto out_free; 3631da177e4SLinus Torvalds } 3641da177e4SLinus Torvalds if (do_nonshared) 3651da177e4SLinus Torvalds dcss_command = DCSS_LOADNSR; 3661da177e4SLinus Torvalds else 3671da177e4SLinus Torvalds dcss_command = DCSS_LOADNOLY; 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds diag_cc = dcss_diag(dcss_command, seg->dcss_name, 3701da177e4SLinus Torvalds &seg->start_addr, &seg->end); 3711da177e4SLinus Torvalds if (diag_cc > 1) { 3721da177e4SLinus Torvalds PRINT_WARN ("segment_load: could not load segment %s - " 3731da177e4SLinus Torvalds "diag returned error (%ld)\n",name,seg->end); 3741da177e4SLinus Torvalds rc = dcss_diag_translate_rc (seg->end); 3751da177e4SLinus Torvalds dcss_diag(DCSS_PURGESEG, seg->dcss_name, 3761da177e4SLinus Torvalds &seg->start_addr, &seg->end); 3771da177e4SLinus Torvalds goto out_free; 3781da177e4SLinus Torvalds } 3791da177e4SLinus Torvalds seg->do_nonshared = do_nonshared; 3801da177e4SLinus Torvalds atomic_set(&seg->ref_count, 1); 3811da177e4SLinus Torvalds list_add(&seg->list, &dcss_list); 3821da177e4SLinus Torvalds rc = seg->vm_segtype; 3831da177e4SLinus Torvalds *addr = seg->start_addr; 3841da177e4SLinus Torvalds *end = seg->end; 3851da177e4SLinus Torvalds if (do_nonshared) 3861da177e4SLinus Torvalds PRINT_INFO ("segment_load: loaded segment %s range %p .. %p " 3871da177e4SLinus Torvalds "type %s in non-shared mode\n", name, 3881da177e4SLinus Torvalds (void*)seg->start_addr, (void*)seg->end, 3891da177e4SLinus Torvalds segtype_string[seg->vm_segtype]); 3901da177e4SLinus Torvalds else 3911da177e4SLinus Torvalds PRINT_INFO ("segment_load: loaded segment %s range %p .. %p " 3921da177e4SLinus Torvalds "type %s in shared mode\n", name, 3931da177e4SLinus Torvalds (void*)seg->start_addr, (void*)seg->end, 3941da177e4SLinus Torvalds segtype_string[seg->vm_segtype]); 3951da177e4SLinus Torvalds goto out; 3961da177e4SLinus Torvalds out_free: 3971da177e4SLinus Torvalds kfree (seg); 3981da177e4SLinus Torvalds out: 3991da177e4SLinus Torvalds return rc; 4001da177e4SLinus Torvalds } 4011da177e4SLinus Torvalds 4021da177e4SLinus Torvalds /* 4031da177e4SLinus Torvalds * this function loads a DCSS segment 4041da177e4SLinus Torvalds * name : name of the DCSS 4051da177e4SLinus Torvalds * do_nonshared : 0 indicates that the dcss should be shared with other linux images 4061da177e4SLinus Torvalds * 1 indicates that the dcss should be exclusive for this linux image 4071da177e4SLinus Torvalds * addr : will be filled with start address of the segment 4081da177e4SLinus Torvalds * end : will be filled with end address of the segment 4091da177e4SLinus Torvalds * return values: 4101da177e4SLinus Torvalds * -ENOSYS : we are not running on VM 4111da177e4SLinus Torvalds * -EIO : could not perform query or load diagnose 4121da177e4SLinus Torvalds * -ENOENT : no such segment 4131da177e4SLinus Torvalds * -ENOTSUPP: multi-part segment cannot be used with linux 4141da177e4SLinus Torvalds * -ENOSPC : segment cannot be used (overlaps with storage) 4151da177e4SLinus Torvalds * -EBUSY : segment can temporarily not be used (overlaps with dcss) 4161da177e4SLinus Torvalds * -ERANGE : segment cannot be used (exceeds kernel mapping range) 4171da177e4SLinus Torvalds * -EPERM : segment is currently loaded with incompatible permissions 4181da177e4SLinus Torvalds * -ENOMEM : out of memory 4191da177e4SLinus Torvalds * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h 4201da177e4SLinus Torvalds */ 4211da177e4SLinus Torvalds int 4221da177e4SLinus Torvalds segment_load (char *name, int do_nonshared, unsigned long *addr, 4231da177e4SLinus Torvalds unsigned long *end) 4241da177e4SLinus Torvalds { 4251da177e4SLinus Torvalds struct dcss_segment *seg; 4261da177e4SLinus Torvalds int rc; 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds if (!MACHINE_IS_VM) 4291da177e4SLinus Torvalds return -ENOSYS; 4301da177e4SLinus Torvalds 4311da177e4SLinus Torvalds spin_lock (&dcss_lock); 4321da177e4SLinus Torvalds seg = segment_by_name (name); 4331da177e4SLinus Torvalds if (seg == NULL) 4341da177e4SLinus Torvalds rc = __segment_load (name, do_nonshared, addr, end); 4351da177e4SLinus Torvalds else { 4361da177e4SLinus Torvalds if (do_nonshared == seg->do_nonshared) { 4371da177e4SLinus Torvalds atomic_inc(&seg->ref_count); 4381da177e4SLinus Torvalds *addr = seg->start_addr; 4391da177e4SLinus Torvalds *end = seg->end; 4401da177e4SLinus Torvalds rc = seg->vm_segtype; 4411da177e4SLinus Torvalds } else { 4421da177e4SLinus Torvalds *addr = *end = 0; 4431da177e4SLinus Torvalds rc = -EPERM; 4441da177e4SLinus Torvalds } 4451da177e4SLinus Torvalds } 4461da177e4SLinus Torvalds spin_unlock (&dcss_lock); 4471da177e4SLinus Torvalds return rc; 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds 4501da177e4SLinus Torvalds /* 4511da177e4SLinus Torvalds * this function modifies the shared state of a DCSS segment. note that 4521da177e4SLinus Torvalds * name : name of the DCSS 4531da177e4SLinus Torvalds * do_nonshared : 0 indicates that the dcss should be shared with other linux images 4541da177e4SLinus Torvalds * 1 indicates that the dcss should be exclusive for this linux image 4551da177e4SLinus Torvalds * return values: 4561da177e4SLinus Torvalds * -EIO : could not perform load diagnose (segment gone!) 4571da177e4SLinus Torvalds * -ENOENT : no such segment (segment gone!) 4581da177e4SLinus Torvalds * -EAGAIN : segment is in use by other exploiters, try later 4591da177e4SLinus Torvalds * -EINVAL : no segment with the given name is currently loaded - name invalid 4601da177e4SLinus Torvalds * 0 : operation succeeded 4611da177e4SLinus Torvalds */ 4621da177e4SLinus Torvalds int 4631da177e4SLinus Torvalds segment_modify_shared (char *name, int do_nonshared) 4641da177e4SLinus Torvalds { 4651da177e4SLinus Torvalds struct dcss_segment *seg; 4661da177e4SLinus Torvalds unsigned long dummy; 4671da177e4SLinus Torvalds int dcss_command, rc, diag_cc; 4681da177e4SLinus Torvalds 4691da177e4SLinus Torvalds spin_lock (&dcss_lock); 4701da177e4SLinus Torvalds seg = segment_by_name (name); 4711da177e4SLinus Torvalds if (seg == NULL) { 4721da177e4SLinus Torvalds rc = -EINVAL; 4731da177e4SLinus Torvalds goto out_unlock; 4741da177e4SLinus Torvalds } 4751da177e4SLinus Torvalds if (do_nonshared == seg->do_nonshared) { 4761da177e4SLinus Torvalds PRINT_INFO ("segment_modify_shared: not reloading segment %s" 4771da177e4SLinus Torvalds " - already in requested mode\n",name); 4781da177e4SLinus Torvalds rc = 0; 4791da177e4SLinus Torvalds goto out_unlock; 4801da177e4SLinus Torvalds } 4811da177e4SLinus Torvalds if (atomic_read (&seg->ref_count) != 1) { 4821da177e4SLinus Torvalds PRINT_WARN ("segment_modify_shared: not reloading segment %s - " 4831da177e4SLinus Torvalds "segment is in use by other driver(s)\n",name); 4841da177e4SLinus Torvalds rc = -EAGAIN; 4851da177e4SLinus Torvalds goto out_unlock; 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds dcss_diag(DCSS_PURGESEG, seg->dcss_name, 4881da177e4SLinus Torvalds &dummy, &dummy); 4891da177e4SLinus Torvalds if (do_nonshared) 4901da177e4SLinus Torvalds dcss_command = DCSS_LOADNSR; 4911da177e4SLinus Torvalds else 4921da177e4SLinus Torvalds dcss_command = DCSS_LOADNOLY; 4931da177e4SLinus Torvalds diag_cc = dcss_diag(dcss_command, seg->dcss_name, 4941da177e4SLinus Torvalds &seg->start_addr, &seg->end); 4951da177e4SLinus Torvalds if (diag_cc > 1) { 4961da177e4SLinus Torvalds PRINT_WARN ("segment_modify_shared: could not reload segment %s" 4971da177e4SLinus Torvalds " - diag returned error (%ld)\n",name,seg->end); 4981da177e4SLinus Torvalds rc = dcss_diag_translate_rc (seg->end); 4991da177e4SLinus Torvalds goto out_del; 5001da177e4SLinus Torvalds } 5011da177e4SLinus Torvalds seg->do_nonshared = do_nonshared; 5021da177e4SLinus Torvalds rc = 0; 5031da177e4SLinus Torvalds goto out_unlock; 5041da177e4SLinus Torvalds out_del: 5051da177e4SLinus Torvalds list_del(&seg->list); 5061da177e4SLinus Torvalds dcss_diag(DCSS_PURGESEG, seg->dcss_name, 5071da177e4SLinus Torvalds &dummy, &dummy); 5081da177e4SLinus Torvalds kfree (seg); 5091da177e4SLinus Torvalds out_unlock: 5101da177e4SLinus Torvalds spin_unlock(&dcss_lock); 5111da177e4SLinus Torvalds return rc; 5121da177e4SLinus Torvalds } 5131da177e4SLinus Torvalds 5141da177e4SLinus Torvalds /* 5151da177e4SLinus Torvalds * Decrease the use count of a DCSS segment and remove 5161da177e4SLinus Torvalds * it from the address space if nobody is using it 5171da177e4SLinus Torvalds * any longer. 5181da177e4SLinus Torvalds */ 5191da177e4SLinus Torvalds void 5201da177e4SLinus Torvalds segment_unload(char *name) 5211da177e4SLinus Torvalds { 5221da177e4SLinus Torvalds unsigned long dummy; 5231da177e4SLinus Torvalds struct dcss_segment *seg; 5241da177e4SLinus Torvalds 5251da177e4SLinus Torvalds if (!MACHINE_IS_VM) 5261da177e4SLinus Torvalds return; 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds spin_lock(&dcss_lock); 5291da177e4SLinus Torvalds seg = segment_by_name (name); 5301da177e4SLinus Torvalds if (seg == NULL) { 5311da177e4SLinus Torvalds PRINT_ERR ("could not find segment %s in segment_unload, " 5321da177e4SLinus Torvalds "please report to linux390@de.ibm.com\n",name); 5331da177e4SLinus Torvalds goto out_unlock; 5341da177e4SLinus Torvalds } 5351da177e4SLinus Torvalds if (atomic_dec_return(&seg->ref_count) == 0) { 5361da177e4SLinus Torvalds list_del(&seg->list); 5371da177e4SLinus Torvalds dcss_diag(DCSS_PURGESEG, seg->dcss_name, 5381da177e4SLinus Torvalds &dummy, &dummy); 5391da177e4SLinus Torvalds kfree(seg); 5401da177e4SLinus Torvalds } 5411da177e4SLinus Torvalds out_unlock: 5421da177e4SLinus Torvalds spin_unlock(&dcss_lock); 5431da177e4SLinus Torvalds } 5441da177e4SLinus Torvalds 5451da177e4SLinus Torvalds /* 5461da177e4SLinus Torvalds * save segment content permanently 5471da177e4SLinus Torvalds */ 5481da177e4SLinus Torvalds void 5491da177e4SLinus Torvalds segment_save(char *name) 5501da177e4SLinus Torvalds { 5511da177e4SLinus Torvalds struct dcss_segment *seg; 5521da177e4SLinus Torvalds int startpfn = 0; 5531da177e4SLinus Torvalds int endpfn = 0; 5541da177e4SLinus Torvalds char cmd1[160]; 5551da177e4SLinus Torvalds char cmd2[80]; 5561da177e4SLinus Torvalds int i; 5571da177e4SLinus Torvalds 5581da177e4SLinus Torvalds if (!MACHINE_IS_VM) 5591da177e4SLinus Torvalds return; 5601da177e4SLinus Torvalds 5611da177e4SLinus Torvalds spin_lock(&dcss_lock); 5621da177e4SLinus Torvalds seg = segment_by_name (name); 5631da177e4SLinus Torvalds 5641da177e4SLinus Torvalds if (seg == NULL) { 5651da177e4SLinus Torvalds PRINT_ERR ("could not find segment %s in segment_save, please report to linux390@de.ibm.com\n",name); 5661da177e4SLinus Torvalds return; 5671da177e4SLinus Torvalds } 5681da177e4SLinus Torvalds 5691da177e4SLinus Torvalds startpfn = seg->start_addr >> PAGE_SHIFT; 5701da177e4SLinus Torvalds endpfn = (seg->end) >> PAGE_SHIFT; 5711da177e4SLinus Torvalds sprintf(cmd1, "DEFSEG %s", name); 5721da177e4SLinus Torvalds for (i=0; i<seg->segcnt; i++) { 5731da177e4SLinus Torvalds sprintf(cmd1+strlen(cmd1), " %X-%X %s", 5741da177e4SLinus Torvalds seg->range[i].start >> PAGE_SHIFT, 5751da177e4SLinus Torvalds seg->range[i].end >> PAGE_SHIFT, 5761da177e4SLinus Torvalds segtype_string[seg->range[i].start & 0xff]); 5771da177e4SLinus Torvalds } 5781da177e4SLinus Torvalds sprintf(cmd2, "SAVESEG %s", name); 5791da177e4SLinus Torvalds cpcmd(cmd1, NULL, 0); 5801da177e4SLinus Torvalds cpcmd(cmd2, NULL, 0); 5811da177e4SLinus Torvalds spin_unlock(&dcss_lock); 5821da177e4SLinus Torvalds } 5831da177e4SLinus Torvalds 5841da177e4SLinus Torvalds EXPORT_SYMBOL(segment_load); 5851da177e4SLinus Torvalds EXPORT_SYMBOL(segment_unload); 5861da177e4SLinus Torvalds EXPORT_SYMBOL(segment_save); 5871da177e4SLinus Torvalds EXPORT_SYMBOL(segment_type); 5881da177e4SLinus Torvalds EXPORT_SYMBOL(segment_modify_shared); 589