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
dcss_set_subcodes(void)105 dcss_set_subcodes(void)
106 {
107 #ifdef CONFIG_64BIT
108 	char *name = kmalloc(8 * sizeof(char), GFP_KERNEL | 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
dcss_mkname(char * name,char * dcss_name)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 *
segment_by_name(char * name)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
dcss_diag(int * func,void * parameter,unsigned long * ret1,unsigned long * ret2)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
dcss_diag_translate_rc(int vm_rc)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
query_segment_type(struct dcss_segment * seg)253 query_segment_type (struct dcss_segment *seg)
254 {
255 	unsigned long dummy, vmrc;
256 	int diag_cc, rc, i;
257 	struct qout64 *qout;
258 	struct qin64 *qin;
259 
260 	qin = kmalloc(sizeof(*qin), GFP_KERNEL | GFP_DMA);
261 	qout = kmalloc(sizeof(*qout), GFP_KERNEL | GFP_DMA);
262 	if ((qin == NULL) || (qout == NULL)) {
263 		rc = -ENOMEM;
264 		goto out_free;
265 	}
266 
267 	/* initialize diag input parameters */
268 	qin->qopcode = DCSS_FINDSEGA;
269 	qin->qoutptr = (unsigned long) qout;
270 	qin->qoutlen = sizeof(struct qout64);
271 	memcpy (qin->qname, seg->dcss_name, 8);
272 
273 	diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc);
274 
275 	if (diag_cc < 0) {
276 		rc = diag_cc;
277 		goto out_free;
278 	}
279 	if (diag_cc > 1) {
280 		pr_warning("Querying a DCSS type failed with rc=%ld\n", vmrc);
281 		rc = dcss_diag_translate_rc (vmrc);
282 		goto out_free;
283 	}
284 
285 #ifdef CONFIG_64BIT
286 	/* Only old format of output area of Diagnose x'64' is supported,
287 	   copy data for the new format. */
288 	if (segext_scode == DCSS_SEGEXT) {
289 		struct qout64_old *qout_old;
290 		qout_old = kzalloc(sizeof(*qout_old), GFP_KERNEL | GFP_DMA);
291 		if (qout_old == NULL) {
292 			rc = -ENOMEM;
293 			goto out_free;
294 		}
295 		memcpy(qout_old, qout, sizeof(struct qout64_old));
296 		qout->segstart = (unsigned long) qout_old->segstart;
297 		qout->segend = (unsigned long) qout_old->segend;
298 		qout->segcnt = qout_old->segcnt;
299 		qout->segrcnt = qout_old->segrcnt;
300 
301 		if (qout->segcnt > 6)
302 			qout->segrcnt = 6;
303 		for (i = 0; i < qout->segrcnt; i++) {
304 			qout->range[i].start =
305 				(unsigned long) qout_old->range[i].start;
306 			qout->range[i].end =
307 				(unsigned long) qout_old->range[i].end;
308 		}
309 		kfree(qout_old);
310 	}
311 #endif
312 	if (qout->segcnt > 6) {
313 		rc = -EOPNOTSUPP;
314 		goto out_free;
315 	}
316 
317 	if (qout->segcnt == 1) {
318 		seg->vm_segtype = qout->range[0].start & 0xff;
319 	} else {
320 		/* multi-part segment. only one type supported here:
321 		    - all parts are contiguous
322 		    - all parts are either EW or EN type
323 		    - maximum 6 parts allowed */
324 		unsigned long start = qout->segstart >> PAGE_SHIFT;
325 		for (i=0; i<qout->segcnt; i++) {
326 			if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) &&
327 			    ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) {
328 				rc = -EOPNOTSUPP;
329 				goto out_free;
330 			}
331 			if (start != qout->range[i].start >> PAGE_SHIFT) {
332 				rc = -EOPNOTSUPP;
333 				goto out_free;
334 			}
335 			start = (qout->range[i].end >> PAGE_SHIFT) + 1;
336 		}
337 		seg->vm_segtype = SEG_TYPE_EWEN;
338 	}
339 
340 	/* analyze diag output and update seg */
341 	seg->start_addr = qout->segstart;
342 	seg->end = qout->segend;
343 
344 	memcpy (seg->range, qout->range, 6*sizeof(struct qrange));
345 	seg->segcnt = qout->segcnt;
346 
347 	rc = 0;
348 
349  out_free:
350 	kfree(qin);
351 	kfree(qout);
352 	return rc;
353 }
354 
355 /*
356  * get info about a segment
357  * possible return values:
358  * -ENOSYS  : we are not running on VM
359  * -EIO     : could not perform query diagnose
360  * -ENOENT  : no such segment
361  * -EOPNOTSUPP: multi-part segment cannot be used with linux
362  * -ENOMEM  : out of memory
363  * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
364  */
365 int
segment_type(char * name)366 segment_type (char* name)
367 {
368 	int rc;
369 	struct dcss_segment seg;
370 
371 	if (!MACHINE_IS_VM)
372 		return -ENOSYS;
373 
374 	dcss_mkname(name, seg.dcss_name);
375 	rc = query_segment_type (&seg);
376 	if (rc < 0)
377 		return rc;
378 	return seg.vm_segtype;
379 }
380 
381 /*
382  * check if segment collides with other segments that are currently loaded
383  * returns 1 if this is the case, 0 if no collision was found
384  */
385 static int
segment_overlaps_others(struct dcss_segment * seg)386 segment_overlaps_others (struct dcss_segment *seg)
387 {
388 	struct list_head *l;
389 	struct dcss_segment *tmp;
390 
391 	BUG_ON(!mutex_is_locked(&dcss_lock));
392 	list_for_each(l, &dcss_list) {
393 		tmp = list_entry(l, struct dcss_segment, list);
394 		if ((tmp->start_addr >> 20) > (seg->end >> 20))
395 			continue;
396 		if ((tmp->end >> 20) < (seg->start_addr >> 20))
397 			continue;
398 		if (seg == tmp)
399 			continue;
400 		return 1;
401 	}
402 	return 0;
403 }
404 
405 /*
406  * real segment loading function, called from segment_load
407  */
408 static int
__segment_load(char * name,int do_nonshared,unsigned long * addr,unsigned long * end)409 __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end)
410 {
411 	unsigned long start_addr, end_addr, dummy;
412 	struct dcss_segment *seg;
413 	int rc, diag_cc;
414 
415 	start_addr = end_addr = 0;
416 	seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA);
417 	if (seg == NULL) {
418 		rc = -ENOMEM;
419 		goto out;
420 	}
421 	dcss_mkname (name, seg->dcss_name);
422 	rc = query_segment_type (seg);
423 	if (rc < 0)
424 		goto out_free;
425 
426 	if (loadshr_scode == DCSS_LOADSHRX) {
427 		if (segment_overlaps_others(seg)) {
428 			rc = -EBUSY;
429 			goto out_free;
430 		}
431 	}
432 
433 	rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
434 
435 	if (rc)
436 		goto out_free;
437 
438 	seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL);
439 	if (seg->res == NULL) {
440 		rc = -ENOMEM;
441 		goto out_shared;
442 	}
443 	seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM;
444 	seg->res->start = seg->start_addr;
445 	seg->res->end = seg->end;
446 	memcpy(&seg->res_name, seg->dcss_name, 8);
447 	EBCASC(seg->res_name, 8);
448 	seg->res_name[8] = '\0';
449 	strncat(seg->res_name, " (DCSS)", 7);
450 	seg->res->name = seg->res_name;
451 	rc = seg->vm_segtype;
452 	if (rc == SEG_TYPE_SC ||
453 	    ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared))
454 		seg->res->flags |= IORESOURCE_READONLY;
455 	if (request_resource(&iomem_resource, seg->res)) {
456 		rc = -EBUSY;
457 		kfree(seg->res);
458 		goto out_shared;
459 	}
460 
461 	if (do_nonshared)
462 		diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
463 				&start_addr, &end_addr);
464 	else
465 		diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
466 				&start_addr, &end_addr);
467 	if (diag_cc < 0) {
468 		dcss_diag(&purgeseg_scode, seg->dcss_name,
469 				&dummy, &dummy);
470 		rc = diag_cc;
471 		goto out_resource;
472 	}
473 	if (diag_cc > 1) {
474 		pr_warning("Loading DCSS %s failed with rc=%ld\n", name,
475 			   end_addr);
476 		rc = dcss_diag_translate_rc(end_addr);
477 		dcss_diag(&purgeseg_scode, seg->dcss_name,
478 				&dummy, &dummy);
479 		goto out_resource;
480 	}
481 	seg->start_addr = start_addr;
482 	seg->end = end_addr;
483 	seg->do_nonshared = do_nonshared;
484 	atomic_set(&seg->ref_count, 1);
485 	list_add(&seg->list, &dcss_list);
486 	*addr = seg->start_addr;
487 	*end  = seg->end;
488 	if (do_nonshared)
489 		pr_info("DCSS %s of range %p to %p and type %s loaded as "
490 			"exclusive-writable\n", name, (void*) seg->start_addr,
491 			(void*) seg->end, segtype_string[seg->vm_segtype]);
492 	else {
493 		pr_info("DCSS %s of range %p to %p and type %s loaded in "
494 			"shared access mode\n", name, (void*) seg->start_addr,
495 			(void*) seg->end, segtype_string[seg->vm_segtype]);
496 	}
497 	goto out;
498  out_resource:
499 	release_resource(seg->res);
500 	kfree(seg->res);
501  out_shared:
502 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
503  out_free:
504 	kfree(seg);
505  out:
506 	return rc;
507 }
508 
509 /*
510  * this function loads a DCSS segment
511  * name         : name of the DCSS
512  * do_nonshared : 0 indicates that the dcss should be shared with other linux images
513  *                1 indicates that the dcss should be exclusive for this linux image
514  * addr         : will be filled with start address of the segment
515  * end          : will be filled with end address of the segment
516  * return values:
517  * -ENOSYS  : we are not running on VM
518  * -EIO     : could not perform query or load diagnose
519  * -ENOENT  : no such segment
520  * -EOPNOTSUPP: multi-part segment cannot be used with linux
521  * -ENOSPC  : segment cannot be used (overlaps with storage)
522  * -EBUSY   : segment can temporarily not be used (overlaps with dcss)
523  * -ERANGE  : segment cannot be used (exceeds kernel mapping range)
524  * -EPERM   : segment is currently loaded with incompatible permissions
525  * -ENOMEM  : out of memory
526  * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
527  */
528 int
segment_load(char * name,int do_nonshared,unsigned long * addr,unsigned long * end)529 segment_load (char *name, int do_nonshared, unsigned long *addr,
530 		unsigned long *end)
531 {
532 	struct dcss_segment *seg;
533 	int rc;
534 
535 	if (!MACHINE_IS_VM)
536 		return -ENOSYS;
537 
538 	mutex_lock(&dcss_lock);
539 	seg = segment_by_name (name);
540 	if (seg == NULL)
541 		rc = __segment_load (name, do_nonshared, addr, end);
542 	else {
543 		if (do_nonshared == seg->do_nonshared) {
544 			atomic_inc(&seg->ref_count);
545 			*addr = seg->start_addr;
546 			*end  = seg->end;
547 			rc    = seg->vm_segtype;
548 		} else {
549 			*addr = *end = 0;
550 			rc    = -EPERM;
551 		}
552 	}
553 	mutex_unlock(&dcss_lock);
554 	return rc;
555 }
556 
557 /*
558  * this function modifies the shared state of a DCSS segment. note that
559  * name         : name of the DCSS
560  * do_nonshared : 0 indicates that the dcss should be shared with other linux images
561  *                1 indicates that the dcss should be exclusive for this linux image
562  * return values:
563  * -EIO     : could not perform load diagnose (segment gone!)
564  * -ENOENT  : no such segment (segment gone!)
565  * -EAGAIN  : segment is in use by other exploiters, try later
566  * -EINVAL  : no segment with the given name is currently loaded - name invalid
567  * -EBUSY   : segment can temporarily not be used (overlaps with dcss)
568  * 0	    : operation succeeded
569  */
570 int
segment_modify_shared(char * name,int do_nonshared)571 segment_modify_shared (char *name, int do_nonshared)
572 {
573 	struct dcss_segment *seg;
574 	unsigned long start_addr, end_addr, dummy;
575 	int rc, diag_cc;
576 
577 	start_addr = end_addr = 0;
578 	mutex_lock(&dcss_lock);
579 	seg = segment_by_name (name);
580 	if (seg == NULL) {
581 		rc = -EINVAL;
582 		goto out_unlock;
583 	}
584 	if (do_nonshared == seg->do_nonshared) {
585 		pr_info("DCSS %s is already in the requested access "
586 			"mode\n", name);
587 		rc = 0;
588 		goto out_unlock;
589 	}
590 	if (atomic_read (&seg->ref_count) != 1) {
591 		pr_warning("DCSS %s is in use and cannot be reloaded\n",
592 			   name);
593 		rc = -EAGAIN;
594 		goto out_unlock;
595 	}
596 	release_resource(seg->res);
597 	if (do_nonshared)
598 		seg->res->flags &= ~IORESOURCE_READONLY;
599 	else
600 		if (seg->vm_segtype == SEG_TYPE_SR ||
601 		    seg->vm_segtype == SEG_TYPE_ER)
602 			seg->res->flags |= IORESOURCE_READONLY;
603 
604 	if (request_resource(&iomem_resource, seg->res)) {
605 		pr_warning("DCSS %s overlaps with used memory resources "
606 			   "and cannot be reloaded\n", name);
607 		rc = -EBUSY;
608 		kfree(seg->res);
609 		goto out_del_mem;
610 	}
611 
612 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
613 	if (do_nonshared)
614 		diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
615 				&start_addr, &end_addr);
616 	else
617 		diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
618 				&start_addr, &end_addr);
619 	if (diag_cc < 0) {
620 		rc = diag_cc;
621 		goto out_del_res;
622 	}
623 	if (diag_cc > 1) {
624 		pr_warning("Reloading DCSS %s failed with rc=%ld\n", name,
625 			   end_addr);
626 		rc = dcss_diag_translate_rc(end_addr);
627 		goto out_del_res;
628 	}
629 	seg->start_addr = start_addr;
630 	seg->end = end_addr;
631 	seg->do_nonshared = do_nonshared;
632 	rc = 0;
633 	goto out_unlock;
634  out_del_res:
635 	release_resource(seg->res);
636 	kfree(seg->res);
637  out_del_mem:
638 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
639 	list_del(&seg->list);
640 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
641 	kfree(seg);
642  out_unlock:
643 	mutex_unlock(&dcss_lock);
644 	return rc;
645 }
646 
647 /*
648  * Decrease the use count of a DCSS segment and remove
649  * it from the address space if nobody is using it
650  * any longer.
651  */
652 void
segment_unload(char * name)653 segment_unload(char *name)
654 {
655 	unsigned long dummy;
656 	struct dcss_segment *seg;
657 
658 	if (!MACHINE_IS_VM)
659 		return;
660 
661 	mutex_lock(&dcss_lock);
662 	seg = segment_by_name (name);
663 	if (seg == NULL) {
664 		pr_err("Unloading unknown DCSS %s failed\n", name);
665 		goto out_unlock;
666 	}
667 	if (atomic_dec_return(&seg->ref_count) != 0)
668 		goto out_unlock;
669 	release_resource(seg->res);
670 	kfree(seg->res);
671 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
672 	list_del(&seg->list);
673 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
674 	kfree(seg);
675 out_unlock:
676 	mutex_unlock(&dcss_lock);
677 }
678 
679 /*
680  * save segment content permanently
681  */
682 void
segment_save(char * name)683 segment_save(char *name)
684 {
685 	struct dcss_segment *seg;
686 	char cmd1[160];
687 	char cmd2[80];
688 	int i, response;
689 
690 	if (!MACHINE_IS_VM)
691 		return;
692 
693 	mutex_lock(&dcss_lock);
694 	seg = segment_by_name (name);
695 
696 	if (seg == NULL) {
697 		pr_err("Saving unknown DCSS %s failed\n", name);
698 		goto out;
699 	}
700 
701 	sprintf(cmd1, "DEFSEG %s", name);
702 	for (i=0; i<seg->segcnt; i++) {
703 		sprintf(cmd1+strlen(cmd1), " %lX-%lX %s",
704 			seg->range[i].start >> PAGE_SHIFT,
705 			seg->range[i].end >> PAGE_SHIFT,
706 			segtype_string[seg->range[i].start & 0xff]);
707 	}
708 	sprintf(cmd2, "SAVESEG %s", name);
709 	response = 0;
710 	cpcmd(cmd1, NULL, 0, &response);
711 	if (response) {
712 		pr_err("Saving a DCSS failed with DEFSEG response code "
713 		       "%i\n", response);
714 		goto out;
715 	}
716 	cpcmd(cmd2, NULL, 0, &response);
717 	if (response) {
718 		pr_err("Saving a DCSS failed with SAVESEG response code "
719 		       "%i\n", response);
720 		goto out;
721 	}
722 out:
723 	mutex_unlock(&dcss_lock);
724 }
725 
726 /*
727  * print appropriate error message for segment_load()/segment_type()
728  * return code
729  */
segment_warning(int rc,char * seg_name)730 void segment_warning(int rc, char *seg_name)
731 {
732 	switch (rc) {
733 	case -ENOENT:
734 		pr_err("DCSS %s cannot be loaded or queried\n", seg_name);
735 		break;
736 	case -ENOSYS:
737 		pr_err("DCSS %s cannot be loaded or queried without "
738 		       "z/VM\n", seg_name);
739 		break;
740 	case -EIO:
741 		pr_err("Loading or querying DCSS %s resulted in a "
742 		       "hardware error\n", seg_name);
743 		break;
744 	case -EOPNOTSUPP:
745 		pr_err("DCSS %s has multiple page ranges and cannot be "
746 		       "loaded or queried\n", seg_name);
747 		break;
748 	case -ENOSPC:
749 		pr_err("DCSS %s overlaps with used storage and cannot "
750 		       "be loaded\n", seg_name);
751 		break;
752 	case -EBUSY:
753 		pr_err("%s needs used memory resources and cannot be "
754 		       "loaded or queried\n", seg_name);
755 		break;
756 	case -EPERM:
757 		pr_err("DCSS %s is already loaded in a different access "
758 		       "mode\n", seg_name);
759 		break;
760 	case -ENOMEM:
761 		pr_err("There is not enough memory to load or query "
762 		       "DCSS %s\n", seg_name);
763 		break;
764 	case -ERANGE:
765 		pr_err("DCSS %s exceeds the kernel mapping range (%lu) "
766 		       "and cannot be loaded\n", seg_name, VMEM_MAX_PHYS);
767 		break;
768 	default:
769 		break;
770 	}
771 }
772 
773 EXPORT_SYMBOL(segment_load);
774 EXPORT_SYMBOL(segment_unload);
775 EXPORT_SYMBOL(segment_save);
776 EXPORT_SYMBOL(segment_type);
777 EXPORT_SYMBOL(segment_modify_shared);
778 EXPORT_SYMBOL(segment_warning);
779