- int i,j,rc;
- unsigned long rx, ry;
-
- typedef struct segentry {
- char thisseg[8];
- } segentry;
-
- struct qout64 {
- int segstart;
- int segend;
- int segcnt;
- int segrcnt;
- segentry segout[6];
- };
-
- struct qin64 {
- char qopcode;
- char rsrv1[3];
- char qrcode;
- char rsrv2[3];
- char qname[8];
- unsigned int qoutptr;
- short int qoutlen;
- };
-
-
- struct qin64 *qinarea;
- struct qout64 *qoutarea;
-
- qinarea = (struct qin64*) get_zeroed_page (GFP_DMA);
- if (!qinarea) {
- rc =-ENOMEM;
- goto out;
- }
- qoutarea = (struct qout64*) get_zeroed_page (GFP_DMA);
- if (!qoutarea) {
- rc = -ENOMEM;
- free_page ((unsigned long) qinarea);
- goto out;
- }
- memset (qinarea,0,PAGE_SIZE);
- memset (qoutarea,0,PAGE_SIZE);
-
- qinarea->qopcode = DCSS_QACTV; /* do a query for active
- segments */
- qinarea->qoutptr = (unsigned long) qoutarea;
- qinarea->qoutlen = sizeof(struct qout64);
-
- /* Move segment name into double word aligned
- field and pad with blanks to 8 long.
- */
-
- for (i = j = 0 ; i < 8; i++) {
- qinarea->qname[i] = (name[j] == '\0') ? ' ' : name[j++];
- }
-
- /* name already in EBCDIC */
- /* ASCEBC ((void *)&qinarea.qname, 8); */
-
- /* set the assembler variables */
- rx = (unsigned long) qinarea;
- ry = DCSS_SEGEXT; /* this is extended function */
-
- /* issue diagnose x'64' */
- __asm__ __volatile__(
-#ifdef CONFIG_ARCH_S390X
- " sam31\n" // switch to 31 bit
- " diag %0,%1,0x64\n"
- " sam64\n" // switch back to 64 bit
-#else
- " diag %0,%1,0x64\n"
-#endif
- " ipm %2\n"
- " srl %2,28\n"
- : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" );
-
- /* parse the query output area */
- *segstart=qoutarea->segstart;
- *segend=qoutarea->segend;
-
- if (rc > 1)
- {
- *rwattr = 2;
- *shattr = 2;
- rc = 0;
- goto free;
- }
-
- if (qoutarea->segcnt > 6)
- {
- *rwattr = 3;
- *shattr = 3;
- rc = 0;
- goto free;
- }
-
- *rwattr = 1;
- *shattr = 1;
-
- for (i=0; i < qoutarea->segrcnt; i++) {
- if (qoutarea->segout[i].thisseg[3] == 2 ||
- qoutarea->segout[i].thisseg[3] == 3 ||
- qoutarea->segout[i].thisseg[3] == 6 )
- *rwattr = 0;
- if (qoutarea->segout[i].thisseg[3] == 1 ||
- qoutarea->segout[i].thisseg[3] == 3 ||
- qoutarea->segout[i].thisseg[3] == 5 )
- *shattr = 0;
- } /* end of for statement */
- rc = 0;
- free:
- free_page ((unsigned long) qoutarea);
- free_page ((unsigned long) qinarea);
+ struct qin64 *qin = kmalloc (sizeof(struct qin64), GFP_DMA);
+ struct qout64 *qout = kmalloc (sizeof(struct qout64), GFP_DMA);
+
+ int diag_cc, rc, i;
+ unsigned long dummy, vmrc;
+
+ if ((qin == NULL) || (qout == NULL)) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+
+ /* initialize diag input parameters */
+ qin->qopcode = DCSS_FINDSEGA;
+ qin->qoutptr = (unsigned long) qout;
+ qin->qoutlen = sizeof(struct qout64);
+ memcpy (qin->qname, seg->dcss_name, 8);
+
+ diag_cc = dcss_diag (DCSS_SEGEXT, qin, &dummy, &vmrc);
+
+ if (diag_cc > 1) {
+ PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc);
+ rc = dcss_diag_translate_rc (vmrc);
+ goto out_free;
+ }
+
+ if (qout->segcnt > 6) {
+ rc = -ENOTSUPP;
+ goto out_free;
+ }
+
+ if (qout->segcnt == 1) {
+ seg->vm_segtype = qout->range[0].start & 0xff;
+ } else {
+ /* multi-part segment. only one type supported here:
+ - all parts are contiguous
+ - all parts are either EW or EN type
+ - maximum 6 parts allowed */
+ unsigned long start = qout->segstart >> PAGE_SHIFT;
+ for (i=0; i<qout->segcnt; i++) {
+ if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) &&
+ ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) {
+ rc = -ENOTSUPP;
+ goto out_free;
+ }
+ if (start != qout->range[i].start >> PAGE_SHIFT) {
+ rc = -ENOTSUPP;
+ goto out_free;
+ }
+ start = (qout->range[i].end >> PAGE_SHIFT) + 1;
+ }
+ seg->vm_segtype = SEG_TYPE_EWEN;
+ }
+
+ /* analyze diag output and update seg */
+ seg->start_addr = qout->segstart;
+ seg->end = qout->segend;
+
+ memcpy (seg->range, qout->range, 6*sizeof(struct qrange));
+ seg->segcnt = qout->segcnt;
+
+ rc = 0;
+
+ out_free:
+ kfree(qin);
+ kfree(qout);
+ return rc;
+}
+
+/*
+ * get info about a segment
+ * possible return values:
+ * -ENOSYS : we are not running on VM
+ * -EIO : could not perform query diagnose
+ * -ENOENT : no such segment
+ * -ENOTSUPP: multi-part segment cannot be used with linux
+ * -ENOSPC : segment cannot be used (overlaps with storage)
+ * -ENOMEM : out of memory
+ * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h
+ */
+int
+segment_type (char* name)
+{
+ int rc;
+ struct dcss_segment seg;
+
+ if (!MACHINE_IS_VM)
+ return -ENOSYS;
+
+ dcss_mkname(name, seg.dcss_name);
+ rc = query_segment_type (&seg);
+ if (rc < 0)
+ return rc;
+ return seg.vm_segtype;
+}
+
+/*
+ * real segment loading function, called from segment_load
+ */
+static int
+__segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end)
+{
+ struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment),
+ GFP_DMA);
+ int dcss_command, rc, diag_cc;
+
+ if (seg == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ dcss_mkname (name, seg->dcss_name);
+ rc = query_segment_type (seg);
+ if (rc < 0)
+ goto out_free;
+
+ rc = add_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1);
+
+ switch (rc) {
+ case 0:
+ break;
+ case -ENOSPC:
+ PRINT_WARN("segment_load: not loading segment %s - overlaps "
+ "storage/segment\n", name);
+ goto out_free;
+ case -ERANGE:
+ PRINT_WARN("segment_load: not loading segment %s - exceeds "
+ "kernel mapping range\n", name);
+ goto out_free;
+ default:
+ PRINT_WARN("segment_load: not loading segment %s (rc: %d)\n",
+ name, rc);
+ goto out_free;
+ }
+
+ if (do_nonshared)
+ dcss_command = DCSS_LOADNSR;
+ else
+ dcss_command = DCSS_LOADNOLY;
+
+ diag_cc = dcss_diag(dcss_command, seg->dcss_name,
+ &seg->start_addr, &seg->end);
+ if (diag_cc > 1) {
+ PRINT_WARN ("segment_load: could not load segment %s - "
+ "diag returned error (%ld)\n",name,seg->end);
+ rc = dcss_diag_translate_rc (seg->end);
+ dcss_diag(DCSS_PURGESEG, seg->dcss_name,
+ &seg->start_addr, &seg->end);
+ goto out_shared;
+ }
+ seg->do_nonshared = do_nonshared;
+ atomic_set(&seg->ref_count, 1);
+ list_add(&seg->list, &dcss_list);
+ rc = seg->vm_segtype;
+ *addr = seg->start_addr;
+ *end = seg->end;
+ if (do_nonshared)
+ PRINT_INFO ("segment_load: loaded segment %s range %p .. %p "
+ "type %s in non-shared mode\n", name,
+ (void*)seg->start_addr, (void*)seg->end,
+ segtype_string[seg->vm_segtype]);
+ else
+ PRINT_INFO ("segment_load: loaded segment %s range %p .. %p "
+ "type %s in shared mode\n", name,
+ (void*)seg->start_addr, (void*)seg->end,
+ segtype_string[seg->vm_segtype]);
+ goto out;
+ out_shared:
+ remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1);
+ out_free:
+ kfree(seg);