xref: /openbmc/linux/arch/s390/mm/extmem.c (revision f77f13e2)
1 /*
2  * File...........: arch/s390/mm/extmem.c
3  * Author(s)......: Carsten Otte <cotte@de.ibm.com>
4  * 		    Rob M van der Heij <rvdheij@nl.ibm.com>
5  * 		    Steven Shultz <shultzss@us.ibm.com>
6  * Bugreports.to..: <Linux390@de.ibm.com>
7  * (C) IBM Corporation 2002-2004
8  */
9 
10 #define KMSG_COMPONENT "extmem"
11 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
12 
13 #include <linux/kernel.h>
14 #include <linux/string.h>
15 #include <linux/spinlock.h>
16 #include <linux/list.h>
17 #include <linux/slab.h>
18 #include <linux/module.h>
19 #include <linux/bootmem.h>
20 #include <linux/ctype.h>
21 #include <linux/ioport.h>
22 #include <asm/page.h>
23 #include <asm/pgtable.h>
24 #include <asm/ebcdic.h>
25 #include <asm/errno.h>
26 #include <asm/extmem.h>
27 #include <asm/cpcmd.h>
28 #include <asm/setup.h>
29 
30 #define DCSS_LOADSHR    0x00
31 #define DCSS_LOADNSR    0x04
32 #define DCSS_PURGESEG   0x08
33 #define DCSS_FINDSEG    0x0c
34 #define DCSS_LOADNOLY   0x10
35 #define DCSS_SEGEXT     0x18
36 #define DCSS_LOADSHRX	0x20
37 #define DCSS_LOADNSRX	0x24
38 #define DCSS_FINDSEGX	0x2c
39 #define DCSS_SEGEXTX	0x38
40 #define DCSS_FINDSEGA   0x0c
41 
42 struct qrange {
43 	unsigned long  start; /* last byte type */
44 	unsigned long  end;   /* last byte reserved */
45 };
46 
47 struct qout64 {
48 	unsigned long segstart;
49 	unsigned long segend;
50 	int segcnt;
51 	int segrcnt;
52 	struct qrange range[6];
53 };
54 
55 #ifdef CONFIG_64BIT
56 struct qrange_old {
57 	unsigned int start; /* last byte type */
58 	unsigned int end;   /* last byte reserved */
59 };
60 
61 /* output area format for the Diag x'64' old subcode x'18' */
62 struct qout64_old {
63 	int segstart;
64 	int segend;
65 	int segcnt;
66 	int segrcnt;
67 	struct qrange_old range[6];
68 };
69 #endif
70 
71 struct qin64 {
72 	char qopcode;
73 	char rsrv1[3];
74 	char qrcode;
75 	char rsrv2[3];
76 	char qname[8];
77 	unsigned int qoutptr;
78 	short int qoutlen;
79 };
80 
81 struct dcss_segment {
82 	struct list_head list;
83 	char dcss_name[8];
84 	char res_name[15];
85 	unsigned long start_addr;
86 	unsigned long end;
87 	atomic_t ref_count;
88 	int do_nonshared;
89 	unsigned int vm_segtype;
90 	struct qrange range[6];
91 	int segcnt;
92 	struct resource *res;
93 };
94 
95 static DEFINE_MUTEX(dcss_lock);
96 static LIST_HEAD(dcss_list);
97 static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
98 					"EW/EN-MIXED" };
99 static int loadshr_scode, loadnsr_scode, findseg_scode;
100 static int segext_scode, purgeseg_scode;
101 static int scode_set;
102 
103 /* set correct Diag x'64' subcodes. */
104 static int
105 dcss_set_subcodes(void)
106 {
107 #ifdef CONFIG_64BIT
108 	char *name = kmalloc(8 * sizeof(char), GFP_DMA);
109 	unsigned long rx, ry;
110 	int rc;
111 
112 	if (name == NULL)
113 		return -ENOMEM;
114 
115 	rx = (unsigned long) name;
116 	ry = DCSS_FINDSEGX;
117 
118 	strcpy(name, "dummy");
119 	asm volatile(
120 		"	diag	%0,%1,0x64\n"
121 		"0:	ipm	%2\n"
122 		"	srl	%2,28\n"
123 		"	j	2f\n"
124 		"1:	la	%2,3\n"
125 		"2:\n"
126 		EX_TABLE(0b, 1b)
127 		: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
128 
129 	kfree(name);
130 	/* Diag x'64' new subcodes are supported, set to new subcodes */
131 	if (rc != 3) {
132 		loadshr_scode = DCSS_LOADSHRX;
133 		loadnsr_scode = DCSS_LOADNSRX;
134 		purgeseg_scode = DCSS_PURGESEG;
135 		findseg_scode = DCSS_FINDSEGX;
136 		segext_scode = DCSS_SEGEXTX;
137 		return 0;
138 	}
139 #endif
140 	/* Diag x'64' new subcodes are not supported, set to old subcodes */
141 	loadshr_scode = DCSS_LOADNOLY;
142 	loadnsr_scode = DCSS_LOADNSR;
143 	purgeseg_scode = DCSS_PURGESEG;
144 	findseg_scode = DCSS_FINDSEG;
145 	segext_scode = DCSS_SEGEXT;
146 	return 0;
147 }
148 
149 /*
150  * Create the 8 bytes, ebcdic VM segment name from
151  * an ascii name.
152  */
153 static void
154 dcss_mkname(char *name, char *dcss_name)
155 {
156 	int i;
157 
158 	for (i = 0; i < 8; i++) {
159 		if (name[i] == '\0')
160 			break;
161 		dcss_name[i] = toupper(name[i]);
162 	};
163 	for (; i < 8; i++)
164 		dcss_name[i] = ' ';
165 	ASCEBC(dcss_name, 8);
166 }
167 
168 
169 /*
170  * search all segments in dcss_list, and return the one
171  * namend *name. If not found, return NULL.
172  */
173 static struct dcss_segment *
174 segment_by_name (char *name)
175 {
176 	char dcss_name[9];
177 	struct list_head *l;
178 	struct dcss_segment *tmp, *retval = NULL;
179 
180 	BUG_ON(!mutex_is_locked(&dcss_lock));
181 	dcss_mkname (name, dcss_name);
182 	list_for_each (l, &dcss_list) {
183 		tmp = list_entry (l, struct dcss_segment, list);
184 		if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) {
185 			retval = tmp;
186 			break;
187 		}
188 	}
189 	return retval;
190 }
191 
192 
193 /*
194  * Perform a function on a dcss segment.
195  */
196 static inline int
197 dcss_diag(int *func, void *parameter,
198            unsigned long *ret1, unsigned long *ret2)
199 {
200 	unsigned long rx, ry;
201 	int rc;
202 
203 	if (scode_set == 0) {
204 		rc = dcss_set_subcodes();
205 		if (rc < 0)
206 			return rc;
207 		scode_set = 1;
208 	}
209 	rx = (unsigned long) parameter;
210 	ry = (unsigned long) *func;
211 
212 #ifdef CONFIG_64BIT
213 	/* 64-bit Diag x'64' new subcode, keep in 64-bit addressing mode */
214 	if (*func > DCSS_SEGEXT)
215 		asm volatile(
216 			"	diag	%0,%1,0x64\n"
217 			"	ipm	%2\n"
218 			"	srl	%2,28\n"
219 			: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
220 	/* 31-bit Diag x'64' old subcode, switch to 31-bit addressing mode */
221 	else
222 		asm volatile(
223 			"	sam31\n"
224 			"	diag	%0,%1,0x64\n"
225 			"	sam64\n"
226 			"	ipm	%2\n"
227 			"	srl	%2,28\n"
228 			: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
229 #else
230 	asm volatile(
231 		"	diag	%0,%1,0x64\n"
232 		"	ipm	%2\n"
233 		"	srl	%2,28\n"
234 		: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
235 #endif
236 	*ret1 = rx;
237 	*ret2 = ry;
238 	return rc;
239 }
240 
241 static inline int
242 dcss_diag_translate_rc (int vm_rc) {
243 	if (vm_rc == 44)
244 		return -ENOENT;
245 	return -EIO;
246 }
247 
248 
249 /* do a diag to get info about a segment.
250  * fills start_address, end and vm_segtype fields
251  */
252 static int
253 query_segment_type (struct dcss_segment *seg)
254 {
255 	struct qin64  *qin = kmalloc (sizeof(struct qin64), GFP_DMA);
256 	struct qout64 *qout = kmalloc (sizeof(struct qout64), GFP_DMA);
257 
258 	int diag_cc, rc, i;
259 	unsigned long dummy, vmrc;
260 
261 	if ((qin == NULL) || (qout == NULL)) {
262 		rc = -ENOMEM;
263 		goto out_free;
264 	}
265 
266 	/* initialize diag input parameters */
267 	qin->qopcode = DCSS_FINDSEGA;
268 	qin->qoutptr = (unsigned long) qout;
269 	qin->qoutlen = sizeof(struct qout64);
270 	memcpy (qin->qname, seg->dcss_name, 8);
271 
272 	diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc);
273 
274 	if (diag_cc < 0) {
275 		rc = diag_cc;
276 		goto out_free;
277 	}
278 	if (diag_cc > 1) {
279 		pr_warning("Querying a DCSS type failed with rc=%ld\n", vmrc);
280 		rc = dcss_diag_translate_rc (vmrc);
281 		goto out_free;
282 	}
283 
284 #ifdef CONFIG_64BIT
285 	/* Only old format of output area of Diagnose x'64' is supported,
286 	   copy data for the new format. */
287 	if (segext_scode == DCSS_SEGEXT) {
288 		struct qout64_old *qout_old;
289 		qout_old = kzalloc(sizeof(struct qout64_old), GFP_DMA);
290 		if (qout_old == NULL) {
291 			rc = -ENOMEM;
292 			goto out_free;
293 		}
294 		memcpy(qout_old, qout, sizeof(struct qout64_old));
295 		qout->segstart = (unsigned long) qout_old->segstart;
296 		qout->segend = (unsigned long) qout_old->segend;
297 		qout->segcnt = qout_old->segcnt;
298 		qout->segrcnt = qout_old->segrcnt;
299 
300 		if (qout->segcnt > 6)
301 			qout->segrcnt = 6;
302 		for (i = 0; i < qout->segrcnt; i++) {
303 			qout->range[i].start =
304 				(unsigned long) qout_old->range[i].start;
305 			qout->range[i].end =
306 				(unsigned long) qout_old->range[i].end;
307 		}
308 		kfree(qout_old);
309 	}
310 #endif
311 	if (qout->segcnt > 6) {
312 		rc = -EOPNOTSUPP;
313 		goto out_free;
314 	}
315 
316 	if (qout->segcnt == 1) {
317 		seg->vm_segtype = qout->range[0].start & 0xff;
318 	} else {
319 		/* multi-part segment. only one type supported here:
320 		    - all parts are contiguous
321 		    - all parts are either EW or EN type
322 		    - maximum 6 parts allowed */
323 		unsigned long start = qout->segstart >> PAGE_SHIFT;
324 		for (i=0; i<qout->segcnt; i++) {
325 			if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) &&
326 			    ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) {
327 				rc = -EOPNOTSUPP;
328 				goto out_free;
329 			}
330 			if (start != qout->range[i].start >> PAGE_SHIFT) {
331 				rc = -EOPNOTSUPP;
332 				goto out_free;
333 			}
334 			start = (qout->range[i].end >> PAGE_SHIFT) + 1;
335 		}
336 		seg->vm_segtype = SEG_TYPE_EWEN;
337 	}
338 
339 	/* analyze diag output and update seg */
340 	seg->start_addr = qout->segstart;
341 	seg->end = qout->segend;
342 
343 	memcpy (seg->range, qout->range, 6*sizeof(struct qrange));
344 	seg->segcnt = qout->segcnt;
345 
346 	rc = 0;
347 
348  out_free:
349 	kfree(qin);
350 	kfree(qout);
351 	return rc;
352 }
353 
354 /*
355  * get info about a segment
356  * possible return values:
357  * -ENOSYS  : we are not running on VM
358  * -EIO     : could not perform query diagnose
359  * -ENOENT  : no such segment
360  * -EOPNOTSUPP: multi-part segment cannot be used with linux
361  * -ENOMEM  : out of memory
362  * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
363  */
364 int
365 segment_type (char* name)
366 {
367 	int rc;
368 	struct dcss_segment seg;
369 
370 	if (!MACHINE_IS_VM)
371 		return -ENOSYS;
372 
373 	dcss_mkname(name, seg.dcss_name);
374 	rc = query_segment_type (&seg);
375 	if (rc < 0)
376 		return rc;
377 	return seg.vm_segtype;
378 }
379 
380 /*
381  * check if segment collides with other segments that are currently loaded
382  * returns 1 if this is the case, 0 if no collision was found
383  */
384 static int
385 segment_overlaps_others (struct dcss_segment *seg)
386 {
387 	struct list_head *l;
388 	struct dcss_segment *tmp;
389 
390 	BUG_ON(!mutex_is_locked(&dcss_lock));
391 	list_for_each(l, &dcss_list) {
392 		tmp = list_entry(l, struct dcss_segment, list);
393 		if ((tmp->start_addr >> 20) > (seg->end >> 20))
394 			continue;
395 		if ((tmp->end >> 20) < (seg->start_addr >> 20))
396 			continue;
397 		if (seg == tmp)
398 			continue;
399 		return 1;
400 	}
401 	return 0;
402 }
403 
404 /*
405  * real segment loading function, called from segment_load
406  */
407 static int
408 __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end)
409 {
410 	struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment),
411 			GFP_DMA);
412 	int rc, diag_cc;
413 	unsigned long start_addr, end_addr, dummy;
414 
415 	if (seg == NULL) {
416 		rc = -ENOMEM;
417 		goto out;
418 	}
419 	dcss_mkname (name, seg->dcss_name);
420 	rc = query_segment_type (seg);
421 	if (rc < 0)
422 		goto out_free;
423 
424 	if (loadshr_scode == DCSS_LOADSHRX) {
425 		if (segment_overlaps_others(seg)) {
426 			rc = -EBUSY;
427 			goto out_free;
428 		}
429 	}
430 
431 	rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
432 
433 	if (rc)
434 		goto out_free;
435 
436 	seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL);
437 	if (seg->res == NULL) {
438 		rc = -ENOMEM;
439 		goto out_shared;
440 	}
441 	seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM;
442 	seg->res->start = seg->start_addr;
443 	seg->res->end = seg->end;
444 	memcpy(&seg->res_name, seg->dcss_name, 8);
445 	EBCASC(seg->res_name, 8);
446 	seg->res_name[8] = '\0';
447 	strncat(seg->res_name, " (DCSS)", 7);
448 	seg->res->name = seg->res_name;
449 	rc = seg->vm_segtype;
450 	if (rc == SEG_TYPE_SC ||
451 	    ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared))
452 		seg->res->flags |= IORESOURCE_READONLY;
453 	if (request_resource(&iomem_resource, seg->res)) {
454 		rc = -EBUSY;
455 		kfree(seg->res);
456 		goto out_shared;
457 	}
458 
459 	if (do_nonshared)
460 		diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
461 				&start_addr, &end_addr);
462 	else
463 		diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
464 				&start_addr, &end_addr);
465 	if (diag_cc < 0) {
466 		dcss_diag(&purgeseg_scode, seg->dcss_name,
467 				&dummy, &dummy);
468 		rc = diag_cc;
469 		goto out_resource;
470 	}
471 	if (diag_cc > 1) {
472 		pr_warning("Loading DCSS %s failed with rc=%ld\n", name,
473 			   end_addr);
474 		rc = dcss_diag_translate_rc(end_addr);
475 		dcss_diag(&purgeseg_scode, seg->dcss_name,
476 				&dummy, &dummy);
477 		goto out_resource;
478 	}
479 	seg->start_addr = start_addr;
480 	seg->end = end_addr;
481 	seg->do_nonshared = do_nonshared;
482 	atomic_set(&seg->ref_count, 1);
483 	list_add(&seg->list, &dcss_list);
484 	*addr = seg->start_addr;
485 	*end  = seg->end;
486 	if (do_nonshared)
487 		pr_info("DCSS %s of range %p to %p and type %s loaded as "
488 			"exclusive-writable\n", name, (void*) seg->start_addr,
489 			(void*) seg->end, segtype_string[seg->vm_segtype]);
490 	else {
491 		pr_info("DCSS %s of range %p to %p and type %s loaded in "
492 			"shared access mode\n", name, (void*) seg->start_addr,
493 			(void*) seg->end, segtype_string[seg->vm_segtype]);
494 	}
495 	goto out;
496  out_resource:
497 	release_resource(seg->res);
498 	kfree(seg->res);
499  out_shared:
500 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
501  out_free:
502 	kfree(seg);
503  out:
504 	return rc;
505 }
506 
507 /*
508  * this function loads a DCSS segment
509  * name         : name of the DCSS
510  * do_nonshared : 0 indicates that the dcss should be shared with other linux images
511  *                1 indicates that the dcss should be exclusive for this linux image
512  * addr         : will be filled with start address of the segment
513  * end          : will be filled with end address of the segment
514  * return values:
515  * -ENOSYS  : we are not running on VM
516  * -EIO     : could not perform query or load diagnose
517  * -ENOENT  : no such segment
518  * -EOPNOTSUPP: multi-part segment cannot be used with linux
519  * -ENOSPC  : segment cannot be used (overlaps with storage)
520  * -EBUSY   : segment can temporarily not be used (overlaps with dcss)
521  * -ERANGE  : segment cannot be used (exceeds kernel mapping range)
522  * -EPERM   : segment is currently loaded with incompatible permissions
523  * -ENOMEM  : out of memory
524  * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
525  */
526 int
527 segment_load (char *name, int do_nonshared, unsigned long *addr,
528 		unsigned long *end)
529 {
530 	struct dcss_segment *seg;
531 	int rc;
532 
533 	if (!MACHINE_IS_VM)
534 		return -ENOSYS;
535 
536 	mutex_lock(&dcss_lock);
537 	seg = segment_by_name (name);
538 	if (seg == NULL)
539 		rc = __segment_load (name, do_nonshared, addr, end);
540 	else {
541 		if (do_nonshared == seg->do_nonshared) {
542 			atomic_inc(&seg->ref_count);
543 			*addr = seg->start_addr;
544 			*end  = seg->end;
545 			rc    = seg->vm_segtype;
546 		} else {
547 			*addr = *end = 0;
548 			rc    = -EPERM;
549 		}
550 	}
551 	mutex_unlock(&dcss_lock);
552 	return rc;
553 }
554 
555 /*
556  * this function modifies the shared state of a DCSS segment. note that
557  * name         : name of the DCSS
558  * do_nonshared : 0 indicates that the dcss should be shared with other linux images
559  *                1 indicates that the dcss should be exclusive for this linux image
560  * return values:
561  * -EIO     : could not perform load diagnose (segment gone!)
562  * -ENOENT  : no such segment (segment gone!)
563  * -EAGAIN  : segment is in use by other exploiters, try later
564  * -EINVAL  : no segment with the given name is currently loaded - name invalid
565  * -EBUSY   : segment can temporarily not be used (overlaps with dcss)
566  * 0	    : operation succeeded
567  */
568 int
569 segment_modify_shared (char *name, int do_nonshared)
570 {
571 	struct dcss_segment *seg;
572 	unsigned long start_addr, end_addr, dummy;
573 	int rc, diag_cc;
574 
575 	mutex_lock(&dcss_lock);
576 	seg = segment_by_name (name);
577 	if (seg == NULL) {
578 		rc = -EINVAL;
579 		goto out_unlock;
580 	}
581 	if (do_nonshared == seg->do_nonshared) {
582 		pr_info("DCSS %s is already in the requested access "
583 			"mode\n", name);
584 		rc = 0;
585 		goto out_unlock;
586 	}
587 	if (atomic_read (&seg->ref_count) != 1) {
588 		pr_warning("DCSS %s is in use and cannot be reloaded\n",
589 			   name);
590 		rc = -EAGAIN;
591 		goto out_unlock;
592 	}
593 	release_resource(seg->res);
594 	if (do_nonshared)
595 		seg->res->flags &= ~IORESOURCE_READONLY;
596 	else
597 		if (seg->vm_segtype == SEG_TYPE_SR ||
598 		    seg->vm_segtype == SEG_TYPE_ER)
599 			seg->res->flags |= IORESOURCE_READONLY;
600 
601 	if (request_resource(&iomem_resource, seg->res)) {
602 		pr_warning("DCSS %s overlaps with used memory resources "
603 			   "and cannot be reloaded\n", name);
604 		rc = -EBUSY;
605 		kfree(seg->res);
606 		goto out_del_mem;
607 	}
608 
609 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
610 	if (do_nonshared)
611 		diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
612 				&start_addr, &end_addr);
613 	else
614 		diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
615 				&start_addr, &end_addr);
616 	if (diag_cc < 0) {
617 		rc = diag_cc;
618 		goto out_del_res;
619 	}
620 	if (diag_cc > 1) {
621 		pr_warning("Reloading DCSS %s failed with rc=%ld\n", name,
622 			   end_addr);
623 		rc = dcss_diag_translate_rc(end_addr);
624 		goto out_del_res;
625 	}
626 	seg->start_addr = start_addr;
627 	seg->end = end_addr;
628 	seg->do_nonshared = do_nonshared;
629 	rc = 0;
630 	goto out_unlock;
631  out_del_res:
632 	release_resource(seg->res);
633 	kfree(seg->res);
634  out_del_mem:
635 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
636 	list_del(&seg->list);
637 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
638 	kfree(seg);
639  out_unlock:
640 	mutex_unlock(&dcss_lock);
641 	return rc;
642 }
643 
644 /*
645  * Decrease the use count of a DCSS segment and remove
646  * it from the address space if nobody is using it
647  * any longer.
648  */
649 void
650 segment_unload(char *name)
651 {
652 	unsigned long dummy;
653 	struct dcss_segment *seg;
654 
655 	if (!MACHINE_IS_VM)
656 		return;
657 
658 	mutex_lock(&dcss_lock);
659 	seg = segment_by_name (name);
660 	if (seg == NULL) {
661 		pr_err("Unloading unknown DCSS %s failed\n", name);
662 		goto out_unlock;
663 	}
664 	if (atomic_dec_return(&seg->ref_count) != 0)
665 		goto out_unlock;
666 	release_resource(seg->res);
667 	kfree(seg->res);
668 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
669 	list_del(&seg->list);
670 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
671 	kfree(seg);
672 out_unlock:
673 	mutex_unlock(&dcss_lock);
674 }
675 
676 /*
677  * save segment content permanently
678  */
679 void
680 segment_save(char *name)
681 {
682 	struct dcss_segment *seg;
683 	int startpfn = 0;
684 	int endpfn = 0;
685 	char cmd1[160];
686 	char cmd2[80];
687 	int i, response;
688 
689 	if (!MACHINE_IS_VM)
690 		return;
691 
692 	mutex_lock(&dcss_lock);
693 	seg = segment_by_name (name);
694 
695 	if (seg == NULL) {
696 		pr_err("Saving unknown DCSS %s failed\n", name);
697 		goto out;
698 	}
699 
700 	startpfn = seg->start_addr >> PAGE_SHIFT;
701 	endpfn = (seg->end) >> PAGE_SHIFT;
702 	sprintf(cmd1, "DEFSEG %s", name);
703 	for (i=0; i<seg->segcnt; i++) {
704 		sprintf(cmd1+strlen(cmd1), " %lX-%lX %s",
705 			seg->range[i].start >> PAGE_SHIFT,
706 			seg->range[i].end >> PAGE_SHIFT,
707 			segtype_string[seg->range[i].start & 0xff]);
708 	}
709 	sprintf(cmd2, "SAVESEG %s", name);
710 	response = 0;
711 	cpcmd(cmd1, NULL, 0, &response);
712 	if (response) {
713 		pr_err("Saving a DCSS failed with DEFSEG response code "
714 		       "%i\n", response);
715 		goto out;
716 	}
717 	cpcmd(cmd2, NULL, 0, &response);
718 	if (response) {
719 		pr_err("Saving a DCSS failed with SAVESEG response code "
720 		       "%i\n", response);
721 		goto out;
722 	}
723 out:
724 	mutex_unlock(&dcss_lock);
725 }
726 
727 /*
728  * print appropriate error message for segment_load()/segment_type()
729  * return code
730  */
731 void segment_warning(int rc, char *seg_name)
732 {
733 	switch (rc) {
734 	case -ENOENT:
735 		pr_err("DCSS %s cannot be loaded or queried\n", seg_name);
736 		break;
737 	case -ENOSYS:
738 		pr_err("DCSS %s cannot be loaded or queried without "
739 		       "z/VM\n", seg_name);
740 		break;
741 	case -EIO:
742 		pr_err("Loading or querying DCSS %s resulted in a "
743 		       "hardware error\n", seg_name);
744 		break;
745 	case -EOPNOTSUPP:
746 		pr_err("DCSS %s has multiple page ranges and cannot be "
747 		       "loaded or queried\n", seg_name);
748 		break;
749 	case -ENOSPC:
750 		pr_err("DCSS %s overlaps with used storage and cannot "
751 		       "be loaded\n", seg_name);
752 		break;
753 	case -EBUSY:
754 		pr_err("%s needs used memory resources and cannot be "
755 		       "loaded or queried\n", seg_name);
756 		break;
757 	case -EPERM:
758 		pr_err("DCSS %s is already loaded in a different access "
759 		       "mode\n", seg_name);
760 		break;
761 	case -ENOMEM:
762 		pr_err("There is not enough memory to load or query "
763 		       "DCSS %s\n", seg_name);
764 		break;
765 	case -ERANGE:
766 		pr_err("DCSS %s exceeds the kernel mapping range (%lu) "
767 		       "and cannot be loaded\n", seg_name, VMEM_MAX_PHYS);
768 		break;
769 	default:
770 		break;
771 	}
772 }
773 
774 EXPORT_SYMBOL(segment_load);
775 EXPORT_SYMBOL(segment_unload);
776 EXPORT_SYMBOL(segment_save);
777 EXPORT_SYMBOL(segment_type);
778 EXPORT_SYMBOL(segment_modify_shared);
779 EXPORT_SYMBOL(segment_warning);
780